diff --git a/otp/uberdog/UberDog.py b/otp/uberdog/UberDog.py new file mode 100644 index 0000000..8c7da04 --- /dev/null +++ b/otp/uberdog/UberDog.py @@ -0,0 +1,380 @@ +""" +The Uber Distributed Obeject Globals server. +""" + +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.fsm.ClassicFSM import ClassicFSM +from direct.fsm.State import State +from otp.otpbase import OTPGlobals +from otp.distributed import OtpDoGlobals +from otp.distributed.OtpDoGlobals import * +from otp.ai.AIRepository import AIRepository +from otp.ai import TimeManagerAI +from pandac.PandaModules import * +from otp.uberdog.AccountDetailRecord import AccountDetailRecord, SubDetailRecord + +from otp.ai.AIMsgTypes import * + + +class UberDog(AIRepository): + notify = directNotify.newCategory("UberDog") + + def __init__( + self, mdip, mdport, esip, esport, dcFileNames, + serverId, minChannel, maxChannel): + AIRepository.__init__( + self, mdip, mdport, esip, esport, dcFileNames, + serverId, minChannel, maxChannel, dcSuffix = 'UD') + + # We're responsible for keeping track of who's online with which avatar + self.onlineAccountDetails = {} + self.onlineAvatars = {} + self.onlinePlayers = {} + + self.pending={} + self.doId2doCache={} + + if hasattr(self, 'setVerbose'): + if self.config.GetBool('verbose-uberrepository'): + self.setVerbose(1) + + # The AI State machine + self.fsm = ClassicFSM( + 'UberDog', [ + State('off', + self.enterOff, + self.exitOff, + ['connect']), + State('connect', + self.enterConnect, + self.exitConnect, + ['noConnection', 'playGame',]), + State('playGame', + self.enterPlayGame, + self.exitPlayGame, + ['noConnection']), + State('noConnection', + self.enterNoConnection, + self.exitNoConnection, + ['connect'])], + # initial state + 'off', + # final state + 'off', + ) + self.fsm.enterInitialState() + self.fsm.request("connect") + + def _connected(self): + """ + Callback for when we successfully connect to the otp_server cluster. + """ + self.setConnectionName("UberDog") + AIRepository._connected(self) + # Listen for Account and Avatar online/offline messages + self.registerForChannel(CHANNEL_PUPPET_ACTION) + self.fsm.request("playGame") + + def dispatchUpdateToDoId(self, dclassName, fieldName, doId, args, channelId=None): + # dispatch immediately to local object if it's local, otherwise send + # it over the wire + obj = self.doId2do.get(doId) + if obj is not None: + assert obj.__class__.__name__ == (dclassName + self.dcSuffix) + method = getattr(obj, fieldName) + apply(method, args) + else: + self.sendUpdateToDoId(dclassName, fieldName, doId, args, channelId) + + def dispatchUpdateToGlobalDoId(self, dclassName, fieldName, doId, args): + # dispatch immediately to local object if it's local, otherwise send + # it over the wire + obj = self.doId2do.get(doId) + if obj is not None: + assert obj.__class__.__name__ == dclassName + method = getattr(obj, fieldName) + apply(method, args) + else: + self.sendUpdateToGlobalDoId(dclassName, fieldName, doId, args) + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def handleAccountUsage(self, di): + priorAccount = di.getUint32() # Historic - used only in __dev__ atm + newAccount = di.getUint32() + + if priorAccount == 0 and newAccount == 0: + assert self.notify.debug("priorAccount==0 and newAccount==0, ignoring accountUsage message") + return + + accountDetailRecord = AccountDetailRecord() + accountDetailRecord.openChatEnabled = (di.getString() == "YES") + accountDetailRecord.createFriendsWithChat = (di.getString() == "YES") + accountDetailRecord.chatCodeCreation = (di.getString() == "YES") + access = di.getString() + if access == "VELVET": + access = OTPGlobals.AccessVelvetRope + elif access == "FULL": + access = OTPGlobals.AccessFull + else: + access = OTPGlobals.AccessUnknown + accountDetailRecord.piratesAccess = access + accountDetailRecord.familyAccountId = di.getInt32() + accountDetailRecord.playerAccountId = di.getInt32() + accountDetailRecord.playerName = di.getString() + accountDetailRecord.playerNameApproved = di.getInt8() + accountDetailRecord.maxAvatars = di.getInt32() + accountDetailRecord.numFamilyMembers = di.getInt16() + accountDetailRecord.familyMembers = [] + for i in range(accountDetailRecord.numFamilyMembers): + accountDetailRecord.familyMembers.append(di.getInt32()) + + logoutReason = di.getInt32() + + # Now retrieve the subscription information + accountDetailRecord.numSubs = di.getUint16() + + for i in range(accountDetailRecord.numSubs): + subDetailRecord = SubDetailRecord() + subDetailRecord.subId = di.getUint32() + subDetailRecord.subOwnerId = di.getUint32() + subDetailRecord.subName = di.getString() + subDetailRecord.subActive = di.getString() + access = di.getString() + if access == "VELVET": + access = OTPGlobals.AccessVelvetRope + elif access == "FULL": + access = OTPGlobals.AccessFull + else: + access = OTPGlobals.AccessUnknown + subDetailRecord.subAccess = access + subDetailRecord.subLevel = di.getUint8() + subDetailRecord.subNumAvatars = di.getUint8() + subDetailRecord.subNumConcur = di.getUint8() + subDetailRecord.subFounder = (di.getString() == "YES") + # Add this subscription to the dict on the account record + accountDetailRecord.subDetails[subDetailRecord.subId] = subDetailRecord + + # How many avatar slots total do you get in this game? + accountDetailRecord.maxAvatarSlots = di.getInt8() + + assert self.notify.debug("accountDetailRecord: %s" % accountDetailRecord) + + if priorAccount: + # Send any previous account offline + self.accountOffline(priorAccount) + pass + + if newAccount: + # Set up the new guy + self.accountOnline(newAccount, accountDetailRecord) + pass + pass + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def handleAvatarUsage(self, di): + priorAvatar = di.getUint32() + newAvatar = di.getUint32() + + if priorAvatar == 0 and newAvatar == 0: + assert self.notify.debug("priorAvatar==0 and newAvatar==0, ignoring avatarUsage message") + return + + newAvatarType = di.getUint16() + + accountId = di.getUint32() + + openChatEnabled = di.getString() + createFriendsWithChat = di.getString() + chatCodeCreation = di.getString() + piratesAccess = di.getString() + familyAccountId = di.getInt32() + playerAccountId = di.getInt32() + playerName = di.getString() + playerNameApproved = di.getInt8() + maxAvatars = di.getInt32() + numFamilyMembers = di.getInt16() + familyMembers = [] + for i in range(numFamilyMembers): + familyMembers.append(di.getInt32()) + + if openChatEnabled == "YES": + openChatEnabled = 1 + else: + openChatEnabled = 0 + + if priorAvatar: + # Send any previous avatar offline + self.avatarOffline(accountId, priorAvatar) + pass + + if newAvatar: + # Set up the new guy + self.avatarOnline(newAvatar, newAvatarType, + playerAccountId, + playerName, + playerNameApproved, + openChatEnabled, + createFriendsWithChat, + chatCodeCreation) + pass + pass + + + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def accountOnline(self, accountId, accountDetailRecord): + self.writeServerEvent('accountOnline', accountId, '') + self.onlineAccountDetails[accountId] = accountDetailRecord + messenger.send('accountOnline', [accountId]) + pass + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def accountOffline(self, accountId): + self.writeServerEvent('accountOffline', accountId, '') + self.onlineAccountDetails.pop(accountId, None) + self.onlinePlayers.pop(accountId, None) + messenger.send('accountOffline', [accountId]) + pass + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def getAccountDetails(self, accountId): + return self.onlineAccountDetails.get(accountId) + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def isAccountOnline(self, accountId): + return accountId in self.onlineAccountDetails + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def isAvatarOnline(self, avatarId): + return avatarId in self.onlineAvatars + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def getAvatarAccountOnline(self, avatarId): + return self.onlineAvatars.get(avatarId, 0) + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def getAccountOnlineAvatar(self, accountId): + return self.onlinePlayers.get(accountId, 0) + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def checkAccountId(self, accountId): + if not accountId: + # SUSPICIOUS + self.notify.warning("Bogus accountId: %s" % accountId) + self.writeServerEvent('suspicious', accountId, 'bogus accountId in OtpAvatarManagerUD') + elif not self.isAccountOnline(accountId): + # SUSPICIOUS + self.notify.warning("Got request from account not online: %s" % accountId) + self.writeServerEvent('suspicious', accountId, 'request from offline account in OtpAvatarManagerUD') + else: + # Everything checks out + return True + return False + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def avatarOnline(self, avatarId, avatarType, accountId, playerName, playerNameApproved, + openChatEnabled, createFriendsWithChat, chatCodeCreation): + self.writeServerEvent('avatarOnline', avatarId, '%s|%s|%s|%s|%s|%s' % ( + accountId, playerName, playerNameApproved, openChatEnabled, + createFriendsWithChat, chatCodeCreation)) + + self.onlineAvatars[avatarId] = accountId + self.onlinePlayers[accountId] = avatarId + + simpleInfo = [avatarId, avatarType] + fullInfo = [avatarId, + accountId, + playerName, + playerNameApproved, + openChatEnabled, + createFriendsWithChat, + chatCodeCreation] + + # necessary for local UD manager objects + messenger.send("avatarOnline", simpleInfo) + messenger.send("avatarOnlinePlusAccountInfo", fullInfo) + pass + + @report(types = ['args'], dConfigParam = 'avatarmgr') + def avatarOffline(self, accountId, avatarId): + self.writeServerEvent('avatarOffline', avatarId, '') + + self.onlinePlayers.pop(accountId, None) + self.onlineAvatars.pop(avatarId, None) + + # necessary for local UD manager objects + messenger.send("avatarOffline", [avatarId]) + pass + + ################################### + # Assumed Obsolete as of 6/29/09 + # + # If you're reading this and there + # haven't been any strange UD crashes + # here lately, you can probably delete + # the next few functions. + ################################### + def _addObject(self, context, distributedObject): + """ + Handle a new distributed object arriving by adding + it to the cache calling self.handleGotDo(). + """ + assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know' + doId=distributedObject.getDoId() + assert not self.doId2doCache.has_key(doId) + if not self.doId2doCache.has_key(doId): + self.doId2doCache[doId]=distributedObject + self.handleGotDo(distributedObject) + + def handleGotDo(self, distributedObject): + """ + This allows derived classes to override the handling + of new distributed objects arriving in the cache. + By default, this will loop through the pending calls + for that object and make the function calls. It + will also remove the handled calls from the pending set. + """ + assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know' + assert self.doId2doCache.has_key(doId) + pending=self.pending.get(doId) + if pending is not None: + del self.pending[doId] + for i in pending: + apply(i[0], i[2]) + + def deleteObject(self, doId): + """ + Ask for the object to be removed from the private + distributed object cache. + """ + assert False, 'JCW: Testing for obsolete functions. If this crashes, let Josh know' + if self.doId2doCache.had_key(doId): + self.unregisterForChannel(doId) + #self.deleteObject(doId) + del self.doId2doCache[doId] + #HACK: + self.unregisterForChannel(doId) + AIRepository.deleteObject(self.doId) + + def uniqueName(self, desc): + return desc + + if __dev__: + """ + Early warning system for unsupported use of the Uberdog Repository + """ + def deleteObjects(self): + assert 0 + + def createDistrict(self, districtId, districtName): + assert 0 + + def deleteDistrict(self, districtId): + assert 0 + + def enterDistrictReset(self): + assert 0 + + def exitDistrictReset(self): + assert 0 + diff --git a/otp/uberdog/UberDogGlobal.py b/otp/uberdog/UberDogGlobal.py new file mode 100644 index 0000000..2310644 --- /dev/null +++ b/otp/uberdog/UberDogGlobal.py @@ -0,0 +1,10 @@ +"""instantiate global ShowBase object""" + +from otp.ai.AIBase import * + +# We're going to end up importing this accidentally anyway, so we +# might as well import it explicitly, and share the same AIBase +# object. +from otp.ai.AIBaseGlobal import * + +__builtins__["uber"] = simbase diff --git a/toontown/uberdog/PartiesUdConfig.py b/toontown/uberdog/PartiesUdConfig.py new file mode 100644 index 0000000..0e1491a --- /dev/null +++ b/toontown/uberdog/PartiesUdConfig.py @@ -0,0 +1,34 @@ +from toontown.toonbase import TTLocalizer + +language = TTLocalizer.getLanguage() + +# Log config + +logFatal = True +logError = True +logWarning = True +logLog = True +logInfo = True +logDebug = False +logChat = True +logSecurity = True +logMaxLinesInMemory = 100 + +# DB config +ttDbHost = "localhost" +ttDbPort = 3306 + +if language == 'castillian': + ttDbName = "es_toontownTopDb" +elif language == "japanese": + ttDbName = "jp_toontownTopDb" +elif language == "portuguese": + ttDbName = "br_toontownTopDb" +elif language == "french": + ttDbName = "french_toontownTopDb" +else: + ttDbName = "toontownTopDb" + +ttDbUser = "ttDb_user" +ttDbPasswd = "toontastic2008" + diff --git a/toontown/uberdog/Start.py b/toontown/uberdog/Start.py new file mode 100644 index 0000000..15b425a --- /dev/null +++ b/toontown/uberdog/Start.py @@ -0,0 +1,108 @@ +""" +Start the Toontown UberDog (Uber Distributed Object Globals server). +""" + +from panda3d.core import loadPrcFile + +# TODO: use argparse for this? +configs = ('etc/Configrc.prc',) +for prc in configs: + loadPrcFile(prc) + +import builtins +from direct.task.Task import Task + +class game: + name = "uberDog" + process = "server" +builtins.game = game() + +import time +import os +import sys + +if os.getenv('TTMODELS'): + from pandac.PandaModules import getModelPath, Filename + # In the publish environment, TTMODELS won't be on the model + # path by default, so we always add it there. In the dev + # environment, it'll be on the model path already, but it + # doesn't hurt to add it again. + getModelPath().appendDirectory(Filename.expandFrom("$TTMODELS/built")) + +from direct.showbase.PythonUtil import * +from otp.uberdog.UberDogGlobal import * +from toontown.coderedemption import TTCodeRedemptionConsts +from toontown.uberdog.ToontownUberDog import ToontownUberDog +from toontown.uberdog import PartiesUdConfig + +print("Initializing the Toontown UberDog (Uber Distributed Object Globals server)...") + +uber.mdip = uber.config.GetString("msg-director-ip", "127.0.0.1") +uber.mdport = uber.config.GetInt("msg-director-port", 6666) + +uber.esip = uber.config.GetString("event-server-ip", "127.0.0.1") +uber.esport = uber.config.GetInt("event-server-port", 4343) + +stateServerId = uber.config.GetInt("state-server-id", 20100000) + +uber.objectNames = set(os.getenv("uberdog_objects", "").split()) + +minChannel = uber.config.GetInt("uberdog-min-channel", 200400000) +maxChannel = uber.config.GetInt("uberdog-max-channel", 200449999) + +uber.sbNSHost = uber.config.GetString("sb-host","") +uber.sbNSPort = uber.config.GetInt("sb-port",6053) +uber.sbListenPort = 6060 +uber.clHost = "127.0.0.1" +uber.clPort = 9090 +uber.allowUnfilteredChat = uber.config.GetInt("allow-unfiltered-chat",0) +uber.bwDictPath = "" + +uber.RATManagerHTTPListenPort = uber.config.GetInt("rat-port",8080) +uber.awardManagerHTTPListenPort = uber.config.GetInt("award-port",8888) +uber.inGameNewsMgrHTTPListenPort = uber.config.GetInt("in-game-news-port",8889) +uber.mysqlhost = uber.config.GetString("mysql-host", PartiesUdConfig.ttDbHost) + + +uber.codeRedemptionMgrHTTPListenPort = uber.config.GetInt('code-redemption-port', 8998) +uber.crDbName = uber.config.GetString("tt-code-db-name", TTCodeRedemptionConsts.DefaultDbName) + +uber.cpuInfoMgrHTTPListenPort = uber.config.GetInt("security_ban_mgr_port",8892) + +uber.air = ToontownUberDog( + uber.mdip, uber.mdport, + uber.esip, uber.esport, + None, + stateServerId, + minChannel, + maxChannel) + +# How we let the world know we are not running a service +uber.aiService = 0 + +uber.wantEmbeddedOtpServer = uber.config.GetInt( + "toontown-uberdog-want-embedded-otp-server", 0) +if uber.wantEmbeddedOtpServer: + otpServerPath = uber.config.GetString( + "toontown-uberdog-otp-server-path", "c:/toonsrv") + sys.path.append(otpServerPath) + + import otp_server_py + if not otp_server_py.serverInit(otpServerPath): + sys.exit(1) + + def ServerYield(task): + otp_server_py.serverLoop() + return Task.cont + + uber.taskMgr.add(ServerYield, 'serverYield') + __builtins__["otpServer"] = otp_server_py + + +try: + run() +except: + info = describeException() + #uber.air.writeServerEvent('uberdog-exception', districtNumber, info) + raise + diff --git a/toontown/uberdog/ToontownUberDog.py b/toontown/uberdog/ToontownUberDog.py new file mode 100644 index 0000000..bee7895 --- /dev/null +++ b/toontown/uberdog/ToontownUberDog.py @@ -0,0 +1,136 @@ +""" +The Toontown Uber Distributed Object Globals server. +""" + +from pandac.PandaModules import * +import time +if __debug__: + from direct.showbase.PythonUtil import * + +from direct.directnotify.DirectNotifyGlobal import directNotify + +from otp.distributed import OtpDoGlobals +from otp.ai.AIMsgTypes import * +from otp.ai import TimeManagerAI +from otp.uberdog.UberDog import UberDog + +from otp.friends.AvatarFriendsManagerUD import AvatarFriendsManagerUD +from toontown.uberdog.DistributedDeliveryManagerUD import DistributedDeliveryManagerUD +from toontown.uberdog.DistributedMailManagerUD import DistributedMailManagerUD +from toontown.parties import ToontownTimeManager +from toontown.rpc.RATManagerUD import RATManagerUD +from toontown.rpc.AwardManagerUD import AwardManagerUD +from toontown.uberdog import TTSpeedchatRelayUD +from toontown.uberdog import DistributedInGameNewsMgrUD +from toontown.uberdog import DistributedCpuInfoMgrUD + +from otp.uberdog.RejectCode import RejectCode + +class ToontownUberDog(UberDog): + notify = directNotify.newCategory("UberDog") + + def __init__( + self, mdip, mdport, esip, esport, dcFilenames, + serverId, minChannel, maxChannel): + assert self.notify.debugStateCall(self) + # TODO: The UD needs to know server time, but perhaps this isn't + # the place to do this? -SG-SLWP + self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager() + self.toontownTimeManager.updateLoginTimes(time.time(), time.time(), globalClock.getRealTime()) + + def isManagerFor(name): + return len(uber.objectNames) == 0 or name in uber.objectNames + self.isFriendsManager = False # latest from Ian this should not run anymore + #self.isFriendsManager = isManagerFor('friends') + self.isSpeedchatRelay = isManagerFor('speedchatRelay') + self.isGiftingManager = isManagerFor('gifting') + self.isMailManager = False # isManagerFor('mail') + self.isPartyManager = isManagerFor('party') + self.isRATManager = False # isManagerFor('RAT') + self.isAwardManager = isManagerFor('award') + self.isCodeRedemptionManager = isManagerFor('coderedemption') + self.isInGameNewsMgr = isManagerFor('ingamenews') + self.isCpuInfoMgr = isManagerFor('cpuinfo') + self.isRandomSourceManager = False # isManagerFor('randomsource') + + UberDog.__init__( + self, mdip, mdport, esip, esport, dcFilenames, + serverId, minChannel, maxChannel) + + def createObjects(self): + UberDog.createObjects(self) + # Ask for the ObjectServer so we can check the dc hash value + self.queryObjectAll(self.serverId) + + if self.isFriendsManager: + self.playerFriendsManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_PLAYER_FRIENDS_MANAGER, + "TTPlayerFriendsManager") + + if self.isSpeedchatRelay: + self.speedchatRelay = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_SPEEDCHAT_RELAY, + "TTSpeedchatRelay") + + if self.isGiftingManager: + self.deliveryManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER, + "DistributedDeliveryManager") + + if self.isMailManager: + self.mailManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_MAIL_MANAGER, + "DistributedMailManager") + + if self.isPartyManager: + self.partyManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_PARTY_MANAGER, + "DistributedPartyManager") + + if simbase.config.GetBool('want-ddsm', 1): + self.dataStoreManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_TEMP_STORE_MANAGER, + "DistributedDataStoreManager") + + if self.isRATManager: + self.RATManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_RAT_MANAGER, + "RATManager") + + if self.isAwardManager: + self.awardManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_AWARD_MANAGER, + "AwardManager") + + if config.GetBool('want-code-redemption', 1): + if self.isCodeRedemptionManager: + self.codeRedemptionManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER, + "TTCodeRedemptionMgr") + + if self.isInGameNewsMgr: + self.inGameNewsMgr = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_IN_GAME_NEWS_MANAGER, + "DistributedInGameNewsMgr") + + if self.isCpuInfoMgr: + self.cpuInfoMgr = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_CPU_INFO_MANAGER, + "DistributedCpuInfoMgr") + + if self.isRandomSourceManager: + self.randomSourceManager = self.generateGlobalObject( + OtpDoGlobals.OTP_DO_ID_TOONTOWN_NON_REPEATABLE_RANDOM_SOURCE, + "NonRepeatableRandomSource") + + def getDatabaseIdForClassName(self, className): + return DatabaseIdFromClassName.get( + className, DefaultDatabaseChannelId) + + if __debug__: + def status(self): + if self.isGiftingManager: + print("deliveryManager is", self.deliveryManager) + if self.isFriendsManager: + print("playerFriendsManager is ",self.playerFriendsManager) +