mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-10-31 08:47:54 +00:00
Merge pull request #35 from loblao/master
New CSM Tokens and DC Imports
This commit is contained in:
commit
5c15478178
10 changed files with 275 additions and 294 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
# Python artifacts
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Batch
|
||||
*.bat
|
||||
|
@ -15,3 +16,6 @@
|
|||
|
||||
# Git
|
||||
*.rej
|
||||
|
||||
# Local config
|
||||
dependencies/config/local.prc
|
||||
|
|
1
dependencies/astron/dclass/stride.dc
vendored
1
dependencies/astron/dclass/stride.dc
vendored
|
@ -34,6 +34,7 @@ dclass Account {
|
|||
string LAST_LOGIN db;
|
||||
string ACCOUNT_ID db;
|
||||
uint16 ACCESS_LEVEL db;
|
||||
uint64 LAST_LOGIN_TS db;
|
||||
};
|
||||
|
||||
struct BarrierData {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
cd ../../../
|
||||
|
||||
"dependencies/panda/python/ppython.exe" "dev/tools/dcimports/parse_dcimports.py" -o "otp/distributed/DCClassImports.py" "dependencies/astron/dclass/stride.dc"
|
||||
|
||||
pause
|
|
@ -1,50 +0,0 @@
|
|||
import argparse
|
||||
|
||||
from pandac.PandaModules import *
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--output', '-o', default='DCClassImports.py',
|
||||
help='The filename of the generated Python module.')
|
||||
parser.add_argument('filenames', nargs='+', default=['otp.dc', 'toon.dc'],
|
||||
help='The DC class files to be included in the generated Python module.')
|
||||
args = parser.parse_args()
|
||||
|
||||
dcFile = DCFile()
|
||||
for filename in args.filenames:
|
||||
dcFile.read(Filename.fromOsSpecific(filename))
|
||||
|
||||
dcImports = {}
|
||||
for n in xrange(dcFile.getNumImportModules()):
|
||||
moduleName = dcFile.getImportModule(n)[:].split('/', 1)[0]
|
||||
if moduleName not in dcImports:
|
||||
dcImports[moduleName] = []
|
||||
importSymbols = []
|
||||
for i in xrange(dcFile.getNumImportSymbols(n)):
|
||||
symbolName = dcFile.getImportSymbol(n, i).split('/', 1)[0]
|
||||
importSymbols.append(symbolName)
|
||||
dcImports[moduleName].extend(importSymbols)
|
||||
|
||||
data = '''\
|
||||
# This file was generated by the parse_dclass.py utility.
|
||||
from pandac.PandaModules import *
|
||||
|
||||
|
||||
hashVal = %r
|
||||
|
||||
|
||||
''' % dcFile.getHash()
|
||||
|
||||
for moduleName, importSymbols in dcImports.items():
|
||||
data += 'from %s import %s\n' % (moduleName, ', '.join(importSymbols))
|
||||
|
||||
data += '''
|
||||
|
||||
dcImports = locals().copy()
|
||||
'''
|
||||
|
||||
print 'Writing %s...' % args.output
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
print 'Done writing %s.' % args.output
|
|
@ -1,48 +0,0 @@
|
|||
# This file was generated by the parse_dclass.py utility.
|
||||
from pandac.PandaModules import *
|
||||
|
||||
|
||||
hashVal = 3796631281L
|
||||
|
||||
|
||||
from toontown.coghq import DistributedCashbotBossSafe, DistributedCashbotBossCrane, DistributedBattleFactory, DistributedCashbotBossTreasure, DistributedCogHQDoor, DistributedCogHQExteriorDoor, DistributedSellbotHQDoor, DistributedFactoryElevatorExt, DistributedMintElevatorExt, DistributedLawOfficeElevatorExt, DistributedLawOfficeElevatorInt, LobbyManager, DistributedMegaCorp, DistributedFactory, DistributedLawOffice, DistributedLawOfficeFloor, DistributedLift, DistributedDoorEntity, DistributedSwitch, DistributedButton, DistributedTrigger, DistributedCrushableEntity, DistributedCrusherEntity, DistributedStomper, DistributedStomperPair, DistributedLaserField, DistributedGolfGreenGame, DistributedSecurityCamera, DistributedMover, DistributedElevatorMarker, DistributedBarrelBase, DistributedGagBarrel, DistributedBeanBarrel, DistributedHealBarrel, DistributedGrid, ActiveCell, DirectionalCell, CrusherCell, DistributedCrate, DistributedSinkingPlatform, BattleBlocker, DistributedMint, DistributedMintRoom, DistributedMintBattle, DistributedStage, DistributedStageRoom, DistributedStageBattle, DistributedLawbotBossGavel, DistributedLawbotCannon, DistributedLawbotChair, DistributedCogKart, DistributedCountryClub, DistributedCountryClubRoom, DistributedMoleField, DistributedCountryClubBattle, DistributedMaze, DistributedFoodBelt, DistributedBanquetTable, DistributedGolfSpot
|
||||
from toontown.golf import DistributedPhysicsWorld, DistributedGolfHole, DistributedGolfCourse
|
||||
from toontown.building import DistributedAnimatedProp, DistributedTrophyMgr, DistributedBuilding, DistributedBuildingQueryMgr, DistributedToonInterior, DistributedToonHallInterior, DistributedSuitInterior, DistributedHQInterior, DistributedGagshopInterior, DistributedPetshopInterior, DistributedKartShopInterior, DistributedDoor, DistributedKnockKnockDoor, DistributedElevator, DistributedElevatorFSM, DistributedElevatorExt, DistributedElevatorInt, DistributedElevatorFloor, DistributedBossElevator, DistributedVPElevator, DistributedCFOElevator, DistributedCJElevator, DistributedBBElevator, DistributedBoardingParty, DistributedTutorialInterior, DistributedClubElevator
|
||||
from toontown.uberdog.DistributedPartyManager import DistributedPartyManager
|
||||
from otp.friends import FriendManager
|
||||
from otp.level import DistributedLevel, DistributedEntity, DistributedInteractiveEntity
|
||||
from toontown.shtiker import DeleteManager, PurchaseManager, NewbiePurchaseManager
|
||||
from toontown.groups import GroupManager
|
||||
from toontown.uberdog.ClientServicesManager import ClientServicesManager
|
||||
from toontown.ai import WelcomeValleyManager, NewsManager, DistributedAprilToonsMgr, DistributedBlackCatMgr, DistributedReportMgr, DistributedPolarPlaceEffectMgr, DistributedGreenToonEffectMgr, DistributedResistanceEmoteMgr, DistributedScavengerHuntTarget, DistributedTrickOrTreatTarget, DistributedWinterCarolingTarget, DistributedJorElCam
|
||||
from otp.chat import ChatAgent
|
||||
from toontown.parties.GlobalPartyManager import GlobalPartyManager
|
||||
from toontown.racing.DistributedStartingBlock import DistributedViewingBlock
|
||||
from toontown.suit import DistributedSuitPlanner, DistributedSuitBase, DistributedSuit, DistributedTutorialSuit, DistributedFactorySuit, DistributedMintSuit, DistributedStageSuit, DistributedSellbotBoss, DistributedCashbotBoss, DistributedCashbotBossGoon, DistributedGoon, DistributedGridGoon, DistributedLawbotBoss, DistributedLawbotBossSuit, DistributedBossbotBoss
|
||||
from toontown.distributed import ToontownDistrict, ToontownDistrictStats, DistributedTimer
|
||||
from toontown.effects import DistributedFireworkShow
|
||||
from toontown.safezone import DistributedTrolley, DistributedPillow, DistributedPartyGate, DistributedBoat, DistributedButterfly, DistributedMMPiano, DistributedDGFlower, DistributedFishingSpot, SafeZoneManager, DistributedTreasure, DistributedGolfKart, DistributedPicnicBasket, DistributedPicnicTable, DistributedChineseCheckers, DistributedCheckers, DistributedFindFour
|
||||
from toontown.fishing import DistributedFishingPond, DistributedFishingTarget, DistributedPondBingoManager
|
||||
from toontown.minigame import DistributedMinigame, DistributedMinigameTemplate, DistributedRaceGame, DistributedCannonGame, DistributedPatternGame, DistributedRingGame, DistributedTagGame, DistributedMazeGame, DistributedTugOfWarGame, DistributedCatchGame, DistributedDivingGame, DistributedTargetGame, DistributedVineGame, DistributedIceGame, DistributedCogThiefGame, DistributedTwoDGame
|
||||
from toontown.racing import DistributedVehicle, DistributedStartingBlock, DistributedRace, DistributedKartPad, DistributedRacePad, DistributedViewPad, DistributedStartingBlock, DistributedLeaderBoard, DistributedGag, DistributedProjectile
|
||||
from toontown.catalog import CatalogManager, AccountDate
|
||||
from toontown.parties import DistributedParty, DistributedPartyActivity, DistributedPartyTeamActivity, DistributedPartyCannon, DistributedPartyCannonActivity, DistributedPartyCatchActivity, DistributedPartyWinterCatchActivity, DistributedPartyCogActivity, DistributedPartyWinterCogActivity, DistributedPartyFireworksActivity, DistributedPartyDanceActivityBase, DistributedPartyDanceActivity, DistributedPartyDance20Activity, DistributedPartyValentineDanceActivity, DistributedPartyValentineDance20Activity, DistributedPartyTrampolineActivity, DistributedPartyValentineTrampolineActivity, DistributedPartyVictoryTrampolineActivity, DistributedPartyWinterTrampolineActivity, DistributedPartyTugOfWarActivity, DistributedPartyJukeboxActivityBase, DistributedPartyJukeboxActivity, DistributedPartyJukebox40Activity, DistributedPartyValentineJukeboxActivity, DistributedPartyValentineJukebox40Activity
|
||||
from toontown.pets.DistributedPet import *
|
||||
from toontown.friends import TTSFriendsManager
|
||||
from toontown.cogdominium import DistributedCogdoInterior, DistributedCogdoBattleBldg, DistributedCogdoElevatorExt, DistributedCogdoElevatorInt, DistributedCogdoBarrel, DistCogdoGame, DistCogdoLevelGame, DistCogdoBoardroomGame, DistCogdoCraneGame, DistCogdoMazeGame, DistCogdoFlyingGame, DistCogdoCrane, DistCogdoCraneMoneyBag, DistCogdoCraneCog
|
||||
from toontown.uberdog.GlobalLobbyManager import GlobalLobbyManager
|
||||
from toontown.uberdog.ARGManager import ARGManager
|
||||
from otp.distributed import Account, DistributedDistrict, DistributedDirectory
|
||||
from toontown.estate import DistributedCannon, DistributedTarget, EstateManager, DistributedEstate, DistributedHouse, DistributedHouseInterior, DistributedGarden, DistributedHouseDoor, DistributedMailbox, DistributedFurnitureManager, DistributedFurnitureItem, DistributedBank, DistributedCloset, DistributedTrunk, DistributedPhone, DistributedFireworksCannon, DistributedLawnDecor, DistributedGardenPlot, DistributedGardenBox, DistributedFlower, DistributedGagTree, DistributedStatuary, DistributedToonStatuary, DistributedChangingStatuary, DistributedAnimatedStatuary, DistributedPlantBase, DistributedLawnDecor
|
||||
from toontown.uberdog.DistributedLobbyManager import DistributedLobbyManager
|
||||
from toontown.toon import DistributedToon, DistributedNPCToonBase, DistributedNPCToon, DistributedSmartNPC, DistributedNPCSpecialQuestGiver, DistributedNPCFlippyInToonHall, DistributedNPCScientist, DistributedNPCClerk, DistributedNPCTailor, DistributedNPCBlocker, DistributedNPCFisherman, DistributedNPCPartyPerson, DistributedNPCPetclerk, DistributedNPCKartClerk, DistributedNPCGlove, DistributedNPCLaffRestock
|
||||
from toontown.tutorial import DistributedBattleTutorial, TutorialManager
|
||||
from toontown.pets import DistributedPetProxy
|
||||
from toontown.coderedemption.TTCodeRedemptionMgr import TTCodeRedemptionMgr
|
||||
from direct.distributed import DistributedObject, DistributedNode, DistributedSmoothNode, DistributedCartesianGrid, DistributedCamera, DistributedObjectGlobal
|
||||
from otp.ai import TimeManager, MagicWordManager
|
||||
from otp.avatar import DistributedAvatar, DistributedPlayer, AvatarHandle
|
||||
from toontown.battle import DistributedBattleBase, DistributedBattle, DistributedBattleBldg, DistributedBattleFinal, DistributedBattleWaiters, DistributedBattleDiners
|
||||
|
||||
|
||||
dcImports = locals().copy()
|
|
@ -11,7 +11,7 @@ from direct.task import Task
|
|||
from pandac.PandaModules import *
|
||||
from otp.avatar import Avatar, DistributedAvatar
|
||||
from otp.avatar.DistributedPlayer import DistributedPlayer
|
||||
from otp.distributed import DCClassImports, OtpDoGlobals
|
||||
from otp.distributed import OtpDoGlobals
|
||||
from otp.distributed.OtpDoGlobals import *
|
||||
from otp.distributed.TelemetryLimiter import TelemetryLimiter
|
||||
from otp.otpbase import OTPGlobals, OTPLocalizer
|
||||
|
@ -218,50 +218,154 @@ class OTPClientRepository(ClientRepositoryBase):
|
|||
self.dclassesByName = {}
|
||||
self.dclassesByNumber = {}
|
||||
self.hashVal = 0
|
||||
try:
|
||||
dcStream
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
self.notify.info('Detected DC file stream, reading it...')
|
||||
dcFileNames = [dcStream]
|
||||
|
||||
if isinstance(dcFileNames, str):
|
||||
if isinstance(dcFileNames, types.StringTypes):
|
||||
# If we were given a single string, make it a list.
|
||||
dcFileNames = [dcFileNames]
|
||||
|
||||
if dcFileNames is not None:
|
||||
for dcFileName in dcFileNames:
|
||||
if isinstance(dcFileName, StringStream):
|
||||
readResult = dcFile.read(dcFileName, 'DC stream')
|
||||
else:
|
||||
readResult = dcFile.read(dcFileName)
|
||||
if not readResult:
|
||||
self.notify.error('Could not read DC file.')
|
||||
else:
|
||||
dcFile.readAll()
|
||||
dcImports = {}
|
||||
if dcFileNames == None:
|
||||
try:
|
||||
# For Nirai
|
||||
readResult = dcFile.read(dcStream, '__dc__')
|
||||
del __builtin__.dcStream
|
||||
|
||||
self.hashVal = DCClassImports.hashVal
|
||||
for i in xrange(dcFile.getNumClasses()):
|
||||
except NameError:
|
||||
readResult = dcFile.readAll()
|
||||
|
||||
if not readResult:
|
||||
self.notify.error("Could not read dc file.")
|
||||
|
||||
else:
|
||||
searchPath = getModelPath().getValue()
|
||||
for dcFileName in dcFileNames:
|
||||
pathname = Filename(dcFileName)
|
||||
vfs.resolveFilename(pathname, searchPath)
|
||||
readResult = dcFile.read(pathname)
|
||||
if not readResult:
|
||||
self.notify.error("Could not read dc file: %s" % (pathname))
|
||||
|
||||
self.hashVal = dcFile.getHash()
|
||||
|
||||
# Now import all of the modules required by the DC file.
|
||||
for n in range(dcFile.getNumImportModules()):
|
||||
moduleName = dcFile.getImportModule(n)[:]
|
||||
|
||||
# Maybe the module name is represented as "moduleName/AI".
|
||||
suffix = moduleName.split('/')
|
||||
moduleName = suffix[0]
|
||||
suffix=suffix[1:]
|
||||
if self.dcSuffix in suffix:
|
||||
moduleName += self.dcSuffix
|
||||
elif self.dcSuffix == 'UD' and 'AI' in suffix: #HACK:
|
||||
moduleName += 'AI'
|
||||
|
||||
importSymbols = []
|
||||
for i in range(dcFile.getNumImportSymbols(n)):
|
||||
symbolName = dcFile.getImportSymbol(n, i)
|
||||
|
||||
# Maybe the symbol name is represented as "symbolName/AI".
|
||||
suffix = symbolName.split('/')
|
||||
symbolName = suffix[0]
|
||||
suffix=suffix[1:]
|
||||
if self.dcSuffix in suffix:
|
||||
symbolName += self.dcSuffix
|
||||
elif self.dcSuffix == 'UD' and 'AI' in suffix: #HACK:
|
||||
symbolName += 'AI'
|
||||
|
||||
importSymbols.append(symbolName)
|
||||
|
||||
self.importModule(dcImports, moduleName, importSymbols)
|
||||
|
||||
# Now get the class definition for the classes named in the DC
|
||||
# file.
|
||||
for i in range(dcFile.getNumClasses()):
|
||||
dclass = dcFile.getClass(i)
|
||||
number = dclass.getNumber()
|
||||
className = dclass.getName() + self.dcSuffix
|
||||
|
||||
# Does the class have a definition defined in the newly
|
||||
# imported namespace?
|
||||
classDef = dcImports.get(className)
|
||||
if classDef is None and self.dcSuffix == 'UD': #HACK:
|
||||
className = dclass.getName() + 'AI'
|
||||
classDef = dcImports.get(className)
|
||||
|
||||
# Also try it without the dcSuffix.
|
||||
if classDef == None:
|
||||
className = dclass.getName()
|
||||
classDef = DCClassImports.dcImports.get(className)
|
||||
classDef = dcImports.get(className)
|
||||
if classDef is None:
|
||||
self.notify.debug('No class definition for %s.' % className)
|
||||
self.notify.debug("No class definition for %s." % (className))
|
||||
else:
|
||||
if type(classDef) == types.ModuleType:
|
||||
if not hasattr(classDef, className):
|
||||
self.notify.warning('Module %s does not define class %s.' % (className, className))
|
||||
self.notify.warning("Module %s does not define class %s." % (className, className))
|
||||
continue
|
||||
classDef = getattr(classDef, className)
|
||||
if (type(classDef) != types.ClassType) and (type(classDef) != types.TypeType):
|
||||
self.notify.error('Symbol %s is not a class name.' % className)
|
||||
|
||||
if type(classDef) != types.ClassType and type(classDef) != types.TypeType:
|
||||
self.notify.error("Symbol %s is not a class name." % (className))
|
||||
else:
|
||||
dclass.setClassDef(classDef)
|
||||
|
||||
self.dclassesByName[className] = dclass
|
||||
if number >= 0:
|
||||
self.dclassesByNumber[number] = dclass
|
||||
|
||||
# Owner Views
|
||||
if self.hasOwnerView():
|
||||
ownerDcSuffix = self.dcSuffix + 'OV'
|
||||
# dict of class names (without 'OV') that have owner views
|
||||
ownerImportSymbols = {}
|
||||
|
||||
# Now import all of the modules required by the DC file.
|
||||
for n in range(dcFile.getNumImportModules()):
|
||||
moduleName = dcFile.getImportModule(n)
|
||||
|
||||
# Maybe the module name is represented as "moduleName/AI".
|
||||
suffix = moduleName.split('/')
|
||||
moduleName = suffix[0]
|
||||
suffix=suffix[1:]
|
||||
if ownerDcSuffix in suffix:
|
||||
moduleName = moduleName + ownerDcSuffix
|
||||
|
||||
importSymbols = []
|
||||
for i in range(dcFile.getNumImportSymbols(n)):
|
||||
symbolName = dcFile.getImportSymbol(n, i)
|
||||
|
||||
# Check for the OV suffix
|
||||
suffix = symbolName.split('/')
|
||||
symbolName = suffix[0]
|
||||
suffix=suffix[1:]
|
||||
if ownerDcSuffix in suffix:
|
||||
symbolName += ownerDcSuffix
|
||||
importSymbols.append(symbolName)
|
||||
ownerImportSymbols[symbolName] = None
|
||||
|
||||
self.importModule(dcImports, moduleName, importSymbols)
|
||||
|
||||
# Now get the class definition for the owner classes named
|
||||
# in the DC file.
|
||||
for i in range(dcFile.getNumClasses()):
|
||||
dclass = dcFile.getClass(i)
|
||||
if ((dclass.getName()+ownerDcSuffix) in ownerImportSymbols):
|
||||
number = dclass.getNumber()
|
||||
className = dclass.getName() + ownerDcSuffix
|
||||
|
||||
# Does the class have a definition defined in the newly
|
||||
# imported namespace?
|
||||
classDef = dcImports.get(className)
|
||||
if classDef is None:
|
||||
self.notify.error("No class definition for %s." % className)
|
||||
else:
|
||||
if type(classDef) == types.ModuleType:
|
||||
if not hasattr(classDef, className):
|
||||
self.notify.error("Module %s does not define class %s." % (className, className))
|
||||
classDef = getattr(classDef, className)
|
||||
dclass.setOwnerClassDef(classDef)
|
||||
self.dclassesByName[className] = dclass
|
||||
|
||||
def getGameDoId(self):
|
||||
return self.GameGlobalsId
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ args = parser.parse_args()
|
|||
for prc in args.config:
|
||||
loadPrcFile(prc)
|
||||
|
||||
if os.path.isfile('dependencies/config/local.prc'):
|
||||
loadPrcFile('dependencies/config/local.prc')
|
||||
|
||||
localconfig = ''
|
||||
if args.base_channel: localconfig += 'air-base-channel %s\n' % args.base_channel
|
||||
if args.max_channels: localconfig += 'air-channel-allocation %s\n' % args.max_channels
|
||||
|
|
|
@ -32,6 +32,9 @@ if __debug__:
|
|||
loadPrcFile('dependencies/config/general.prc')
|
||||
loadPrcFile('dependencies/config/release/dev.prc')
|
||||
|
||||
if os.path.isfile('dependencies/config/local.prc'):
|
||||
loadPrcFile('dependencies/config/local.prc')
|
||||
|
||||
defaultText = ""
|
||||
|
||||
def __inject_wx(_):
|
||||
|
|
|
@ -1,45 +1,74 @@
|
|||
import anydbm
|
||||
import base64
|
||||
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||
from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD
|
||||
from direct.distributed.PyDatagram import *
|
||||
from direct.fsm.FSM import FSM
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
from pandac.PandaModules import *
|
||||
import time
|
||||
import urllib2
|
||||
import os
|
||||
|
||||
from otp.ai.MagicWordGlobal import *
|
||||
from otp.distributed import OtpDoGlobals
|
||||
|
||||
from toontown.makeatoon.NameGenerator import NameGenerator
|
||||
from toontown.toon.ToonDNA import ToonDNA
|
||||
from toontown.toonbase import TTLocalizer
|
||||
from toontown.uberdog import NameJudgeBlacklist
|
||||
|
||||
from pandac.PandaModules import *
|
||||
|
||||
# Import from PyCrypto only if we are using a database that requires it. This
|
||||
# allows local hosted and developer builds of the game to run without it:
|
||||
accountDBType = simbase.config.GetString('accountdb-type', 'developer')
|
||||
import hashlib, hmac, json
|
||||
import anydbm, math, os
|
||||
import urllib2, time
|
||||
|
||||
def rejectConfig(issue, securityIssue=True, retarded=True):
|
||||
print
|
||||
print
|
||||
print 'Lemme get this straight....'
|
||||
print 'You are trying to use remote account database type...'
|
||||
print 'However,', issue + '!!!!'
|
||||
if securityIssue:
|
||||
print 'Do you want this server to get hacked?'
|
||||
if retarded:
|
||||
print '"Either down\'s or autism"\n - JohnnyDaPirate, 2015'
|
||||
print 'Go fix that!'
|
||||
exit()
|
||||
|
||||
def entropy(string):
|
||||
prob = [float(string.count(c)) / len(string) for c in dict.fromkeys(list(string))]
|
||||
entropy = -sum([p * math.log(p) / math.log(2.0) for p in prob])
|
||||
return entropy
|
||||
|
||||
def entropyIdeal(length):
|
||||
prob = 1.0 / length
|
||||
return -length * prob * math.log(prob) / math.log(2.0)
|
||||
|
||||
accountDBType = config.GetString('accountdb-type', 'developer')
|
||||
accountServerSecret = config.GetString('account-server-secret', 'dev')
|
||||
accountServerHashAlgo = config.GetString('account-server-hash-algo', 'sha512')
|
||||
if accountDBType == 'remote':
|
||||
from Crypto.Cipher import AES
|
||||
if accountServerSecret == 'dev':
|
||||
rejectConfig('you have not changed the secret in config/local.prc')
|
||||
|
||||
# Sometimes we'll want to force a specific access level, such as on the
|
||||
# developer server:
|
||||
minAccessLevel = simbase.config.GetInt('min-access-level', 100)
|
||||
if len(accountServerSecret) < 16:
|
||||
rejectConfig('the secret is too small! Make it 16+ bytes', retarded=False)
|
||||
|
||||
accountServerEndpoint = simbase.config.GetString(
|
||||
'account-server-endpoint', 'http://tigercat1.me/tmpremote/api/')
|
||||
accountServerSecret = simbase.config.GetString(
|
||||
'account-server-secret', '9sj6816aj1hs795j')
|
||||
secretLength = len(accountServerSecret)
|
||||
ideal = entropyIdeal(secretLength) / 2
|
||||
entropy = entropy(accountServerSecret)
|
||||
if entropy < ideal:
|
||||
rejectConfig('the secret entropy is too low! For %d bytes,'
|
||||
' it should be %d. Currently it is %d' % (secretLength, ideal, entropy),
|
||||
retarded=False)
|
||||
|
||||
hashAlgo = getattr(hashlib, accountServerHashAlgo, None)
|
||||
if not hashAlgo:
|
||||
rejectConfig('%s is not a valid hash algo' % accountServerHashAlgo, securityIssue=False)
|
||||
|
||||
http = HTTPClient()
|
||||
http.setVerifySsl(0)
|
||||
hashSize = len(hashAlgo('').digest())
|
||||
|
||||
minAccessLevel = config.GetInt('min-access-level', 100)
|
||||
|
||||
def executeHttpRequest(url, **extras):
|
||||
# TO DO: THIS IS QUITE DISGUSTING
|
||||
# INSTEAD OF USING THE SAME SECRET, WE SHOULD HAVE AN API KEY EXCLUSIVE TO THAT
|
||||
# MOVE THIS TO ToontownInternalRepository (this might be interesting for AI)
|
||||
request = urllib2.Request('http://tigercat1.me/tmpremote/api/' + url)
|
||||
timestamp = str(int(time.time()))
|
||||
signature = hashlib.sha256(timestamp + accountServerSecret + "h*^ahJGHA017JI&A&*uyhU07")
|
||||
|
@ -53,10 +82,10 @@ def executeHttpRequest(url, **extras):
|
|||
except:
|
||||
return None
|
||||
|
||||
|
||||
notify = directNotify.newCategory('ClientServicesManagerUD')
|
||||
|
||||
def executeHttpRequestAndLog(url, **extras):
|
||||
# SEE ABOVE
|
||||
response = executeHttpRequest(url, extras)
|
||||
|
||||
if response is None:
|
||||
|
@ -93,20 +122,17 @@ def judgeName(name):
|
|||
|
||||
# --- ACCOUNT DATABASES ---
|
||||
# These classes make up the available account databases for Toontown Stride.
|
||||
# Databases with login tokens use the PyCrypto module for decrypting them.
|
||||
# DeveloperAccountDB is a special database that accepts a username, and assigns
|
||||
# each user with 700 access automatically upon login.
|
||||
|
||||
|
||||
class AccountDB:
|
||||
notify = directNotify.newCategory('AccountDB')
|
||||
|
||||
def __init__(self, csm):
|
||||
self.csm = csm
|
||||
|
||||
filename = simbase.config.GetString(
|
||||
'account-bridge-filename', 'account-bridge.db')
|
||||
filename = os.path.join("dependencies", filename)
|
||||
filename = config.GetString('account-bridge-filename', 'account-bridge.db')
|
||||
filename = os.path.join('dependencies', filename)
|
||||
|
||||
self.dbm = anydbm.open(filename, 'c')
|
||||
|
||||
|
@ -119,8 +145,20 @@ class AccountDB:
|
|||
def removeNameRequest(self, avId):
|
||||
pass
|
||||
|
||||
def lookup(self, username, callback):
|
||||
pass # Inheritors should override this.
|
||||
def lookup(self, data, callback):
|
||||
userId = data['userId']
|
||||
|
||||
data['success'] = True
|
||||
data['accessLevel'] = max(data['accessLevel'], minAccessLevel)
|
||||
|
||||
if str(userId) not in self.dbm:
|
||||
data['accountId'] = 0
|
||||
|
||||
else:
|
||||
data['accountId'] = int(self.dbm[str(userId)])
|
||||
|
||||
callback(data)
|
||||
return data
|
||||
|
||||
def storeAccountID(self, userId, accountId, callback):
|
||||
self.dbm[str(userId)] = str(accountId) # anydbm only allows strings.
|
||||
|
@ -131,33 +169,20 @@ class AccountDB:
|
|||
self.notify.warning('Unable to associate user %s with account %d!' % (userId, accountId))
|
||||
callback(False)
|
||||
|
||||
|
||||
class DeveloperAccountDB(AccountDB):
|
||||
notify = directNotify.newCategory('DeveloperAccountDB')
|
||||
|
||||
def lookup(self, username, callback):
|
||||
# Let's check if this user's ID is in your account database bridge:
|
||||
if str(username) not in self.dbm:
|
||||
# Nope. Let's associate them with a brand new Account object! We
|
||||
# will assign them with 700 access just because they are a
|
||||
# developer:
|
||||
response = {
|
||||
'success': True,
|
||||
'userId': username,
|
||||
'accountId': 0,
|
||||
'accessLevel': max(700, minAccessLevel)
|
||||
}
|
||||
else:
|
||||
# We have an account already, let's return what we've got:
|
||||
response = {
|
||||
'success': True,
|
||||
'userId': username,
|
||||
'accountId': int(self.dbm[str(username)]),
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
def lookup(self, userId, callback):
|
||||
return AccountDB.lookup(self, {'userId': userId,
|
||||
'accessLevel': 700,
|
||||
'notAfter': 0},
|
||||
callback)
|
||||
|
||||
class RemoteAccountDB(AccountDB):
|
||||
# TO DO FOR NAMES:
|
||||
# CURRENTLY IT MAKES n REQUESTS FOR EACH AVATAR
|
||||
# IN THE FUTURE, MAKE ONLY 1 REQUEST
|
||||
# WHICH RETURNS ALL PENDING AVS
|
||||
notify = directNotify.newCategory('RemoteAccountDB')
|
||||
|
||||
def addNameRequest(self, avId, name):
|
||||
|
@ -171,105 +196,43 @@ class RemoteAccountDB(AccountDB):
|
|||
return executeHttpRequest('names/remove', ID=str(avId))
|
||||
|
||||
def lookup(self, token, callback):
|
||||
# First, base64 decode the token:
|
||||
'''
|
||||
Token format:
|
||||
The token is obfuscated a bit, but nothing too hard to read.
|
||||
Most of the security is based on the hash.
|
||||
|
||||
I. Data contained in a token:
|
||||
A json-encoded dict, which contains timestamp, userid and extra info
|
||||
|
||||
II. Token format
|
||||
X = BASE64(ROT13(DATA)[::-1])
|
||||
H = HASH(X)[::-1]
|
||||
Token = BASE64(H + X)
|
||||
'''
|
||||
|
||||
try:
|
||||
token = base64.b64decode(token)
|
||||
except TypeError:
|
||||
self.notify.warning('Could not decode the provided token!')
|
||||
response = {
|
||||
'success': False,
|
||||
'reason': "Can't decode this token."
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
token = token.decode('base64')
|
||||
hash, token = token[:hashSize], token[hashSize:]
|
||||
|
||||
# Ensure this token is a valid size:
|
||||
if (not token) or ((len(token) % 16) != 0):
|
||||
self.notify.warning('Invalid token length!')
|
||||
response = {
|
||||
'success': False,
|
||||
'reason': 'Invalid token length.'
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
correctHash = hashAlgo(token + accountServerSecret).digest()
|
||||
if len(hash) != len(correctHash):
|
||||
raise ValueError('invalid hash')
|
||||
|
||||
# Next, decrypt the token using AES-128 in CBC mode:
|
||||
accountServerSecret = simbase.config.GetString(
|
||||
'account-server-secret', '9sj6816aj1hs795j')
|
||||
value = 0
|
||||
for x, y in zip(hash[::-1], correctHash):
|
||||
value |= ord(x) ^ ord(y)
|
||||
|
||||
# Ensure that our secret is the correct size:
|
||||
if len(accountServerSecret) > AES.block_size:
|
||||
self.notify.warning('account-server-secret is too big!')
|
||||
accountServerSecret = accountServerSecret[:AES.block_size]
|
||||
elif len(accountServerSecret) < AES.block_size:
|
||||
self.notify.warning('account-server-secret is too small!')
|
||||
accountServerSecret += '\x80'
|
||||
while len(accountServerSecret) < AES.block_size:
|
||||
accountServerSecret += '\x00'
|
||||
if value:
|
||||
raise ValueError('invalid hash')
|
||||
|
||||
# Take the initialization vector off the front of the token:
|
||||
iv = token[:AES.block_size]
|
||||
token = json.loads(token.decode('base64')[::-1].decode('rot13'))
|
||||
|
||||
# Truncate the token to get our cipher text:
|
||||
cipherText = token[AES.block_size:]
|
||||
|
||||
# Decrypt!
|
||||
cipher = AES.new(accountServerSecret, mode=AES.MODE_CBC, IV=iv)
|
||||
try:
|
||||
token = json.loads(cipher.decrypt(cipherText).replace('\x00', ''))
|
||||
if ('timestamp' not in token) or (not isinstance(token['timestamp'], int)):
|
||||
raise ValueError
|
||||
if ('userid' not in token) or (not isinstance(token['userid'], int)):
|
||||
raise ValueError
|
||||
if ('accesslevel' not in token) or (not isinstance(token['accesslevel'], int)):
|
||||
raise ValueError
|
||||
except ValueError, e:
|
||||
print e
|
||||
self.notify.warning('Invalid token.')
|
||||
response = {
|
||||
'success': False,
|
||||
'reason': 'Invalid token.'
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
|
||||
# Next, check if this token has expired:
|
||||
expiration = simbase.config.GetInt('account-token-expiration', 1800)
|
||||
tokenDelta = int(time.time()) - token['timestamp']
|
||||
if tokenDelta > expiration:
|
||||
response = {
|
||||
'success': False,
|
||||
'reason': 'This token has expired.'
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
|
||||
# This token is valid. That's all we need to know. Next, let's check if
|
||||
# this user's ID is in your account database bridge:
|
||||
if str(token['userid']) not in self.dbm:
|
||||
|
||||
# Nope. Let's associate them with a brand new Account object!
|
||||
response = {
|
||||
'success': True,
|
||||
'userId': token['userid'],
|
||||
'accountId': 0,
|
||||
'accessLevel': max(int(token['accesslevel']), minAccessLevel)
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
# Yep. Let's return their account ID and access level!
|
||||
response = {
|
||||
'success': True,
|
||||
'userId': token['userid'],
|
||||
'accountId': int(self.dbm[str(token['userid'])]),
|
||||
'accessLevel': max(int(token['accesslevel']), minAccessLevel)
|
||||
}
|
||||
callback(response)
|
||||
return response
|
||||
except:
|
||||
resp = {'success': False}
|
||||
callback(resp)
|
||||
return resp
|
||||
|
||||
return AccountDB.lookup(self, token, callback)
|
||||
|
||||
# --- FSMs ---
|
||||
class OperationFSM(FSM):
|
||||
|
@ -294,7 +257,6 @@ class OperationFSM(FSM):
|
|||
else:
|
||||
del self.csm.account2fsm[self.target]
|
||||
|
||||
|
||||
class LoginAccountFSM(OperationFSM):
|
||||
notify = directNotify.newCategory('LoginAccountFSM')
|
||||
TARGET_CONNECTION = True
|
||||
|
@ -315,6 +277,7 @@ class LoginAccountFSM(OperationFSM):
|
|||
self.userId = result.get('userId', 0)
|
||||
self.accountId = result.get('accountId', 0)
|
||||
self.accessLevel = result.get('accessLevel', 0)
|
||||
self.notAfter = result.get('notAfter', 0)
|
||||
if self.accountId:
|
||||
self.demand('RetrieveAccount')
|
||||
else:
|
||||
|
@ -330,6 +293,12 @@ class LoginAccountFSM(OperationFSM):
|
|||
return
|
||||
|
||||
self.account = fields
|
||||
|
||||
if self.notAfter:
|
||||
if self.account.get('LAST_LOGIN_TS', 0) > self.notAfter:
|
||||
self.notify.debug('Rejecting old token: %d, notAfter=%d' % (self.account.get('LAST_LOGIN_TS', 0), self.notAfter))
|
||||
return self.__handleLookup({'success': False})
|
||||
|
||||
self.demand('SetAccount')
|
||||
|
||||
def enterCreateAccount(self):
|
||||
|
@ -339,6 +308,7 @@ class LoginAccountFSM(OperationFSM):
|
|||
'ACCOUNT_AV_SET_DEL': [],
|
||||
'CREATED': time.ctime(),
|
||||
'LAST_LOGIN': time.ctime(),
|
||||
'LAST_LOGIN_TS': time.time(),
|
||||
'ACCOUNT_ID': str(self.userId),
|
||||
'ACCESS_LEVEL': self.accessLevel
|
||||
}
|
||||
|
@ -449,6 +419,7 @@ class LoginAccountFSM(OperationFSM):
|
|||
self.accountId,
|
||||
self.csm.air.dclassesByName['AccountUD'],
|
||||
{'LAST_LOGIN': time.ctime(),
|
||||
'LAST_LOGIN_TS': time.time(),
|
||||
'ACCOUNT_ID': str(self.userId)})
|
||||
|
||||
# We're done.
|
||||
|
@ -547,7 +518,6 @@ class CreateAvatarFSM(OperationFSM):
|
|||
self.csm.sendUpdateToAccountId(self.target, 'createAvatarResp', [self.avId])
|
||||
self.demand('Off')
|
||||
|
||||
|
||||
class AvatarOperationFSM(OperationFSM):
|
||||
POST_ACCOUNT_STATE = 'Off' # This needs to be overridden.
|
||||
|
||||
|
@ -570,7 +540,6 @@ class AvatarOperationFSM(OperationFSM):
|
|||
|
||||
self.demand(self.POST_ACCOUNT_STATE)
|
||||
|
||||
|
||||
class GetAvatarsFSM(AvatarOperationFSM):
|
||||
notify = directNotify.newCategory('GetAvatarsFSM')
|
||||
POST_ACCOUNT_STATE = 'QueryAvatars'
|
||||
|
@ -807,7 +776,6 @@ class SetNamePatternFSM(AvatarOperationFSM):
|
|||
self.csm.sendUpdateToAccountId(self.target, 'setNamePatternResp', [self.avId, 1])
|
||||
self.demand('Off')
|
||||
|
||||
|
||||
class AcknowledgeNameFSM(AvatarOperationFSM):
|
||||
notify = directNotify.newCategory('AcknowledgeNameFSM')
|
||||
POST_ACCOUNT_STATE = 'GetTargetAvatar'
|
||||
|
@ -863,7 +831,6 @@ class AcknowledgeNameFSM(AvatarOperationFSM):
|
|||
self.csm.sendUpdateToAccountId(self.target, 'acknowledgeAvatarNameResp', [])
|
||||
self.demand('Off')
|
||||
|
||||
|
||||
class LoadAvatarFSM(AvatarOperationFSM):
|
||||
notify = directNotify.newCategory('LoadAvatarFSM')
|
||||
POST_ACCOUNT_STATE = 'GetTargetAvatar'
|
||||
|
@ -1007,7 +974,6 @@ class UnloadAvatarFSM(OperationFSM):
|
|||
self.csm.air.writeServerEvent('avatarUnload', self.avId)
|
||||
self.demand('Off')
|
||||
|
||||
|
||||
# --- CLIENT SERVICES MANAGER UBERDOG ---
|
||||
class ClientServicesManagerUD(DistributedObjectGlobalUD):
|
||||
notify = directNotify.newCategory('ClientServicesManagerUD')
|
||||
|
|
|
@ -33,6 +33,9 @@ args = parser.parse_args()
|
|||
for prc in args.config:
|
||||
loadPrcFile(prc)
|
||||
|
||||
if os.path.isfile('dependencies/config/local.prc'):
|
||||
loadPrcFile('dependencies/config/local.prc')
|
||||
|
||||
localconfig = ''
|
||||
if args.base_channel:
|
||||
localconfig += 'air-base-channel %s\n' % args.base_channel
|
||||
|
|
Loading…
Reference in a new issue