Code:
import collections, logging, os, sys, weakref, time, datetime, platform, enum, locale, thing.accessor
import thing.asyncio as asyncio
import thing.system
from natuum.activeobjects import ApartmentActiveObject
import natuum.apartment, natuum.app, natuum.builder, natuum.renderer, natuum.jobQueue, natuum.configuration, natuum.client.action, natuum.client.pager, natuum.client.resource, natuum.client.craft, natuum.client.logic, natuum.util,
natuum.variation, natuum.indexBook, natuum.reward, natuum.growth, natuum.emotionCutscene, natuum.client.synergy, natuum.client.contract, natuum.client.relationship, natuum._entity.apartmentEvents, natuum._entity.framework.cluster
import natuum._entity.messageRPC as messageRPC
from natuum.nexon import NXLogClient
from natuum._entity.interfaces.clientSubscriber import IGatewaySubscriber
import natuum.gameService.wordFilter
if __thing_can_get_zcom_objects__:
import multiprocessing
if not __final__:
import natuum.objects
DEFAULT_CLIENT_RESOLUTION = (1920, 1080)
if not __final__:
LOG_DEBUG = natuum.getLogger('client.app').debug
LOG_WARNING = natuum.getLogger('client.app').warning
import socket
def callbackBeforeDump():
if natuum.client.app.Instance:
natuum.client.app.Instance.nxlog.stageLog(1020, 'crash')
natuum.flushLog()
def callbackAfterDump():
pass
TEXT_FORMAT_TO_PRELOAD = ((0, '2002', 24.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 12.0, 3, 0, 5, 1.0),
(0, 'KoPubDotum', 13.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 13.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 14.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 14.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 15.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 15.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 17.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 17.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 18.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 18.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 21.0, 5, 0, 5, 1.0), (0, 'KoPubDotum', 22.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 23.0, 5, 0, 5, 1.0), (0, 'KoPubDotum', 24.0, 3, 0, 5, 1.0),
(0, 'KoPubDotum', 26.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 27.36, 3, 0, 5, 1.0),
(0, 'KoPubDotum', 27.6, 5, 0, 5, 1.0), (0, 'KoPubDotum', 33.0, 5, 0, 5, 1.0),
(0, 'KoPubDotum', 34.0, 3, 0, 5, 1.0), (0, 'KoPubDotum', 50.0, 5, 0, 5, 1.0),
(0, 'NanumSquare', 12.0, 5, 0, 5, 1.0), (0, 'NanumSquare', 14.0, 3, 0, 5, 1.0),
(0, 'NanumSquare', 14.0, 5, 0, 5, 1.0), (0, 'NanumSquare', 15.0, 5, 0, 5, 1.0),
(0, 'NanumSquare', 16.0, 5, 2, 5, 1.0), (0, 'NanumSquare', 17.0, 5, 0, 5, 1.0),
(0, 'NanumSquare', 40.0, 5, 0, 5, 1.0))
SIDEMENU_HTMLIVEWS = ('sideMenu_page_collection', 'sideMenu_page_map', 'sideMenu_page_present',
'sideMenuAvatar_editor')
Instance = None
class ListenerType(enum.IntEnum):
ZONE = 0
PERSON = 1
class App(natuum.app.App):
_App__LONG_TIME_PLAY_WARNING_INTERVAL = 3600000
def __init__(self):
global Instance
super().__init__()
Instance = self
self.RealTimeClock = thing.Value.RealTimeClock()
self.RunOptions = self._getRunOptions('Natuum Client', '', (self._App__RunOptionHandler(),), sys.argv[1:])
now = time.clock()
self._App__clientTime = now
self._App__serverTime = now
self._App__fps = 60
self.nxlog = None
@Property
def FPS(self):
return self._App__fps
@FPS.setter
def FPS(self, fps):
self._App__fps = fps
def __sendPacket(self, blob):
pass
Resolution = thing.accessor.ri(doc='클라이언트 해상도')
Renderer = thing.accessor.ri(doc='렌더러 객체')
RPCManager = thing.accessor.ri(doc='RPC 관리자 객체')
SoundManager = thing.accessor.ri(None, doc='음향 관리자 객체')
Apartment = thing.accessor.ri(doc='클라이언트 아파트먼트')
EventLoop = thing.accessor.ri(None, doc='클라이언트 이벤트루프')
ClusterGuestNode = thing.accessor.ri(doc='클라이언트 클러스터 노드')
ClientZone = thing.accessor.rw(None)
QuitOnRendererShutdown = thing.accessor.ri(True, doc='렌더러 셧다운시 프로그램 종료')
Platform = thing.accessor.ri(doc='플랫폼 객체')
GatewayEntityID = thing.accessor.rw(None, doc='게이트웨이엔티티 ID')
ChatManagerEntityID = thing.accessor.rw(None, doc='채팅관리자엔티티 ID')
ItemDesignLibraryEntityID = thing.accessor.rw(None, doc='아이템설계관리자엔티티 ID')
DocumentContentLibraryEntityID = thing.accessor.rw(None, doc='문서내용관리자엔티티 ID')
ServerHostAddress = thing.accessor.ri(None, doc='게임 서버 주소')
LoaderThreadCount = thing.accessor.ri(1, doc='로더 스레드 갯수')
RealTimeClock = thing.accessor.ri(None, doc='RealTimeClock')
EngineConfig = thing.accessor.rw({}, doc='EngineConfig')
ServerConfig = thing.accessor.rw({}, doc='ServerConfig')
ControlModeParams = thing.accessor.ri({}, doc='ControlModeParams')
PlayerInitConfig = thing.accessor.ri({}, doc='PlayerInitConfig')
MaxFPS = thing.accessor.rw(None, doc='MaxFPS')
AccountSession = thing.accessor.ri(None, doc='AccountSession')
ClientSpace = thing.accessor.ri(None, doc='ClientSpace')
GameLogic = thing.accessor.ri(None, doc='GameLogic')
Context = thing.accessor.ri(None, doc='Context')
DummyAvatarSceneObjectData = thing.accessor.ri(None, doc='DummyAvatarSceneObjectData')
IsPreloading = thing.accessor.ri(True, doc='선로딩 여부')
UserConfig = thing.accessor.ri(None, doc='UserConfig')
InitialSplashLoadTask = thing.accessor.ri(None, doc='InitialSplashLoadTask')
_App__SERVER_EPOCH = datetime.datetime(2018, 1, 1)
def __setup(self) -> 'int, exit code':
if not __final__:
thing.system.setQuietMode(self.RunOptions.Quiet)
DebugLogCategories = '*,-action,-builder,-cutscene,' if self.RunOptions.SSOAuthToken is not None else ''
DebugLogCategories += os.environ.get('NT_DEBUG_LOG', '*') + ',' + self.RunOptions.DebugLogCategories
for category in DebugLogCategories.split(','):
if not category:
continue
if category.startswith('-'):
logger = natuum.getLogger(category[1:])
logger.setLevel(logging.INFO)
LOG_INFO('Log category [{}] level=debug was disabled'.format(logger.name))
from thing.changeset import changeset, NexonCrashReporterVersion
LOG_INFO('Changeset for Code:{}'.format(changeset))
LOG_INFO('BuildVersion:{}'.format(NexonCrashReporterVersion))
LOG_INFO('LauncherVer:{}, NGM Param:{}, NexonSN:{}, NexonSID:{}'.format(self.RunOptions.LauncherBuildVersion, self.RunOptions.LauncherOptionalParam, self.RunOptions.NexonSN, self.RunOptions.NexonSID))
self.Platform = platform = thing.Platform.Platform()
self.ETWSession_findObjects = platform.createETWSession('findObjects')
if self.RunOptions.SendCrashReport:
installed = platform.installCrashReporter(self.RunOptions.ProjectID)
if not installed:
LOG_WARNING('exception handler is not installed')
else:
platform.setCrashReporterInformation('ProcessType', self.RunOptions.ProductName)
from thing.changeset import NexonCrashReporterVersion
platform.setCrashReporterInformation('ProjectVersion', NexonCrashReporterVersion)
if natuum.m_logFilePath:
relPath = os.path.relpath(natuum.m_logFilePath, os.path.dirname(sys.executable))
platform.setCrashReporterAuxFile(relPath)
if __thing_supports_gc__:
if natuum.objects.m_refCycleFilePath:
relPath = os.path.relpath(natuum.objects.m_refCycleFilePath, os.path.dirname(sys.executable))
platform.setCrashReporterAuxFile(relPath)
platform.registerCrashReporterCallbackBeforeDump(callbackBeforeDump)
thing.system._installExceptHook(fromInit=False)
if not __final__:
self._App__initConsoleCtrlHandler()
if not __final__:
platform.listenEasyProfileGUI(28077)
self._App__thMan, self.RPCManager, self._App__chMan, self._App__chManThread = self._App__setupRPCManager()
self._App__clientLoaderApartments = self._App__createLoaderApartments()
rootFS = thing.FileSystem.FileSystem(natuum.getRootPath())
dataFS = rootFS['data']
try:
resourcesFS = rootFS['resources']
except Exception:
resourcesFS = rootFS.createDirectory('resources')
rm = natuum.client.resource.Manager(dataFS, resourcesFS)
rm.IsPreloading = self.IsPreloading
self._App__clientLoaderRegisterTokens = self._App__setupResourceManager(rm, self._App__clientLoaderApartments)
self.JobQueue = natuum.jobQueue.JobQueue()
self.EngineConfig = engineConfig = rm.load(natuum.builder.combineID('_', 'EngineConfig')).lockForReading().Data
self.ServerConfig = rm.load(natuum.builder.combineID('_', 'ServerConfig')).lockForReading().Data[self.RunOptions.ClusterID]
self.GatewayEntityID = self.ServerConfig['GatewayEntityID']
self.ChatManagerEntityID = self.ServerConfig['ChatManagerEntityID']
self.ItemDesignLibraryEntityID = self.ServerConfig['ItemDesignLibraryEntityID']
self.DocumentContentLibraryEntityID = self.ServerConfig['DocumentContentLibraryEntityID']
self.ServerHostAddress = self.RunOptions.Host
self.ControlModeParams = rm.ensureResource('controlModeParams@GameSetting')
self.PlayerInitConfig = rm.ensureResource('playerInitialization@GameSetting')
fullScreen = self.RunOptions.FullScreen
if fullScreen:
import ctypes
user32 = ctypes.windll.user32
screenWidth = user32.GetSystemMetrics(0)
screenHeight = user32.GetSystemMetrics(1)
screenRatio = screenWidth / screenHeight
if screenRatio > 1.7777777777777777:
width, height = (2400, 1080)
elif screenRatio > 1.6:
if screenWidth == 2048 and screenHeight == 1152:
width, height = screenWidth, screenHeight
else:
width, height = (1920, 1080)
elif screenRatio > 1.3333333333333333:
width, height = (1920, 1200)
else:
width, height = (1600, 1200)
else:
width, height = DEFAULT_CLIENT_RESOLUTION
self.AspectRatio = thing.Dirp.Dirp_Static(thing.system.vec(width / height, 1))
self.MaxFPS = max(int(engineConfig.Config.get('maxFPS', 60)), 1)
rendererMode = thing.Renderer.RendererManager().createRendererMode(fullScreen, width, height, 32)
self.Renderer = self._App__Renderer(rendererMode, engineConfig.Config)
self.Renderer.TerrainTessellationQueueLength = 3
self.Renderer.HangDetector = thing.ToolHelpers.HangDetector(self._App__onHangDetected)
self.PropCanvasMipHeadCutCountOnLoad = 0
userConfig = self._loadUserConfig()
if userConfig is not None:
self.UserConfig = userConfig
self._refreshCanvasLoadQuality(userConfig)
self.Renderer.UserConfig = userConfig
else:
self.UserConfig = self.Renderer.UserConfigValue['']
currentLocale = locale.getdefaultlocale()[1]
if currentLocale != 'cp949':
if self.Renderer.NativeWindowHandle:
text = 'Unsupported system locale : {}'.format(currentLocale)
text += '\n\nTo execute the client, change system locale to cp949 (Korean)'
self.showWindowsMessageBox(text)
self.nxlog = None
self.quit(0)
return
self._App__rendererModeWatchToken = self.Renderer.ModeVersion.addWatcher(self._App__onRendererModeChangedEntry, '__call__', weakref.ref(self))
self.nxlog = NXLogClient(self.RunOptions.NexonSN, self.RunOptions.NexonSID)
self.nxlog.stageLog(420, '__setup')
self._App__rendererReadyAtom = rendererReadyAtom = thing.Thread.Atom()
self._App__pagerThread = thread = self._App__thMan.createThread(thing.system.nativeThreadEntry, '__call__', self._App__pagerThreadEntry, rendererReadyAtom)
thread.Name = 'Main Updater'
thread.run()
_App__settingsDirName = 'settings'
_App__userConfigFileName = 'userConfig.nothing'
def _loadUserConfig(self):
settingsDirPath = os.path.join(natuum.getRootPath(), 'storage', __class__._App__settingsDirName)
try:
if os.path.exists(settingsDirPath):
fs = thing.FileSystem.FileSystem(settingsDirPath)
else:
raise KeyError
f = fs[__class__._App__userConfigFileName]
ra = thing.Archive.ReadingArchive_FromFile(f)
userConfig = thing.Collection.Dict()
userConfig.load(ra)
LOG_INFO('user config loaded')
except KeyError:
LOG_WARNING('cannot load user config')
userConfig = None
return userConfig
def _saveUserConfig(self, userConfig):
settingsDirPath = os.path.join(natuum.getRootPath(), 'storage', __class__._App__settingsDirName)
try:
os.makedirs(settingsDirPath)
except OSError:
pass
try:
fs = thing.FileSystem.FileSystem(settingsDirPath)
f = fs.createFile(__class__._App__userConfigFileName, True)
wa = thing.Archive.WritingArchive_FromFile(f)
userConfig.save(wa)
wa.flush()
LOG_INFO('user config saved')
except:
LOG_WARNING('cannot save user config')
def _refreshCanvasLoadQuality(self, userConfig):
propTexQuality = userConfig.get('PropTextureQuality', -1)
if propTexQuality >= 0:
propTexQuality = min(2, propTexQuality)
oldValue = self.PropCanvasMipHeadCutCountOnLoad
newValue = (2, 1, 0)[propTexQuality]
if oldValue != newValue:
self.PropCanvasMipHeadCutCountOnLoad = newValue
return True
return False
def __preload(self):
markerResourceIDs = self._App__collectMarkerResources()
effectPreloadList = tuple(markerResourceIDs.get('Effect', set()) | set(self._App__PRELOAD_EFFECTS))
rm = natuum.client.resource.Manager.Instance
rm.preload('Avatar', preloadList=('AT099800(man_default)', 'AT099900(woman_default)'))
rm.preload('Effect', preloadList=effectPreloadList)
rm.preload('CameraAngle')
rm.preload('CameraAngleBlender')
rm.preload('CameraAngleBlenderSet')
rm.preload('HTMLView', preloadList=SIDEMENU_HTMLIVEWS)
rm.preload('HTML', preloadList=('sideMenu', 'sideMenuAvatar'))
rm.preload('ItemDesign')
rm.preload('ItemIO')
rm.preload('MeshSet', preloadList=(self._App__DUMMY_AVATAR_MESHSETS))
rm.preload('PropPart', preloadList=(self._App__PRELOAD_PROPPARTS))
rm.preload('Quest')
rm.preload('VueApp', preloadList=('app', ))
rm.preload('Zone', preloadList=('ZR0000(carveKiranaArea)', ))
for typeName, resourceIDs in markerResourceIDs.items():
if typeName in ('Effect', 'ItemDesign'):
continue
else:
rm.preload(typeName, preloadList=(tuple(resourceIDs)))
rm.preload('GameSetting')
def __collectMarkerResources(self):
def collectResourceIDs(value, resourceIDs_):
if isinstance(value, str) and natuum.builder.isResourceID(value):
name, typeName, _ = natuum.builder.splitID(value)
resourceIDs_[typeName].add(name)
elif not isinstance(value, str) or isinstance(value, collections.Sequence):
for value_ in value:
collectResourceIDs(value_, resourceIDs_)
elif isinstance(value, collections.Mapping):
for value_ in value.values():
collectResourceIDs(value_, resourceIDs_)
def collectMarkerClasses(marker, markerClasses_):
markerClasses.add(marker)
for marker_ in marker.__subclasses__():
collectMarkerClasses(marker_, markerClasses_)
markerResourceIDs = collections.defaultdict(set)
markerClasses = set()
collectMarkerClasses(natuum.client.clientSpace.marker.Marker, markerClasses)
for arranger in natuum.client.clientSpace.arranger.__dict__.values():
if isinstance(arranger, type):
collectMarkerClasses(arranger, markerClasses)
for marker in markerClasses:
for attribute in marker.__dict__.values():
collectResourceIDs(attribute, markerResourceIDs)
collectMarkerClasses = None
collectResourceIDs = None
return markerResourceIDs
def __run(self) -> 'int, exit code':
if not __final__:
if not self.RunOptions.NoUsageReport:
self._App__sendUsageReport()
windowTitle = 'Peria Chronicles{}'.format(natuum.util.getBuildVersion())
rendererExitCode = self.Renderer.start(windowTitle, thing.system.ICanvas.PIXEL_FORMAT_R8G8B8A8, self._App__rendererReadyAtom, self.QuitOnRendererShutdown)
return self._App__exitCode
def __cleanup(self) -> None:
global Instance
if not __final__:
for i in range(5):
if self._App__pagerThread.wait(1):
break
else:
(LOG_DEBUG if i <= 2 else LOG_WARNING)('Pager 스레드 종료 대기중 (%d초)', i + 1)
else:
self._App__pagerThread.wait(5)
del self._App__pagerThread
self.JobQueue.shutdown()
self._App__clientLoaderRegisterTokens.clear()
for apartment in self._App__clientLoaderApartments.values():
apartment.shutdown()
self._App__shutdownTableManagers()
self._App__cleanupResourceManager()
if self._App__chMan is not None:
self._App__chMan.shutdown()
VERIFY(self._App__chManThread.wait(5))
del self._App__chManThread
del self._App__mainApartment
self.RPCManager.shutdown()
Instance = None
@Thing.overrides(natuum.app.App)
def run(self) -> 'int, exit code':
self._App__setup()
ret = self._App__run()
self._App__cleanup()
return ret
@Thing.overrides(natuum.app.App)
def quit(self, exitCode):
LOG_INFO('client quit called')
self.Renderer.shutdown()
curPage = natuum.client.pager.CurPage
if curPage is not None:
if not natuum.client.pager.command((curPage.canQuitApp), wait=True):
return
super().quit()
self._App__exitCode = exitCode
if self.RunOptions.SendCrashReport:
self.Platform.setCrashReporterClientExit(exitCode)
if natuum.client.app.Instance.nxlog is not None:
natuum.client.app.Instance.nxlog.stageLog(1010, str(exitCode))
self.Renderer.quit(True)
def isInRendererThreadGroup(self, threadID):
return threadID == self.Renderer.ThreadID
def collectZcomObjects(self, showBackrefsOfIDs=False):
if not __thing_can_get_zcom_objects__:
return
createdZcomObjects = self._App__createdZcomObjects
if createdZcomObjects is None:
self._App__createdZcomObjects = thing.ToolHelpers.CreatedObjects()
else:
self._App__createdZcomObjects = None
objs = None
try:
backrefsDir = os.path.join(natuum.getLogPath(), 'backrefs')
os.makedirs(backrefsDir, exist_ok=True)
today = datetime.datetime.today()
timestamp = '%4d%02d%02d_%02d%02d%02d' % (today.year, today.month, today.day, today.hour, today.minute, today.second)
graphFilePath = os.path.join(backrefsDir, 'graph_c{}.txt'.format(timestamp))
with open(graphFilePath, 'w', encoding='utf_8-sig') as file:
natuum.objects.Graph.collectToFile(file)
objs = list(createdZcomObjects)
objectIDsFilePath = os.path.join(backrefsDir, 'objects_c{}.txt'.format(timestamp))
with open(objectIDsFilePath, 'w') as file:
natuum.objects.saveObjectIDsToFile(file, objs)
if showBackrefsOfIDs:
multiprocessing.Process(target=(natuum.objects.showBackrefsOfIDs),
args=(
backrefsDir, graphFilePath, objectIDsFilePath)).start()
thing.system._debugBreak()
objs.clear()
finally:
if objs is not None:
objs.clear()
def checkMemory(self):
if __final__ or (self._App__memoryReclaimer is None):
return
for i in range(10):
self._App__memoryReclaimer.reclaimAll()
natuum.objects.GarbageCollector().collect(-1, True)
trendsCollector = self._App__objectCountTrendsCollector
if trendsCollector is None:
self._App__objectCountTrendsCollector = trendsCollector = natuum.objects.CountTrendsCollector()
trendsCollector.collect()
trends = [(v, trendsCollector.getCount(k), k) for k, v in trendsCollector.items()]
trends.sort(reverse=True)
tracker = self._App__memoryTracker
lines = []
lines.append('**** object count trend log start ****')
for trend, count, typeName in trends:
countList = tracker.get(typeName, None)
if countList is None:
countList = list()
tracker[typeName] = countList
else:
countList.append(count)
lines.append('{:G}\t{}\t{}'.format(trend, typeName, '\t'.join([str(i) for i in countList])))
lines.append('**** object count trend log end ****')
def getRoughServerTime(self) -> 'int. milliseconds':
passed = int(time.time() - self._App__clientTime) * 1000
return (self._App__serverTime + passed) % 2147483647
def setListener(self, flag, position) -> None:
soundManager = self.SoundManager
if soundManager is not None:
configuration = self.ControlModeParams.get('sound')
if configuration is None:
if ListenerType.ZONE == flag:
soundManager.Listener = position
elif configuration['listener'] == flag:
soundManager.Listener = thing.Dirp.Dirp_FromLocal(configuration['local'], thing.math.ROT_IDENTITY, position)
def _setServerTime(self, serverNow):
self._App__clientTime = time.time()
self._App__serverTime = int(serverNow * 1000)
class __Renderer(natuum.renderer.Renderer):
@Thing.overrides(natuum.renderer.Renderer)
def onRendererCloseButton(self, renderer: 'thing.system.IRenderer', private) -> None:
Instance.quit(0)
@Thing.overrides(natuum.renderer.Renderer)
def onRendererConfigChange(self, renderer: 'thing.system.IRenderer', private) -> None:
if isinstance(private, thing.system.IMutableMapping):
userConfig = private
natuum.client.app.Instance._saveUserConfig(userConfig)
if natuum.client.app.Instance._refreshCanvasLoadQuality(userConfig):
natuum.client.pager.command(natuum.client.app.Instance.GameLogic.onCanvasLoadQualityChange)
def _registerLayerCategories(self) -> None:
self.registerLayerCategory('UI').forget()
class __RunOptionHandler(natuum.runOptions.IHandler):
def fillDefaults(self, options):
options.Host = None
options.Quiet = False
options.InfuseActor = None
options.CrashReportURL = 'http://socorro.thingsoft.com:8882/submit'
options.ProductName = 'client'
options.ProductVersion = '0.8.1'
options.NoUsageReport = False
options.NoEffect = False
options.BatchJob = None
options.Locale = None
options.ClusterID = 'default'
options.SSOAuthToken = None
options.FullScreen = False
options.DebugLogCategories = '*,-action,-builder,-cutscene' if __final__ else ''
options.ProjectID = 'peria_dev'
options.LauncherBuildVersion = -1
options.LauncherOptionalParam = ''
options.NexonSN = 0
options.NexonSID = None
options.disableNXLog = False
crashReport = os.environ.get('NT_NO_CRASH_REPORT', '0')
options.SendCrashReport = crashReport == '0'
def buildParser(self, parser: 'optparse.OptionParser', options) -> None:
parser.add_option('-a', '--address', dest='Host', help='server address')
parser.add_option('-q', '--quiet', dest='Quiet', default=False, action='store_true', help='quiet mode')
parser.add_option('-i', '--infuseActor', dest='InfuseActor', help='auto infuse actor')
parser.add_option('-b', '--batch', dest='BatchJob', help='batch job file')
parser.add_option('-l', '--locale', dest='Locale', help='locale')
parser.add_option('-C', '--cluster', dest='ClusterID', help='cluster id')
parser.add_option('--crashreport-url', dest='CrashReportURL', help='crash report url')
parser.add_option('--product-name', dest='ProductName', help='product name')
parser.add_option('--product-version', dest='ProductVersion', help='product version')
parser.add_option('--nousagereport', dest='NoUsageReport', default=False, action='store_true', help="don't send usage report")
parser.add_option('-t', '--auth', dest='SSOAuthToken', help='auth token')
parser.add_option('-f', '--fullScreen', dest='FullScreen', default=False, action='store_true', help='full screen mode')
parser.add_option('--debugLog', type='str', dest='DebugLogCategories', help='comma seperated log categories')
parser.add_option('--projectid', dest='ProjectID', help='crash reporter project id')
parser.add_option('--launcherBuildVersion', dest='LauncherBuildVersion', help='launcher build version')
parser.add_option('--launcherParam', dest='LauncherOptionalParam', default='', help='argument from NexonGameManager')
parser.add_option('--nexonsn', dest='NexonSN', default=0, help='argument from NexonGameManager')
parser.add_option('--nexonsid', dest='NexonSID', default='', help='argument from NexonGameManager')
parser.add_option('--disableNXLog', dest='disableNXLog', default=False, action='store_true', help='disable NXLOG')
def onParsed(self, parser: 'optparse.OptionParser', options, args: tuple) -> None:
if options.BatchJob is not None:
batchJobPath = os.path.join(natuum.getRootPath(), 'settings', options.BatchJob)
options.BatchJob = natuum.util.readXDF(batchJobPath)
if options.Locale is not None:
if options.Locale in ('ko', 'ja', 'zh', 'en'):
natuum.util.DefaultLocale = options.Locale
def __createLoaderApartments(self) -> '{ str: thing.system.IApartment }':
apartments = {}
for i in range(self.LoaderThreadCount):
name = 'Loader{}'.format(i)
apartmentObject = ApartmentActiveObject(self._App__thMan, self.RPCManager)
apartmentObject.start(name, -0.13333333333333333)
apartments[name] = apartmentObject.Apartment
return apartments
if not __final__:
def __initConsoleCtrlHandler(self):
def _terminateProcess(ctrlType):
thing.system._TerminateProcess()
thing.system.setConsoleCtrlHandler(_terminateProcess)
def __setupRPCManager(self):
thMan = thing.Thread.ThreadManager()
rpcManager = thing.RPC.RPCManager(thMan)
self._App__mainApartment = natuum.apartment.Apartment()
return (
thMan, rpcManager, None, None)
def __setupResourceManager(self, rm, loaderApartments) -> '{ str : thing.system.IForgettableToken }':
tokens = {}
for name, apartment in loaderApartments.items():
token = rm.CacheManager.registerDelayedLoaderApartment(name, apartment.Inner)
tokens[name] = token
return tokens
def __cleanupResourceManager(self) -> None:
natuum.client.resource.Manager.Instance.shutdown()
@staticmethod
def __onRendererModeChangedEntry(watched, weakSelf, hint, funcName) -> None:
self = weakSelf()
if self is not None:
clientSpace = self.ClientSpace
if clientSpace is not None:
natuum.client.pager.command(clientSpace.onRendererModeChanged)
def __pagerThreadEntry(self, rendererReadyAtom: 'thing.system.IAtom') -> None:
self.Context = thing.Common.Sites()
self.EventLoop = loop = natuum._entity.apartmentEvents.EventLoop()
asyncio.set_event_loop(loop)
self.Apartment = loop.Apartment
thing.system.setSwitchIntervalForCurrentThread(1)
rendererReadyAtom.wait()
inputManager = natuum.client.input.Manager()
self._App__initFontManager()
self.showInitialSplash(True)
if self.InitialSplashLoadTask:
loop.run_until_complete(self.InitialSplashLoadTask)
_ = self.Platform.disableAccessibility()
self.GUI = guiSystem = thing.Renderer.GUISystem()
self.Renderer.GUISystem = guiSystem
self.SoundManager = soundManager = self._App__createSoundManager(parameters=(self.ControlModeParams))
try:
soundManager.setVolume('music', self.UserConfig['BGMVolume'])
soundFXVol = self.UserConfig['SoundEffectVolume']
for bus in ('effect', 'voice', 'environment', 'kiranaContract'):
soundManager.setVolume(bus, soundFXVol)
except KeyError:
pass
natuum.configuration.Configuration.initialize()
self.ClusterGuestNode = node = natuum._entity.framework.cluster.setup_guest(self.ServerConfig['GatewayServers'])
if self.ServerHostAddress is not None:
node.overrideConnectingtHost(self.ServerHostAddress)
self.AccountSession = natuum.client.app.AccountSession()
self.ZoneChanged = natuum.signal.Signal(natuum.client.zone.Zone)
self._App__initClassTables()
if self.IsPreloading:
self._App__preload()
self._App__initTableManagers()
clientSpace = natuum.client.clientSpace.ClientSpace()
clientSpace.onRendererModeChanged()
clientSpace.addDesktop()
clientSpace.addDesktop()
clientSpace.addInventory()
self.ClientSpace = clientSpace
inputManager.setEventHandler(clientSpace)
self.GameLogic = gameLogic = natuum.client.logic.GameLogic(clientSpace)
gameLogic.SyncClock.defer(App._App__LONG_TIME_PLAY_WARNING_INTERVAL, App._App__warnLongTimePlay, 1).forget()
self._App__setupIDE()
self._App__buildDummyAvatar()
self._App__processorToken = thing.Thread.Processor().addTask(0, self.MaxFPS)
self._App__memoryReclaimer = memoryReclaimer = thing.Common.MemoryReclaimer()
memoryReclaimer.clearPolicies()
memoryReclaimer.appendPolicy(memoryReclaimer.MEMORY_RECLAIMER_CRITERION_AVAILABLE_PHYSICAL_MB_LESS_THAN, 100, 209715200, 5)
memoryReclaimer.appendPolicy(memoryReclaimer.MEMORY_RECLAIMER_CRITERION_AVAILABLE_PHYSICAL_MB_LESS_THAN, 500, 104857600, 10)
memoryReclaimer.appendPolicy(memoryReclaimer.MEMORY_RECLAIMER_CRITERION_AVAILABLE_PHYSICAL_MB_LESS_THAN, 1000, 1, 15)
memoryReclaimer.appendPolicy(memoryReclaimer.MEMORY_RECLAIMER_CRITERION_PAGE_FILE_USAGE_MB_GREATER_THAN, 7168, 102400, 15)
natuum.apartment.getCurrentApartment().Alarm.postTask(self._App__reclaimMemory, 0).forget()
if not __final__:
natuum.apartment.getCurrentApartment().Alarm.postTask(self._App__logMemoryReclaimer, 60000).forget()
self._App__memoryTracker = {}
loop.call_soon(natuum.client.pager.enter)
loop.run_forever()
loop.run_until_complete(node.shutdown())
loop.close()
gameLogic.shutdown()
thing.Client.EffectManager().terminate()
soundManager.shutdown()
guiSystem.shutdown()
self.Renderer.GUISystem = None
def __initClassTables(self) -> None:
rm = natuum.client.resource.Manager.Instance
natuum.client.action.ActionTableManager.ensureInstance()
rawClassTables = rm.load(natuum.gameClass.CLASS_TABLE).lockForReading().Data
natuum.gameClass.initManager(rawClassTables)
if self.IsPreloading:
natuum.gameClass.getManagerInstance().preloadClasses()
def __initTableManagers(self) -> None:
rm = natuum.client.resource.Manager.Instance
natuum.emotionCutscene.initManager(rm)
natuum.client.craft.initManager(rm)
natuum.variation.initManager(rm)
natuum.indexBook.initManager(rm)
natuum.client.contract.initManager(rm)
natuum.reward.initManager(rm)
natuum.growth.initManager(rm)
natuum.actor.initManager(rm)
natuum.client.quest.initManager(rm)
natuum.town.initManager(rm)
natuum.client.relationship.initManager(rm)
natuum.client.synergy.initManager(rm)
natuum.gameService.wordFilter.initManager(rm)
def __initFontManager(self) -> None:
fontManager = thing.Renderer.FontManager()
rm = natuum.client.resource.Manager.Instance
rm.preload('Font', preloadList=(natuum.util.FontNames))
for fontName in natuum.util.FontNames:
fontID = natuum.builder.combineID(fontName, 'Font')
fontData = rm.ensureResource(fontID)
fontManager.registerFontData(fontData[0], fontData[1])
for info in TEXT_FORMAT_TO_PRELOAD:
(fontManager.createRasterTextFormat)(self.Renderer, *info)
def __setupIDE(self) -> None:
pass
def __buildDummyAvatar(self) -> None:
rm = natuum.client.resource.Manager.Instance
meshSet = rm.ensureResource('AP7250@MeshSet')
sceneObjectData = thing.Scene.SceneObjectData_Mesh()
sceneObjectData.ShaderFlags = thing.system.IShader.SHADER_SHADOW_MAP | thing.system.IShader.SHADER_REALISTIC
for meshName, meshAndBone in meshSet.items():
sceneObjectMeshElementData = thing.Scene.SceneObjectMeshElementData()
sceneObjectMeshElementData.Mesh = meshAndBone['mesh']
sceneObjectData.Elements[meshName] = sceneObjectMeshElementData
self.Renderer.warm(sceneObjectData, 0)
self.DummyAvatarSceneObjectData = sceneObjectData
def __shutdownTableManagers(self) -> None:
natuum.client.craft.shutdownManager()
natuum.client.action.ActionTableManager.shutdown()
natuum.gameClass.shutdownManager()
def __reclaimMemory(self) -> None:
if self.IsQuitting:
return
waitDuration = self._App__memoryReclaimer.reclaim()
natuum.apartment.getCurrentApartment().Alarm.postTask(self._App__reclaimMemory, waitDuration).forget()
if not __final__:
def __logMemoryReclaimer(self) -> None:
if self.IsQuitting:
return
memoryReclaimer = self._App__memoryReclaimer
LOG_INFO('available physical memory: {} GB'.format(memoryReclaimer.AvailablePhysicalMemoryGB))
LOG_INFO('page file usage: {} GB'.format(memoryReclaimer.PageFileUsageGB))
natuum.apartment.getCurrentApartment().Alarm.postTask(self._App__logMemoryReclaimer, 60000).forget()
def __sendUsageReport(self) -> None:
def sendReport(info: 'sequence of ( str, str )') -> str:
tokenDelim = '_TDELIM_'
emptyToken = '_EMPTY'
stream = tokenDelim.join([value if value != '' else emptyToken for value in info])
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(stream.encode('utf-8'), ('wild.thingsoft.com', 8025))
LOG_INFO('Usage report sent..')
except socket.error:
pass
sock.close()
rendererManager = thing.Renderer.RendererManager()
usageInfo = (
'{}'.format(platform.node()),
'{} {}'.format(platform.system(), platform.version()),
'{}'.format(platform.processor()),
'{}'.format(self.Platform.MemorySize),
'{}'.format(self.Platform.HardDiskSize),
'{}'.format(rendererManager.DeviceDescription.split('\x00')[0].strip()),
'{}'.format(natuum.util.getBuildVersion()))
sendReport(usageInfo)
sendReport = None
@classmethod
def __createSoundManager(cls, parameters=None, liveUpdate=False) -> 'thing.system.ISoundManager':
rm = natuum.client.resource.Manager.Instance
masterBank = rm.load(cls._App__masterBankResourceID)
masterBank = masterBank.lockForReading().Data
try:
masterBank = masterBank.SoundData
except KeyError:
LOG_WARNING('{}를 읽지 못했습니다. 이벤트 경로를 읽지 못해 소리가 나오지 ' + '않습니다.'.format(cls._App__masterBankResourceID))
return
else:
masterStringBank = rm.load(cls._App__masterStringBankResourceID)
try:
masterStringBank = masterStringBank.lockForReading().Data.SoundData
except KeyError:
LOG_WARNING('{}를 읽지 못했습니다. 이벤트 경로를 읽지 못해 소리가 나오지 ' + '않습니다.'.format(cls._App__masterStringBankResourceID))
return
else:
busGroups = cls._App__getBusGroups(parameters)
return thing.Sound.SoundManager(masterBank, masterStringBank, liveUpdate, natuum.getRootPath(), busGroups)
def __closeSplashZoneViewerMarker(self):
self._App__splashZoneViewerMarker.close()
self._App__splashZoneViewerMarker = None
def showInitialSplash(self, show):
self._App__splashNoticeLayer = None
self._App__splashLayer = None
if self._App__splashZoneViewerMarker:
self.Renderer.Timer.defer(0, self._App__closeSplashZoneViewerMarker).forget()
if show:
rm = natuum.client.resource.Manager.Instance
canvas = rm.load('ui_title_booting@Canvas').lockForReading().Data
self._App__splashLayer = self.Renderer.createOverlayLayer(thing.Dirp.Dirp_Static(), thing.Dirp.Dirp_Static(self.AspectRatio), canvas)
self.Renderer.updateLayers()
@staticmethod
def __onSplashZoneLoaded(fut_) -> None:
app = natuum.client.app.Instance
canvas = app._App__splashZoneViewerMarker.getViewCanvas()
app._App__splashLayer = app.Renderer.createOverlayLayer(thing.Dirp.Dirp_Static(), thing.Dirp.Dirp_Static(app.AspectRatio), canvas)
text = natuum.client.text.Text('로딩 중입니다. 기다리는 동안 지형 편집을 연습해 보세요.', 22, 'NanumSquare', thing.system.vec(xyzw=1), thing.system.vec(a=1), 'bold', 0.0)
app._App__splashNoticeLayer = app.Renderer.createOverlayLayer(thing.Dirp.Dirp_Static(thing.system.vec(0.5, 0.8)), thing.Dirp.Dirp_Static(thing.system.vec(text.getWidth(), text.getHeight()) / text.getHeight() * 0.04), text.
getCanvas())
app.Renderer.updateLayers()
def showWindowsMessageBox(self, text, title='Peria Chronicles', uType=0, quit=False):
import ctypes
MessageBox = ctypes.windll.user32.MessageBoxW
ret = None
if natuum.client.app.Instance.Renderer.NativeWindowHandle:
ret = MessageBox(natuum.client.app.Instance.Renderer.NativeWindowHandle, text, 'Peria Chronicles', uType)
if quit:
self.GameLogic.turnOffGame()
return ret
@staticmethod
def __warnLongTimePlay(count):
app = Instance
if app is None:
return
gameLogic = app.GameLogic
if gameLogic is None:
return
getLocalString = natuum.action.ActionTableManager.getInstance().getLocalString
author = getLocalString('NoticeAuthor')
longTimePlayWarning = getLocalString('LongTimePlayWarning').format(N=count)
ageRatingInformation = getLocalString('AgeRatingInformation')
gameLogic.createMarker('message', ('(색상:빨강)(크기:크게){}(/크기)(/색상)'.format(longTimePlayWarning)), forced=True)
gameLogic.SyncClock.addWork(5000, (gameLogic.createMarker), 'message', ('(색상:빨강)(크기:크게){}(/크기)(/색상)'.format(ageRatingInformation)), forced=True).forget()
gameLogic.vueUiController.ChattingView.addChatText('notice', author, longTimePlayWarning)
gameLogic.vueUiController.ChattingView.addChatText('notice', author, ageRatingInformation)
gameLogic.SyncClock.defer(App._App__LONG_TIME_PLAY_WARNING_INTERVAL, App._App__warnLongTimePlay, count + 1).forget()
@staticmethod
def __getBusGroups(parameters) -> '{ str : var }':
if parameters is None:
return {}
try:
return parameters['sound']['bus']['groups']
except KeyError:
return {}
@staticmethod
def __onHangDetected(detected: 'thing.system.IHangDetector') -> None:
if not __final__:
import thing.debugger
if thing.debugger.checkAttached():
LOG_WARNING('hang detected')
thing.debugger.breakIfAttached()
return
try:
raise RuntimeError('hang detected')
except Exception as e:
try:
sys.excepthook(type(e), e, e.__traceback__)
finally:
e = None
del e
_App__thMan = None
_App__chMan = None
_App__clientLoaderApartments = None
_App__clientLoaderRegisterTokens = None
_App__chManThread = None
_App__pagerThread = None
_App__rendererReadyAtom = None
_App__exitCode = 0
_App__serverPort = 9999
_App__server = None
_App__memoryReclaimer = None
_App__memoryChecker = None
if not __final__:
_App__objectCountTrendsCollector = None
if __thing_can_get_zcom_objects__:
_App__createdZcomObjects = None
_App__actionTableManager = None
_App__masterBankResourceID = natuum.builder.combineID('MasterBank', 'Sound')
_App__masterStringBankResourceID = natuum.builder.combineID('MasterBankstrings', 'Sound')
_App__splashLayer = None
_App__splashNoticeLayer = None
_App__splashZoneViewerMarker = None
_App__PRELOAD_EFFECTS = ('EF7000', 'EF7000_lumi', 'EF7001', 'EF7002', 'EF7003',
'EF7004', 'EF7006', 'EF7007', 'EF7010', 'EF7012', 'EF7013',
'EF7016', 'EF7019', 'EF7020', 'EF7022', 'EF7024', 'EF7033',
'EF7050', 'EF7051', 'EF7052', 'EF7053', 'EF7054', 'EF7058',
'EF7074', 'EF7075', 'EF7080', 'EF7081', 'EF7082', 'EF7083',
'EF7084', 'EF7093', 'EF7094', 'EF7095', 'EF7096', 'EF7098',
'EF7099', 'EF8000', 'EF8001', 'EF8002', 'EF8003', 'EF8003',
'EF8004', 'EF8008', 'EF8010', 'EF8011', 'EF8012', 'EF8013',
'EF8015', 'EF8016', 'EF8017', 'EF8020', 'EF8021', 'EF8022',
'EF8030', 'EF8031', 'EF8040', 'EF8041', 'EF8050', 'EF8051',
'EF8100', 'EF8100a', 'EF8100b', 'EF8100d', 'EF8107',
'EF8130', 'EF8131', 'EF8132', 'EF8133', 'EF8134', 'EF8135',
'EF8136', 'EF8137', 'EF8139', 'EF8142', 'EF8142', 'EF8143',
'EF8144', 'EF8145', 'EF8146', 'EF8147', 'EF8148', 'EF8149',
'EF8149', 'EF8149', 'EF8150', 'EF8151', 'EF8152', 'EF8153',
'EF8154', 'EF8156', 'EF8159', 'EF8160', 'EF8162', 'EF8165',
'EF8167', 'EF8168', 'EF8170', 'EF8171', 'EF8172', 'EF8173',
'EF8174', 'EF8175', 'EF8176', 'EF8179', 'EF8182', 'EF8184',
'EF8186', 'EF8187', 'EF8188', 'EF8189', 'EF8191', 'EF8192',
'EF8194', 'EF8196', 'EF8197', 'EF8198', 'EF8199', 'EF8200',
'EF8201', 'EF8205', 'EF8207', 'EF8210', 'EF8211', 'EF8212',
'EF8213', 'EF8216', 'EF8217', 'EF8218', 'EF8219', 'EF8220',
'EF8158a', 'EF8158b', 'EF8158c', 'EF8224', 'EF8233',
'EF8234', 'EF8236', 'EF8237', 'SEF(warning_01)', 'SEF1018(world_map)',
'SEF1020(marker_open)', 'SEF1023(guardian_qattack_ready)',
'SEF1025(guardian_qattack_fire)', 'SEF1026(guardian_qattack_targeting)',
'SEF1028(actionmaker_click)', 'SEF1029(actionmaker_hover)',
'SEF1030(actionmaker_open)', 'SEF1031(actionmaker_release)',
'SEF1032(conversation_click)', 'SEF1033(conversation_hover)',
'SEF1034(f_key_to_continue_conversation)', 'SEF1035(f_key_to_start_conversation)',
'SEF1036(general_button_click)', 'SEF1037(general_button_release)',
'SEF1038(changing_tab)', 'SEF1043(list_open)', 'SEF1044(list_close)',
'SEF1045(Xbutton)', 'SEF1046(popup_window)', 'SEF1047(conversation_release)',
'SEF1048(f_key_hover)', 'SEF1049(general_button_hover)',
'SEF1050(tab_hover)', 'SEF1051(small_button_click)',
'SEF1052(small_button_hover)', 'SEF1053(small_button_release)',
'SEF1054(kirana_listup_hover)', 'SEF1055(kirana_listup_click)',
'SEF1057(kirana_listup_release)', 'SEF1058(kirana_listup_release_cancel)',
'SEF1063(f_key_click)', 'SEF1065(battle_book_use)',
'SEF1066(inventory_hover)', 'SEF1067(inventory_click)',
'SEF1068(inventory_block)', 'SEF1069(inventory_release)',
'SEF1070(tab_release)', 'SEF1071(battle_book_hover)',
'SEF1075(battle_book_appearance)', 'SEF1076(battle_book_disappearance)',
'UI_deliveryRangeSphere', 'balloon_marker', 'button_marker',
'caption_marker', 'close_marker', 'coordinates_marker',
'gauge_marker', 'label', 'list_marker', 'list_marker3',
'text_balloon_marker', 'text_edit_marker', 'tooltip_marker',
'window', 'window2')
_App__PRELOAD_PROPPARTS = ('PP6954', 'PP6366(blue)', 'PP6366(green)', 'PP6366(purple)',
'PP6366(red)', 'PP6366(white)', 'PP6366(yellow)',
'PP6851', 'PP8200', 'PP8201', 'PP8202', 'PP8203',
'PP8204', 'PP8205', 'PP8206', 'PP8207', 'PP8208',
'PP8209', 'PP8210', 'PP8211', 'PP8212', 'PP8213',
'PP8214')
_App__DUMMY_AVATAR_MESHSETS = ('AP7250', )
class AccountSession(natuum._entity.IEntitySubscriber, IGatewaySubscriber):
def __init__(self):
self._AccountSession__connected = False
self._AccountSession__gatewaySubscription = None
self._AccountSession__NGSClient = None
self._AccountSession__JYPClient = None
self._AccountSession__accountID = None
self._AccountSession__kickoutMessage = None
self._AccountSession__weakOnConntionQueueChangedCallback = None
self._AccountSession__weakOnAccountAuthenticatedAfterWaitingQueueCallback = None
self.OnDisconnected = natuum.signal.Signal(bool, str)
self.OnBeginTransferMessage = natuum.signal.Signal(bool)
self.OnPrepareTransferMessage = natuum.signal.Signal(str)
self.OnFinishTransferMessage = natuum.signal.Signal(thing.system.uuid, str, bool)
async def connect(self, callback=None, maxTryCount=-1):
if self._AccountSession__connected:
return
await natuum.client.app.Instance.ClusterGuestNode.start()
apartment = asyncio.get_event_loop().Apartment
tryCount = 0
entity = None
while maxTryCount < 0 or maxTryCount > tryCount:
try:
entity = await apartment.EntityManager.findEntity(natuum.client.app.Instance.GatewayEntityID)
break
except natuum._entity.error.RemoteEntityServerNotFound:
LOG_WARNING('RemoteEntityServer not found')
return
except natuum._entity.error.EntityNotExist:
LOG_WARNING('GatewayEntity not exist')
return
except natuum._entity.error.EntityNotRestored:
LOG_WARNING('GatewayEntity not restored. try after 5 seconds. try count : {}'.format(tryCount))
tryCount += 1
await asyncio.sleep(5)
except natuum._entity.error.SubscriptionChannelClosed:
tryCount += 1
await asyncio.sleep(5)
except asyncio.TimeoutError:
return
except asyncio.CancelledError:
return
if entity is None:
return
try:
subscription = await natuum._entity.subscribeEntity(entity, 'hello', None, self)
except natuum._entity.error.SubscribeFailed:
return
else:
self._AccountSession__gatewaySubscription = subscription
self._AccountSession__connected = True
LOG_INFO('Connection to game server established')
async def disconnect(self, forced=True):
if not self._AccountSession__connected:
return
LOG_INFO('Connection to game server closed')
self._AccountSession__connected = False
self._AccountSession__gatewaySubscription = None
self._AccountSession__NGSClient = None
self._AccountSession__JYPClient = None
await natuum.client.app.Instance.ClusterGuestNode.shutdown()
self.OnDisconnected.emit(forced, self.KickOutMessage)
self.KickOutMessage = None
def setAuthenticationWaitingCallback(self, onConntionQueueChanged, onAccountAuthenticatedAfterWaitingQueue):
self._AccountSession__weakOnConntionQueueChangedCallback = weakref.WeakMethod(onConntionQueueChanged) if onConntionQueueChanged is not None else None
self._AccountSession__weakOnAccountAuthenticatedAfterWaitingQueueCallback = weakref.WeakMethod(onAccountAuthenticatedAfterWaitingQueue) if onAccountAuthenticatedAfterWaitingQueue is not None else None
@Property
def Connected(self) -> 'bool':
return self._AccountSession__connected
@Property
def AccountID(self) -> 'str':
return self._AccountSession__accountID
@accountID.setter
def AccountID(self, accountID: 'str'):
self._AccountSession__accountID = accountID
@Property
def KickOutMessage(self) -> 'str':
return self._AccountSession__kickoutMessage
@KickOutMessage.setter
def KickOutMessage(self, msg: 'str'):
self._AccountSession__kickoutMessage = msg
@Property
def GatewaySubscription(self):
return self._AccountSession__gatewaySubscription
def setupNGS(self):
LOG_INFO('NGS:setupNGS')
if self._AccountSession__NGSClient is not None:
return
natuum.client.app.Instance.nxlog.stageLog(510, 'setupNGS')
def _sendMessage(weakSubscription, data):
subscription = weakSubscription()
if subscription is not None:
_data = data.tobytes()
subscription.MessageProxy.onNGSMessageReceived(_data, nowait=True)
def _callSendMessage(loop, weakSelf, data):
loop.call_soon_threadsafe(_sendMessage, weakSelf, data)
callableFactory = thing.Common.CallableFactory()
args = thing.Collection.Array()
args.append(asyncio.get_event_loop())
args.append(weakref.ref(self._AccountSession__gatewaySubscription))
args.append(callableFactory.PlaceHolder[1])
sendNGSMessage = callableFactory.bind(_callSendMessage, '__call__', args.iterate())
self._AccountSession__NGSClient = thing.NGSClient.NGSClient(sendNGSMessage)
if not self._AccountSession__NGSClient.isInitialized():
natuum.client.app.Instance.showWindowsMessageBox('NGS 초기화에 실패하여 프로그램을 종료합니다.')
natuum.client.app.Instance.GameLogic.turnOffGame()
natuum.client.app.Instance.nxlog.stageLog(520, 'setupNGS')
def setupJYP(self, nexonID):
LOG_INFO('NGS:setupJYP')
if self._AccountSession__JYPClient is not None:
return
self._AccountSession__JYPClient = thing.JYP.JYPClient(nexonID)
self._AccountSession__JYPClient.collectThisThread()
def onFrameRenderForJYP(self):
if self._AccountSession__JYPClient is not None:
self._AccountSession__JYPClient.onFrameRender()
def onZoneEnterForJYP(self, zoneID):
if self._AccountSession__JYPClient is not None:
self._AccountSession__JYPClient.onSetZone(zoneID)
@messageRPC.impl(IGatewaySubscriber)
def doAnything(self, *args, **kwds):
pass
@messageRPC.impl(IGatewaySubscriber)
def onBeginTransfer(self, targetZoneName: str):
self.OnBeginTransferMessage.emit(targetZoneName)
@messageRPC.impl(IGatewaySubscriber)
def onPrepareTransfer(self, zoneStyle: str):
self.OnPrepareTransferMessage.emit(zoneStyle)
@messageRPC.impl(IGatewaySubscriber)
async def onFinishTransfer(self, zoneEntityID: 'thing.system.uuid or None', reason: str, isTimeout: bool):
self.OnFinishTransferMessage.emit(zoneEntityID, reason, isTimeout)
@messageRPC.impl(IGatewaySubscriber)
def onNGSMessageReceivedFromServer(self, data):
if self._AccountSession__NGSClient is not None:
self._AccountSession__NGSClient.onReceive(data)
@messageRPC.impl(IGatewaySubscriber)
async def notifyKickOut(self, type, errorCode):
LOG_INFO('서버에 의해 게임을 강제 종료:{},{}.'.format(type, errorCode))
text = '서버에 의해 프로그램이 종료됩니다.\n\n사유 : {}\n에러 코드 :{}'.format(type, errorCode)
self._AccountSession__kickoutMessage = text
@messageRPC.impl(IGatewaySubscriber)
async def notifyServerClock(self, now):
Instance._setServerTime(now)
@Thing.overrides(natuum._entity.IEntitySubscriber)
def onMessage(self, serial: 'int', message: 'var'):
pass
@messageRPC.impl(IGatewaySubscriber)
async def checkAlive(self, channelID=None):
pass
@Thing.overrides(natuum._entity.IEntitySubscriber)
def onChannelStatusChanged(self, status: 'int', detailedStatus: 'str'):
asyncio.ensure_future_and_forget(self.disconnect())
@messageRPC.impl(IGatewaySubscriber)
def onConnectionQueueChanged(self, left: 'int'):
callback = self._AccountSession__weakOnConntionQueueChangedCallback()
if callback is not None:
callback(left)
@messageRPC.impl(IGatewaySubscriber)
def onAccountAuthenticatedAfterWaitingQueue(self, userID: 'str', actors: 'collections.Sequence'):
callback = self._AccountSession__weakOnAccountAuthenticatedAfterWaitingQueueCallback()
if callback is not None:
callback(userID, actors)
_AccountSession__connected = None
class EngineConfig:
def __init__(self, id: 'str', config: 'collisions.Mapping'):
self._EngineConfig__id = id
self._EngineConfig__config = config
@Property
def ID(self):
return self._EngineConfig__id
@Property
def Config(self):
return self._EngineConfig__config
def apply(self):
pass
def onReload(self, newFactory):
self._EngineConfig__config = newFactory._EngineConfig__config
self.apply()
_EngineConfig__id = None
_EngineConfig__config = None
# okay decompiling app.pyc
This is decompiled with something else than uncompyle6 and says : okay decompiling