*EX @Cpcv4JuKJ)OX None addLabel hasRight splitRegionVCore Nexgen108Color Register addButtonSystemtrim GetValue SetValueFormatdivideRegionH splitRegionH SetMaxLength addCheckBox addEditBoxlogAdminActionUWindowConsoleCommand InternalTime stringHashnscLog skipRegionSetTextReplaceMessageNotifyaddContentPanel fixStrLensetRPCI addPanel boolHash setContentshowMsgsignalConfigUpdateEnginedivideRegionVsplitAddTextUBitsUClampVClampPaletteVBitsUSizeVSizeMipZerogetconfigChangedClosebMasked bInvisiblelfillpanelIdentifier addNewLine addComponent MaxColor playerEventprintlngetControllerCreatedSetNumericOnly addListBox setValues AddMessageMutatesetAppend setDisabled getPropertyplayerSelectedaddRightDefinitoncreateWindowRootRegiongameInfoChanged showPopup fixByteRangeget_paAccountTypeCreateControl selectRegion TeamColorfixStr notifyEventdecodeCaptionaddAccountTypeencodegetClientByNumClear addProperty setNexgenHUDsignalGameInfoUpdateColors TeamGamePlus bindCommand addRegionset_paAccountType PreBeginPlay broadcastMsg wrapLength PostRendergetUserAccountTitleclientInitialized saveSettings removePlayerRemovecreatePanelRootRegion SetTextColorgetAccountTypeTitleLocalizedMessageupdatePlayerInfoaddPlayerToList getClient getPanelrenderMessageBoxBotpack GetStringUWindowSmallButtonSortPaintSetTeam FirstPage Initialize NextPageaddHistoryMsgupdatePreviewGetSelectedIndexDrawTypingPromptCriticalEvent CountShown MD5String ReconnectNexgenPanelContainergetCurrentRights GameEndedsetEncryptionParamsaccountSelectedclientCommandaccountTypeSelectedloadAccountTypesgetUserAccountIndexUser daysInMonth sendMessage baseColors!doTournamentModeReadySignalCheck isSpectatorloadMutatorList playerJoined renderAlert GetTeamNamebanPeriodSelectedgetCurrentDate playerLeftplayerNameChangedgetSelectedAccountNum updateBan formatGUID gameStartedhandleMsgCommandplayerTeamChanged SavedMessageaddPropertyLabelsetPlayerTeamremoveMessageColorTag PanelHeightisMutedreconnectText ChangeNameSetFontAddItem selectPanelannouncePlayerAttrChangemoveSelectedPlayerTogetLogTypeTagcalcStaticChecksum addPlayer RemoteRolecalcDynamicChecksumEvent startGamemakeKeygetClientByIDloadGameTypeListcanEditAccount PlayerPawn virtualTimer checkConfigloadUserAccounts BalanceTeamsMutatorTeamMessage!MutatorBroadcastLocalizedMessage PreventDeathMutatorBroadcastMessage ModifyPlayer isLeapYear addChatMsgMutatorTakeDamagegetPlayerColorsetNexgenMessageHUD ModifyLogin checkLoginsplit2loadMatchSettingsgetBanPeriodType addListCombo ScoreKillGetLookAndFeelTextureTeamSaySetSelectedIndexloadBootControlSettings isBlockedNotifyBeforeLevelChangeKeyDownupdateBootControlseparatePlayers sendPasswordtoggleMatchMode banPlayer PasswordTextCorrodedMessage GetPlayer readDateserializeDateisValidClientID daysInYearSpectatorText bAlwaysTick execCommandTimerNexgenControllerNexgenMainFrameupdateChecksumupdateStaticChecksumupdateDynamicChecksums setFlagTexaddEditControlgetWrapPositionresetEncryptionConfigcanUseAccountType getBanIndexselectionHasAccount isExpiredBan removeBanhudReplacementSelectedloadHUDReplacementListUWindowCheckboxreceivePasswordaddUserAccountgetMutatorIndexweaponSelectedloadIgnoredWeaponList setGameInfo ListClass ServerTravel getIDList getIPListreconnectPlayer isFirstGameSetup pauseGamegetCurrentBanPeriodbanPeriodTypeSelected banSelectedmoveAccountType loadBanListloadAccountTypeInfoSayaddDynamicTextAreaGetServerPortFellunblockPlayer fixIntRange canGetSlot addImageBoxdisconnectClientsetupControlPanelremoveClientHandlerclientCreatedEndGame BlockPlayercheckPreventDamage getItemByIDgetBackgroundColorsendMsggetDisplayTextgetPlayerJoinEventArgsgetBanPeriodDescriptiongetDisplayFontBurnedupdateLoginOption showPanelshowGameReadyToLaunchMessageswitchPreferredPlayergetSmallestTeaminitializeConsoleWindowgetLargestTeamgetLocalizedDateStrclearReadySignalsgetBoolgetDaterenderMessagegetPanelWidth renderPanel isValidLevelignoreWeaponFire firePressedgetClientStategetServerState clientLoginrenderTypingPromtgetShortDayNameaddMsgNexgenControllerPawn getMonthNamegetPlayerNameIndicesaddColorizedMessagegetShortMonthNameCopyToClipboard windowHeight windowWidth clearDatagetDelocalizedDateStr firstTicksetAccountInfo hasRightsdoCompatibilityCheck forceEndGameinitializePopupWindowinitPreferredSwitchersDrawCrossHairgameSpeedChangedinitializeControlPanel parseDatecompareSwitchDesirabilitygetCompactDateStrbShowCountryFlagaddControllerupdateCountryCodescheckConfigUpdateplayerRespawnedgetDisplayColorclearLogBufferaddLogBufferEntry shouldLogPlayReceivedMessagegetPlayerByID respawnedmodifyLoginRejectcheckResidentConfigClientInitialize InputNumberaddRaisedButton RestartGameaddPluginClientConfigPanelProcessKeyEvent!initialConfigReplicationCompletemodifyClientState AdminLoginClientPlaySound ChangeHudChangeCrosshairmodifyServerStatesetPlayerDataisReadyToInitialize isBanned hasUniqueKeyRestartPlayergiveJoinOverrideCodeisValidJoinOverrideCode newClient SuicidedregisterPluginsetServerSettingssetReplacementHUDClasssendPM receivePM initGameInfo Spectator NetPriority ShowWindowupdateAccountType addSubPanel SetRange loadKeyBinds DrawItemcontainsCommandremoveKeybindInsert addKeybindupdatePlayerTitlesdeleteAccountTypeComparedeleteAccount FocusWindow applyConfig CloseUWindow loadBanInfo preloadMap SetEditableupdateAccountselectRandomBootMap addAccount doNexgenBootcheckLogSettingstoggleTeamSwitchregisterServersendPlayerToURLLaunchUWindowtoggleGlobalTeamSwitchaddLogPostNetBeginPlayendLogtoggleGlobalTeamBalance PreRendertoggleLockedTeamsupdateMapPrefixDoubleClickItem beginLogflush deleteBan closeFile openFileNexgenLogin setLevelInfosetupTeamButtonsaddBangetEncryptionParamscheckMatchSettingsloadFavouritessetActiveMutatorList getGameIndex checkBanListserverSelected getAllRightscheckAccountSystemSettingsreceiveMessageupdateMatchSettingscheckBootControlSettingstogglePlayerMuteupdateBanPeriodssetPlayerNamecleanExpiredBans kickPlayercheckExtraServerSettingsshowAdminMessagePawntoggleGlobalMute ItemHeightupdatePlayerListinstallVersion108installVersion107installVersion106 updateConfiginstalltoggleGlobalNameChangesetServerSettingsExt1setServerSettingsExt2 parseBoolpostInitialize SetFrame rebootServerdelIgnoredWeaponsaveIgnoredWeapondelHUDReplacementClass getFlagTexcopyTogetLoginParameterscheckLoginParametersparseCommandStrsaveHUDReplacementClassNexgenHUDWrappertoggleGlobalTournamentModeTick visitServersetLogSettings computeDate DestroyedexecSwitchTeamexecJoinAsSpec SetDamageisValidIPAddressDeathMatchPlusexecJoinAsPlayer isValidKeyexecBalanceTeamsexecStartGameexecPauseGamegetMessageColorUMenu autoFormatcheckEncryptiongetLevelFileNamevalidateFileName getDayName urlEncodecheckGlobalServerSettings getNextLinerfillcountryFlagsPkginternalVersion versionCodeversion DecToHexIIHHGGFF ROTATE_LEFTMD5Move MD5Final MD5Update MD5TransformMD5InitMD5_CTXpNum msgLabelTextsendButtonTextcloseButtonText slotMessagereplyButtonText messageText senderText noReasonTextIpDrv HTTPError periodText reasonText loginText UBrowserHTTPReceivedDataBrowse buttonSpace buttonHeightbuttonPanelHeightbuttonPanelBorderSizeeditControlLabelVOffseteditControlHeightautoCloseControlPanelhasCloseButtonrightNotDefined rightDenied rightGranted BeginPlayPostBeginPlaySpawnNotificationGWAVi PackageNameLogEventString ChangeTeamFbAlwaysRelevantbNetTemporary GetItemName GetIntOptionDisplayMessagesClientGameEndedPBT_Transparent PBT_Beveled PBT_Default LayoutRegionitemIDselectedAccountColor accountColor selectColorSetProgressTimeRegisterDamageMutator getFloatgetIntgetByteSetProgressMessageAttributeEntrySShot ActorClass GetLocalURLClearProgressMessages ipToCountrypluginVersion pluginAuthor pluginNameUMenuRaisedButtonTriggerctrlIDaddPluginConfigPanel bIsSpecial ButtonWidthbIsConsoleMessage csnormalcsdeadcsmuted csprotectedcsidlecslogin ssnormalssmatch sspausedssended ssstaringssready sswaiting ssoffline PanelInfo MessageInfo StartMatch blankColor signalEventGetStaticHugeFontGetStaticSmallestFontBot BorderSizeHRegisterHUDMutatorRegisterMessageMutator BeforePaint HideWindowGetDesiredDimensionsGotoTab HideConsoleSetEditTextColorSetSelectedItem noplayrightChallengeCTFHUD serverfull invalidpassbanned duplicateid illegalloginNSCbUseExternalConfig SetHistory GetValue2CreateRootWindowAddPageUWindowEditControl NexgenUtilNexgenTeamBalancerNexgenSimplePlayerListBoxNexgenSimpleListItemNexgenSimpleListBoxNexgenServerFullDialogNexgenScrollPanelContainerNexgenResidentConfigDialogNexgenRCPUserAccountsNexgenRCPServerSettingsNexgenRCPServerInfo NexgenRCPReplacementHUDSettingsNexgenRCPPrivateMsgNexgenRCPModerateNexgenRCPMiscNexgenSettingsNexgenRCPMatchSetNexgenRCPMatchControlNexgenRCPLogSettings BaseColor NexgenRCPIgnoredWeaponsSettingsNexgenRCPHomeNexgenRCPGameInfoNexgenRCPClientConfigNexgenRCPBootControl LabelHeightNexgenRCPBanControlNexgenRCPAccountTypesNexgenRCPAboutNexgenPrivateMsgDialogNexgenPopupFrameNexgenPlayerDataNexgenPlayerListNexgenPlayerACListItemNexgenPlayerListBoxNexgenPlayerACListBoxNexgenPasswordDialogNexgenNullMessageNexgenNoPlayRightDialogNexgenMainPanelBarMD5HashNexgenAccountUpdatedDialogNexgenPopupDialog NexgenActorNexgenAdminLoginDialogNexgenBannedDialog NexgenClientNexgenClientControllerNexgenClientCoreNexgenClientLoginHandlerNexgenCommandHandler NexgenConfigNexgenConfigCheckerNexgenConfigExtNexgenConfigSysNexgenContentPanelNexgenMainPanelNexgenCorePlugin NexgenPluginNexgenCPKeyBind NexgenPanelNexgenDummyComponentNexgenEditBoxNexgenEditControlNexgenGameInfoNexgenHTTPClient NexgenHUDNexgenTextFile NexgenHUDxASNexgenHUDxCTF NexgenHUDxDMNexgenHUDxDOMNexgenHUDxTDMNexgenIdleKickedDialogNexgenIDUsedDialogNexgenIllegalLoginDialogNexgenImageControlNexgenJustBannedDialog NexgenLangNexgenLogEntryNexgenLogFile ParentWindowAlign bCanEdit bDisabled NotifyWindow serverID bStretchGeneralConfig ServerConfig WhiteTexturebBlink MaxLength NexgenCCRootWin TeamButton LeftOverACbBottombBold LabelTextItems GameTypeList Smallest bHideFaces bHideHUD bHideFragsbHideTeamInfo bHideAmmo bHideStatusbHideAllWeapons Static_A00 LadrStaticmyFlag ControllerTGRIMyFontsbPlayersBalanceTeamsWinEndTime bStartMatch bNetReadyserverControllercontrol separatorstaticChecksumdynamicChecksumsdynamicChecksumModifiers updateCountsCT_GlobalServerSettingsCT_AccountTypesCT_UserAccounts CT_BanListCT_BootControlCT_MatchSettingsCT_ExtraServerSettingsCT_ExclWeaponListCT_HUDReplacementListCT_LogSettings bInstalledlastInstalledVersion serverKey isNexgenBootisAdminRebootencryptionKey codeSchemeconfigEncryptionKeyconfigCodeSchemeglobalServerPasswordglobalAdminPassword enableUplink playerSlots vipSlots adminSlotsspectatorSlots MOTDLineautoUpdateBansremoveExpiredBans waitTimeautoReconnectTime maxIdleTimemaxIdleTimeCPspawnProtectionTimeteamKillDamageProtectionTimeteamKillPushProtectionTimebroadcastTeamKillAttemptsallowTeamSwitchallowTeamBalanceallowNameChangeautoDisableMatchTimespawnProtectExcludeWeaponsrestoreScoreOnTeamSwitchenableNexgenStartControlbroadcastAdminActionsautoRegisterServer logEventslogSystemMessageslogChatMessageslogPrivateMessageslogAdminActions logToConsole logToFilelogPathlogFileExtensionlogFileNameFormatlogFileTimeStampFormatenableBootControlrestartOnLastGame bootGameTypebootMapPrefix bootMutatorsbootMutatorIndices bootOptions bootCommandslastServerURLmatchModeActivatedmatchesToPlay currentMatchserverPasswordspectatorsNeedPasswordmuteSpectatorsDuringMatchenableMatchBootControlmatchAutoLockTeamsmatchAutoPausematchAutoSeparatetagsToSeparateuseNexgenMessageHUDreplacementClassHUDReplacementClass atTypeName atRightsatTitle atPassword paPlayerID paPlayerNamepaAccountTypepaCustomRightspaCustomTitle rightsDefgiveRightToRoot bannedName bannedIPs bannedIDs banReason banPeriodmaxBanIPAddressesmaxBanClientIDs BP_Forever BP_Matches BP_UntilDateserverInfoPanelClassgameInfoPanelClassmatchControlPanelClass gameTypeInfo mutatorInfoactiveGameTypeactiveMutatorIndicesIW_Fire IW_AltFireFirstChildWindow configTypeNextSiblingWindowTextY OwnerWindowbWindowVisible bTopCentric WindowTitleWindowIsVisible remainingactiveGameTypeClassgameTypeClass GetParent gameInfoStr DrawUpBevelmutatorInfoStr ClipTextcb bValidCharDrawStretchedTextureDrawClippedTexture CreateWindownewReplacementRuleSetAcceptsFocus BringToFrontSetSize typeNamerightsGetPlayerOwnerrightID bNotForRootUWindowListBoxItem bConfigOkcCheckUWindowListBox playerIP bNameMatch bIPMatch bIDMatchbIgnoreRDoubleClickcurrIPcurrIDipCountidCountbIgnoreMDoubleClick bExpiredbanPeriodTypebanPeriodArgs UWindowBasecurrBan bBanDeleted bAlwaysOnTop entryNumUWindowWindowUWindowButton accountNumUWindowDialogControl accountTypeUWindowRootWindow clientIDUWindowClientWindow accountName customRights customTitleUWindowComboControlbForRootAdminUWindowFramedWindow banPeriodStrargsUWindowListControl currClass UWindowListUWindowControlFrameUWindowDialogClientWindowcsUWindowDynamicTextAreaUWindowPageControlPagecknbUWindowPageControlcilbUWindowPageWindowbRequireReadyUWindowEditBox updateCount bTeamsLockedbNoTeamSwitchbNoTeamBalance gameStatematchAdminCount bMuteAllbNoNameChangerebootCountDownbTournamentMode numReadynumRequiredReady GS_Waiting GS_Ready GS_Starting GS_Playing GS_EndedIT_GlobalRightsIT_GameSettingsbIsOpenUWindowVScrollbar tempFileNameBestRecordDateWindowConsolebBuffer BestFPHslngtimeStampFormatdefaultLogFileExtensiontempLogFileExtension logFilePath logFileName tempLogFilelogFile TextColor logEntrylogLinebIgnoreLDoubleClick BestPlayerslogType serverHost TotalFlags TotalDeaths bSpecialModebBootSeqFailedbServerReloadedbIsNexgenBootbIsAdminRebootsConf clientList cmdHandlergInfplugins teamBalancerplayerDataList loginHandler logBuffer httpClientnextPlayerNumjoinOverrideCodesoverrideCodeLeaseTimesmaxOverrideCodeLeaseTimejoinOverrideCodeOptionbUTPureEnabledlastTimeDilationbServerTravelDetectedvirtualTimerCounterbFirstTickPassedlastPlayerLeftTimegameStartTime gameEndTimelogTag TotalFrags timerFreqserverPauserNamemaxBootAttempts CMD_PrefixCMD_SwitchTeamCMD_BalanceTeams CMD_Play CMD_SpectateCMD_StartGame CMD_ExitCMD_Disconnect CMD_Open CMD_OpenVote CMD_PauserebootCommandsuicideDamageTypefallDamageTypeburnDamageTypecorrodeDamageType LT_System LT_Event LT_MessageLT_Say LT_TeamSayLT_PrivateMsgLT_AdminActionRT_IllegalLoginParameters TotalGamesRT_DuplicateID NumTeams RT_BannedChallengeDominationHUDRT_InvalidPasswordLocalMessagePlusRT_ServerFull FontInfoRT_NoPlayRightTournamentGameReplicationInfosecondsPerMinutewildcardTokenbUseTeamColor bTournament randomMapbootURL attempts bMapLoaded CountDown shortMapNamebFirst mapCountrandomMapIndexOpacitylvlSummaryObjectStr HUDScale StatusScale WeaponScale hudClassNamehudReplaceClassNamesrcHudClassName tokenIndexUWindowLookAndFeelpluginkclientHandler overrideCodeC_WhitebValid bInvalidC_Black Receiver bRejectedallowSpecReconnect rejectTypepopupWindowClass popupArgsFrame currClientPadding banIndexbBannedNextMessageMutatorhasVIPSlotAccesshasAdminSlotAccess vipCount adminCountvipAdminCountspecialSlotsUsedFavoriteHUDColorpDatNextHUDMutatorCrosshairColor ZoneNameWidthClipX EditBoxWidth bAllowChangebWasForcedChangedLine bAllowSwitchbPreventDamage bResetPlayerAction StatLogFinal bAltFirebFire AssaultHUDMsgTypenewLogHistory TypedStrstr1str2str3str4 bNoBroadcastbSendToAdminsOnlyAmount Checksum SelectedItem PlayerZone TalkTextureSender bIsCommand bCheckedValue2 PlayerIDGameEndedCommentsbSend senderPRIbIsSpecMessagebIsNexgenCommandbIsLegacyCommand MOTDLine3 AdminName infoTypeRemainingTimeattributeNameNewItemPZoneChallengeTeamHUD argumentsPName instigatedByCriticalStringGFXgrad32grad64 playerIcon playerIcon2 serverIcon serverIcon2 offlineIcon offlineIcon2 waitIcon waitIcon2 specIcon specIcon2 shieldIcon shieldIcon2 idleIcon idleIcon2 mutedIcon mutedIcon2 matchIcon matchIcon2lastSetupTime baseFont ChallengeHUD baseHUDColorStartYbaseFontHeight MessagescolEditBoxtextCol solidIcon chatMessages chatMsgCount msgCountfaceImg msgBoxWidthmsgBoxLineHeight msgBoxHeightminPanelWidthbFlashMessages lastResX lastResY lastTeamlastLevelTimeSecondsbShowPlayerLocation iconSizechatMessageLifeTimemessageLifeTimemessageBlinkTimemessageBlinkRatepanelBlinkRateblinkColorDampconnectionAlertDelay secPerMinutematchInfoSwitchTime hDefBarSizealertAnimTimealertAnimDistalertBorderSize newLineTokenC_REDC_BLUEC_GREEN C_YELLOWC_PINKC_CYANC_METAL C_ORANGE SS_Offline bCanDrag SS_WaitingButton SS_ReadybAcceptsFocus SS_Starting PlayerNum SS_Ended WinHeight SS_Paused MutatorName SS_Match FirstMap SS_NormalRestartButton CS_Login MutatorListCS_Idle StartButton CS_ProtectedPages CS_Muted bInitializedCS_Dead WeaponClass CS_Normal LabelWidthbadConnectionTimetimeRemainingStartX LocationName HUDMutatorcxcy animIndex hBarSize textWidth maxTextWidth textHeight lineCountpri1pri2 playerColor messageColorbIsSpecSayMsgspecPRITeamNumfirstPlayerNamesecondPlayerName firstIndex secondIndexfirstPlayerColorsecondPlayerColor msgPart1 msgPart2 msgPart3 msgColor tempIndextempPlayerColorSourceindex1index2tmpPRI nameIndex tmpIndex PlayerOwnercol1text1col2text2col3text3col4text4col5StrKeyNum bUpdateBaseClipY serverState clientStateYPos msgOffsetXPos msgIndex msgWidth msgHeight blinkFactorblinkIntensity bPollingpInf stateInfo stateTypeConsoleWindow protectTimebComplexStringseparatorWidthVictim iconHeightNextDamageMutator colorNum timer_tick nextClientgcscloginHandlerChecksum bNetLogin bNetWaitloginComplete ipAddress bHasAccount loginOptionsloginTimerFreq waitTimeOutcountry bSpectator bFirePressedbadConnectionSincebBadConnectionDetectedbMutedteamSwitchOverrideTimenameChangeOverrideTimelastRespawnTimelastSwitchTimespawnProtectionTimeXtkDmgProtectionTimeXtkDmgProtectionTimetkPushProtectionTimeXtkPushProtectionTimebScreenShotTakenbReconnecting originalHUDnewHUDbEncryptionParamsSet idleTime idleTimeCPidleTimeRemainingbUsingNexgenMessageHUDscoreBeforeTeamSwitchbIsReadyToPlay clientCtrlnscHUD popupWindownextDynamicUpdateCountnextDynamicChecksumbWaitingForNewConfiggameInfoUpdateSSTR_ClientKeySSTR_ClientIDSSTR_ServerPasswordSSTR_OverrideClassSSTR_UseNexgenHUDSSTR_FlashMessagesSSTR_ShowPlayerLocationSSTR_PlayPMSoundSSTR_AutoSSNormalGameSSTR_AutoSSMatchSSTR_RunCount R_MayPlay R_VIPAccessR_AdminAccess R_NeedsNoPW R_CanBeIdle R_MatchAdmin R_ModerateR_BanOperatorR_AccountManagerR_ServerAdmin R_MatchSetR_BanAccountsR_HiddenAdminRCN_ReconnectOnlyRCN_ReconnectAsPlayerRCN_ReconnectAsSpecreconnectCommanddisconnectCommand exitCommand startCommandspectatorClassPE_PlayerJoinedPE_PlayerLeftPE_AttributeChanged PA_ClientID PA_IPAddressPA_Name PA_TitlePA_Team PA_CountrymaxIdleRadiusidleCountDelayidleTimeWarningmaxOverrideTimecancelSpawnProtectDelaynormalModeTimerFreqautoScreenShotDelayautoSSMinGameTimewelcomeMsgCountmaxClientCtrlInitWaitTimebClientControllersReadylastIdleTimeRemainingTextbIgnore bIgnoreFirebIgnoreAltFirecurrWeaponClassexcludedModesPortal Palette1 runCountValuebEnableKey cleanMsg NextMutator BaseMutator Palette3 rightsStr bHasRights MapPrefixAdminPasswordwWidthwHeightwTopwLeftpos bGameEnded popupClassdistNextSwitchCountdown optionNameNextURL PawnListcontrollerClasscreatorEngineVersion controllerIDNetMode Screenshot updateNumSummaryPauserIdealPlayerCountSecondMinuteUMenuFramedWindow panelClass containerHour DayOfWeekDayUMenuLabelControlbCanModifyHUDStatePanelMonthYear TimeSecondsOptionalObjectblockedPlayers bBlockAllopenURLCommand restartURL rebootDelay RelatedPRI_1 bBlockedbBeepOwnershortServerNameMOTD1MOTD2MOTD3MOTD4 specSlotsbEnableUplink Password bWindowed targetCtrl senderTitle nextPawn senderIDbForcedpmPanel FilenameaccountTypeNumReason newTitle TimerRateuserAccountsChangedremovedAccountTypemyHUD oldPosition newPositionmovedAccountTypeTargetremovedAccountremovedAccountTitle InstigatorHUDTypebAlreadyHasAccount accountTitleBasebGameIsPausedRegionGameNextbSpecPRIfURLContextUMenuMapListFrameCWPlayerLocationbValidPassword bRootAdminbFound deletedBanbWaitingPlayeripListidList bIsSpectator bLoadLastMap mutators extraOptions indexStrHasFlag teamTagsScore targetPlayer pwToSendpwReceivedMsgOldName PlayerName teamTag0 teamTag1 teamTag2 teamTag3 PRIArray MOTDLine4 MOTDLine2 MOTDLine1 AdminEmailbanPeriodDescbHasExistingBanEntry NumPlayersCommandbBadConnectionAlert bTeamGame GameClassSecondsItem currIndexMignorePrimaryFireignoreAltFireweaponConfigStrL OldLocationoriginalClassreplacementConfigStr bSelectedaPawnopenMapVoteCommand MutateStringbInvalidCommand failMessagebTyping bAllowedDamage numSpecs HitLocationIndex Momentum actualDamageN SpawnClassKillerP RelatedPRI_2MsgURL bNetOwner NewValuemaxLenHitLocintVar lowerBound upperBound EventTypebyteVar CHSpectator WinWidthTransA3OldTeam bShowScoresOptionWinTop PA_MutedPA_NoTeamSwitchResult bCancelledipInfoDelta oldClientdateFormatStrdefaultLogFileNameFormatdefaultLogFileTimeStampFormatlongDateTimeFormatstartingControllerMsgnoDedicatedServerMsgcompatibilityModeMsgautoInstallMsgautoUpdateMsginvalidConfigMsgattrServerIDMsgnexgenBootMsgnexgenMapLoadFailMsgnexgenBootFailMsgexecCommandMsgbootLevelSwitchMsgnexgenActiveMsglogFileCreateFailedlogFileCreatedloadingPluginMsginitFailedMsg regFailedMsgnoHUDReplacementClassMsgloginRequestMsgattrClientIPMsgattrClientIDMsgattrPasswordMsgillegalLoginParametersMsgduplicateIDMsg bannedMsginvalidPassMsgserverCapacityMsgnoPlayRightMsgloginAcceptedMsgloginRejectedMsg logFileTitlelogFileEngineVersionlogFileNexgenVersionlogFileServerIDlogFileServerNamelogFileServerPortlogFileGameClasslogFileLevelNamelogFileLevelTitle logFileStart logFileClosegameStartedMsg gameEndedMsgplayerJoinMsgplayerLeaveMsgplayerNameChangeMsgplayerLeaveLogMsgteamSwitchFailMsgteamsLockedMsgnameChangeFailMsgnameChangeDisabledinvalidCommandMsgcommandFailedMsginternalErrorMsgteamSwitchFailedMsgspecTeamSwitchMsgteamSwitchDisabledMsgplayerTeamSwitchDisabledMsginvalidTeamMsg sameTeamMsgnoSpecsAllowedMsgnoMoreSpecSlotsMsgnoPlayingRightsMsgnoMorePlayerSlotsMsgteamBalanceFailedMsgnotATeamGameMsgteamBalanceDisabledMsgteamsAlreadyBalancedMsg balanceMsg launchMsgplayerReadyMsg forcedEndMsgrootAdminTitle specTitledeathPreventedMsgmutedReminderMsgaccountTypeNameStrteamKillAttemptMsghttpClientErrorMsgcontrollerSystemLogTag eventLogTagmessageLogTagchatMessageLogTagteamSayMessageLogTagprivateMessageLogTagadminActionLogTagallowedToPlayRightDescvipSlotAccessRightDescadminSlotAccessRightDescneedsNoPWRightDesccanBeIdleRightDescmatchAdminRightDescmatchSetRightDescmoderatorRightDescbanOpRightDescaccountMngrRightDescserverAdminRightDesccanBanAccountsRightDeschiddenAdminRightDesclaunchGameMsgadminLaunchGameMsgtournamentLaunchGameMsgserverCrashedClientMsgbootFailedClientMsgserverAdminRebootClientMsg welcomeMsgsettingsSavedMsgreceivedPMMsgreceivedPWMsgpasswordSendMsgnoBanAccountRightMsgadminTeamSwitchMsgadminPauseGameMsgadminResumeGameMsgadminRestartGameMsgadminStopGameMsg adminPlayerTeamSwitchDisableMsgadminPlayerTeamSwitchEnableMsgadminReconnectAsPlayerMsgadminReconnectAsSpecMsgadminSendToURLMsgadminDisableTeamSwitchMsgadminEnableTeamSwitchMsgadminDisableTeamBalanceMsgadminEnableTeamBalanceMsgadminLockTeamsMsgadminUnlockTeamsMsgadminAddBanMsgadminDeleteBanMsgadminEnableMatchModeMsgadminDisableMatchModeMsgadminMutePlayerMsgadminUnmutePlayerMsgadminSetNameMsgadminKickPlayerMsgadminBanPlayerMsgadminMuteAllMsgadminUnmuteAllMsgadminEnableNameChangeMsgadminDisableNameChangeMsgadminRebootServerMsgadminEnableTournamentModeMsgadminDisableTournamentModeMsgadminAddAccountTypeadminUpdateAccountTypeadminRemoveAccountTypeadminMoveAccountTypeadminRemoveAccountadminUpdateAccountadminAddAccountadminUpdateBanMsgadminUpdateBootControladminSeparateByTagadminUpdateMatchSettingsadminUpdateIgnoredWeaponListadminUpdateHUDReplacementList adminUpdateGlobalServerSettingsadminUpdateMiscNexgenSettingsadminUpdateLogServerSettingsinvalidPasswordMsgadminLoginMsgautoReconnectAlertreconnectingAlert rebootAlert idleAlert waitingStatewaitingStateUnknownTime readyStatereadySignalWaitStatestartingState onlineState offlineStateofflineStateRCN endedState pausedState loginState idleState mutedStateprotectedState deadState matchState clientTabTxt homeTabTxtsettingsTabTxtprivateMessageTabTxt gameTabTxtplayersTabTxtmoderatorTabTxtmatchControlTabTxtmatchSetupTabTxt serverTabTxt infoTabTxtbanControlTabTxtaccountsTabTxtaccountTypesTabTxtbasicSettingsTabTxtnexgenSettingsTabTxt bootTabTxtpluginsTabTxt aboutTabTxt welcomeTxtrightsOverviewTxtrightNotDefinedTxtteamBalanceTxt redTeamTxt blueTeamTxt greenTeamTxt goldTeamTxtplayTxt spectateTxt reconnectTxtdisconnectTxtexitTxt mapVoteTxt startTxt loginTxtserverNameTxtshortServerNameTxt MOTDLineTxt adminNameTxtadminEmailTxtserverPasswordTxtadminPasswordTxtplayerSlotsTxt vipSlotsTxtadminSlotsTxt specSlotsTxt advertiseTxt resetTxtsaveTxt keyBindsTxtbalanceBindTxtswitchRedBindTxtswitchBlueBindTxtswitchGreenBindTxtswitchGoldBindTxtsuicideBindTxtopenMapVoteBindTxtopenCPBindTxtpauseGameBindTxtUISettingsTxtenableMsgHUDTxtmsgFlashEffectTxtshowPlayerLocationTxt pmSoundTxtmiscSettingsTxtautoSSNormalGameTxtautoSSMatchTxtplayerListTxtblockedListTxtblockToggleTxtblockAllPMsTxt messageTxtsendNormalPMTxtsendWindowedPMTxt historyTxt blockMsg unblockMsg sendMsgTxtreceivedMsgTxtaccountNameTxtaccountTitleTxt passwordTxtaddAccountTypeTxtdelAccountTypeTxt moveUpTxt moveDownTxt userNameTxtaccountTypeTxt userTitleTxt onlineTxt offlineTxt updateTxtaddTxt deleteTxtcustomAccountTxtswitchToTeamTxt sendToURLTxtreconnectAsPlayerTxtreconnectAsSpecTxtdisableTeamSwitchTxt pauseGameTxt endGameTxtrestartGameTxtallowTeamSwitchTxtallowTeamBalanceTxt lockTeamsTxttournamentModeTxtplayerNameTxt banReasonTxt banPeriodTxtbanForeverTxtbanMatchesTxt banDaysTxtbanUntilDateTxtipAddressesTxt clientIDsTxt addBanTxt updateBanTxt delBanTxt removeTxtenableBootCtrlTxtrestartOnLastGameTxtinclMutatorsTxtexclMutatorsTxtbootCmdLineTxt gameTypeTxt mapPrefixTxtextraCmdLineOptTxtpreSwitchCommandsTxt rebootTxtadministratorTxtcontactAddrTxtmsgOfTheDayTxt serverIDTxtstatisticsTxttotalGamesTxttotalFragsTxttotalDeathsTxttotalFlagsTxtbestPlayersTxtFPHTxt recordSetTxt timeLimitTxtscoreLimitTxtteamScoreLimitTxt gameSpeedTxtteamSwitchEnabledTxtteamBalanceEnabledTxtteamsLockedTxtnameChangeAllowedTxt mutatorsTxt levelTxtfileTxt titleTxt authorTxtidealPlayerCountTxtmatchSettingsTxtmatchNumOfGamesTxtmatchCurrGameNumTxtmatchSpecNoPassTxtmatchMuteSpecsTxtmatchBootControlTxtmatchAutoLockTeamsTxtmatchAutoPauseTxtmatchSeparateByTagTxtmatchAutoTagSeparateTxtmatchDoSeparateTxtstartMatchTxt stopMatchTxtsendPasswordTxtallPlayersTxt clientIDTxt ipAddressTxtmuteToggleTxtsetPlayerNameTxtkickPlayerTxt banPlayerTxt muteAllTxtallowNameChangeTxtshowAdminMessageTxtcopyTxtnexgenMiscSettingsPanelTitleautoUpdateBansTxtautoDelExpiredBansTxtannounceTeamKillsTxtrestoreScoreOnTeamSwitchTxtenableNexgenStartControlTxtbroadcastAdminActionsTxtenableNexgenMessageHUDTxtdefaultAllowTeamSwitchTxtdefaultAllowTeamBalanceTxtdefaultAllowNameChangeTxtautoRegisterServerTxtgameWaitTimeTxtgameStartDelayTxtautoReconnectTimeTxtmaxIdleTimeTxtmaxIdleTimeCPTxtspawnProtectTimeTxtteamKillDmgProtectTxtteamKillPushProtectTxtautoDisableMatchTimeTxtignoredWeaponsTxtweaponClassTxtignorePrimaryFireTxtignoreAltFireTxthudReplaceClassesTxtoriginalHUDClassTxtreplacementHUDClassTxtaddNewItemTxtlogSettingsPanelTitlelogToConsoleTxt logToFileTxt logEventsTxtlogFilePathTxtlogFileExtensionTxtlogFileNameFormatTxtlogTimeStampFormatTxtlogMessagesTxtlogChatMessagesTxtlogPrivateMessagesTxtlogAdminActionsTxt TimeStampOther DeltaTimedateStr bSaveDefaultMapName PanelWidth strValueNewName PlayerCount PlayerListTempPlayerName nextLogEntryMinutes attributesGetPlayerNetworkAddressGRINewTeamGoalTeamScore TimeLimit maxTeamCount teamSizeminPlayersPerTeam targetTeampreferredSwitchersprefSwitchTeamOffsetsswitchedCountbHighDetailModelargestMaxSpectators FragLimit nextOffset tempClientbSortedbStopCountDown preferredPickupsclient1client2 TimeDilation lastChecksavedMessagessavedMsgCountbHandleByParentmsgStrcanvcons GameSpeed bWasTyping noCountry Palette7 LeftMessageoffsetXflagTexbackgroundColorEnteredMessage MaxPlayers MutatorClass Lifetime ServerName ShortNameIcon FileFlush Palette11 Palette39 CloseLog Palette19OpenLog mainPaneldialog dialogClass Palette23regions regionCount currRegionEPanelBackType Palette27Dec Palette31 panelBGType parentCPminRegionSize AL_CenterAL_LeftAL_Top AL_Right AL_BottomdefaultComponentDistdefaultButtonHeightdefaultLabelHeightdefaultEditBoxHeightdefaultCheckBoxHeightdefaultRaisedButtonHeightdefaultlistComboHeight SmallFontSetPosHealthAdmin Location bIsPlayer bPercent splitPointregion1heightregion2height region1top region2top LocalMessage region1width region2width region1left region2leftGetNextIntDesctotalRegionHeight regionHeight carryOver currHeightcurrTop Palette35totalRegionWidth regionWidth currWidth currLeftTypeStyle GameNameTeam DrawColor checkBox Palette43 raisedButton Palette45 imageBoxSizebgType contentPanelbQuickKeyEnable listCombo MaxTeams DamageType listBoxClasslistBoxbLeaveOnscreenPanel bShowConsole playerItemtempA bindButtonD selectedBindbindSeparatorgetKeyNameCommandgetKeyBindCommandsetKeyBindCommandX keyActionC commands PropValue actionStr PropNameZ panelNameY identifier newPanel parentPanel subPanelsW barHeight scrollBarpanels numPanelsclientAreaDesiredHeightnextPanelOffsetpanelDistanceborderDistancedefaultPanelHeightbNeedScrollBardesiredPanelHeightlogonumAccountTypesrpciaccountTypeListaddAccountTypeButtondeleteAccountTypeButton moveUpButtonmoveDownButtonaccountNameInpaccountTitleInpaccountPasswordInprightEnableInp resetButton saveButton rightDefLabelRootaccountRightsKeyNamejWrapPosbanList addBanButtonupdateBanButtondeleteBanButtonplayerNameInp banReasonInp banPeriodInpmatchCountInpdateInp addIPButton delIPButton addIDButton delIDButton ipAddressInp clientIDInp ScriptTextnumBansSBbanArgsR Selected ReturnValueLenselectedIndexinclMutatorListexclMutatorList rebootButton mapPrefixInpextraOptionsInp commandsInpenableBootControlInprestartOnLastGameInp previewBoxDynamicLoadObjectoldItem mutatorIndex TotalPlayersbootCmd GameTypeenableNexgenHUDInpuseMsgFlashEffectInpshowPlayerLocationInpplayPMSoundInpautoSSNormalGameInpautoSSMatchInptimeLimitLabelfragLimitLabelteamScoreLimitLabelgameSpeedLabelenableTeamSwitchInpenableTeamBalanceInpteamsLockedInpallowNameChangeInp fileLabel titleLabel authorLabel playersLabelFavoriteCountMutator levelFileteamBalanceButtonplaySpecButtonreconnectButtondisconnectButton exitButtonmapVoteButton loginButtonserverTitleLabel MainWindow CloseButton PageControl rightPanelWeaponignoredWeaponListweapSaveButtonweapRemButtonignorePrimaryFireInpignoreAltFireInpweaponClassInp LevelInfo GameInfologToConsoleInp logEventsInplogMessagesInplogChatMessagesInplogPrivateMessagesInplogAdminActionsInp logToFileInplogFilePathInplogFileExtensionInplogFileNameFormatInplogTimeStampFormatInp ZoneInfoHUD teamButtons pauseButton endButtonsendToURLButtonreconnectAsPlayerButtonreconnectAsSpecButtondisableTeamSwitchButtonurlInpallowTeamSwitchInpallowTeamBalanceInp lockTeamsInptournamentModeInpfavouritesListdefaultButtonColorPlayerReplicationInfoStatLog favsCountfavStr serverIP StatLogFilestartStopButtontagInp numGamesInp currGameInp passwordInpspecNeedNoPWInp muteSpecsInp autoLockInp autoPauseInpautoSeperateInpseparateButtonsendPasswordButtonInfoautoUpdateBansInpautoDelExpiredBansInpbroadcastAdminActionsInpannounceTeamKillsInpenableNexgenMessageHUDInpenableNexgenStartControlInprestoreScoreOnTeamSwitchInpdefaultAllowTeamSwitchInpdefaultAllowTeamBalanceInpdefaultAllowNameChangeInpautoRegisterServerInpgameWaitTimeInpgameStartDelayInpautoReconnectTimeInpmaxIdleTimeInpmaxIdleTimeCPInpspawnProtectTimeInpteamKillDmgProtectInpteamKillPushProtectInpautoDisableMatchTimeInpipAddressLabelclientIDLabelcopyIPAddressButtoncopyClientIDButtonmuteToggleButtonsetNameButton kickButton banButtonshowMsgButtonnumMatchesInp numDaysInp messageInpbanForeverInpbanMatchesInp banDaysInp muteAllInpReplicationInfoblockedPlayerListblockToggleButtonsendNormalButtonsendWindowedButtonmsgInp blockAllInpGameReplicationInfo LevelSummaryselectedPlayer locationidhudReplacementListhudReplSaveButtonhudReplRemButtonoriginalHUDClassInpreplacementHUDClassInp SpawnNotify ViewportCanvasserverNameInpshortServerNameInpMOTDInp adminNameInpadminEmailInpserverPasswordInpadminPasswordInpplayerSlotsInp vipSlotsInpadminSlotsInp specSlotsInp doUplinkInp accountList updateButton deleteButtoninvalidAccountTypecustomAccountTypedefaultAccountTypeFont accountItem ServerActorsLevelActorbItemSelectedBitmap rightStrbClientIsServerAdminPlayerClientbDoneWindowminWrapRetain wrapChars bSuccessnextButtonPosPart FavoritesCode ServerPortUBrowserHTTPClientUBrowserFavoritesFactcw newButtonList fontTypewrapLentextStrlineStr newLinePoswrapStrCount maxCharsHeightAuthor DescriptionOptionspasswordInputImage reasonLabel periodLabel DefaultValueData NewValue2periodspectatorButton allowSpecs msgLabel senderLabel replyButton LookAndFeel bByParentEditBoxTextColorfirstLineWrapLenlineNum lineEntry slotLabel WndClass sendButtonPageVAlignHAlign borderDistseparatorDistcomponentDisteditCtrlHeight sayCommandbDownpTitle pIPAddress pClientID pCountrypTeam bFlagTexSet specTeamSoundTextureTitleConsolepTitleApTitleBpNameApNameB displayTextFontsbufferCmddigestHexbRightbMovingBufblock ClientHeight inputLenpartlentmpbuf ClientWidthbitspadLenstrbits ClientAreasrc srcindex bufindex EditControlTailHead TextAreabUnique TextAlignRole clientKeyStaticbLoginParametersValidbSelectOnFocus bAllSelected NotifyOwner CaretOffset keyChars keyFormatnexgenCommand assignment escapeTokenillegalFileNameCharsClassoldStrnewStr subStrIndexstrLeft formattedStrParentPackage minLengthfillStrOutputInputcmdStrcmdName argCountcclc recStartrecEndbRecStrConstpropStrformattedValueSwitchpropDefaultValuesaved StartTime daysToCountdaysRemaining TextBufferObjectEnum FunctioncurrentSectionsectioncharStateVector formatIndex keyIndexStructName UnrealShare StrPropertyStructProperty rawGUIDStrClassProperty NamePropertyhashObjectPropertyFloatPropertylvl BoolProperty IntProperty bInvalidMap BytePropertynsbMaxOutputLengthReachedCh indexCRLFindexCRindexLFtext5QGx>l U3::$::$w9U[T>{:>&{V*s3T>*s3*s3{V;B*s3*s3{V;B.e> Y 3B> Y 3B}"#}"#.e> Y{#U> Y}"#{.e*s3T>*s3T>{{{*s3T>T>T>T>T>> Y> Y> Y> Y> Y*s*s*s/T2/T2*s3Z6Z6Z6Z6/T2/T2/T2/T2o .'l.'l.'l*s*s/T2o /T2o /T2o *s*s3T>Z6Z6/T2o /T2o /T2o Z6Z6Z6Z6Z6> YR@G/T2/T2/T2/T2 3B.e{V.e{V 3B> YR@G> YR@GI&/> Y> Y5I> Y5I.e> Y5I5I5Ia ,O> Y5I> YMT>MMM{V> Y{#U> Y> Y.eo ${{> Y> Y> Y> YX' 3B 3B> Y> Y> Y> Y> Y> Y{{> Y> Y*s> Y.e> Y{#U> Y{{> Y 3B{T>{{> YMM> Y.e> Y{#U.e& es}$@{Fz8q 7 ::$nYQZ=$:e 3B 3B 3B 3B 3B 3B 3Bq3A"q3A".e{V.e{V 3B 3B 3B 3B'n 6'n 6'n 6'n 6'n 6'n 6'n 6'n 6 3BUު.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V.e{V 3B 3BԝX& jFm7hJ .e=$:e{V9999999{{999H,{V{VH,=$:e'n 6{Vttttt'n 6-%nԝX 3B99999{99{9999H, 3Bv v H,=$:eԝXttttEl{#UH,=$:eԝXtԝX{#U{#U{#U9{{99999{Vttt{#UH,=$:eԝX99{Vttt{#UH,=$:eH,b{V{V{{{H,{V{{{H,{9{V{999{V9{V{{{{{{{99{{{{{{{H,999˔> Y{#U99˔> Y{#U9ˋ{{{{{99˔> Y9ˋ 3BH,999999ˉ 3B9ˉ 3B 3B999ˉ 3B 3B999999EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1{V{V{V{V{V{V{Vb{{{{{{{-%n-%n{{{{{}99˔> Y{#U99˔> Y{#U9˔> Y9999˔> Y{#U9˔> Y9˔> Y99{VH,=$:e99{V'n 6ttt{#Ut{#U99{Vt{#Ut{#UtH,=$:e999˔> Y{#U99˔> Y{#U9999{{V{{V9{V99˔> Y{#U9H,99{V999{{V999˔> Y{#UH,9˔> Y{#U{V99˔> Y99{V9S^S99{H,{V99{V{V9{{{9ˋ{{{{9H,{{9{V9{V9{V{{99ˋ}"#99˔> Y{#U999˔> Y{#U99{{9999{H,{V999˔> Y{{{-%n{{{{9H,H,=$:e999999˔> Y9S^S9S^SS^S9S^S99999999999999999{V999ˉ 3B99{V{V9{V{V9{V{V 3B9{V{Vo ${V{V{V999{V9{V999{VH,H,9{V{V99{V9ˋ{H,=$:e99999999ˉ 3BH,{V{V{{{{{}"#{{{{{-%n'n 6'n 6'n 6'n 6'n 6'n 6'n 6'n 6 3B 3B{V{V 3B{V{V{V{V{V{V{V 3B{V{V 3B{V{V{V{V{V 3B-%n-%nW]8&iy ee*pDG;L ]{VU[ډ 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B& kFj*u 4L4L4Ll5ezZԌzZl5e_4_4_44L4L4LL ¦L ¦|.Xڌ|.Xڌ|.X[[[[᱘᱘᱘7XÐ7X ?Bv@aG  3BxWUު.e.e.e.e.e.e.e.e{V 3B 3B y$q=?x"lw"[h] Nexgen108v]CountryFlags2D!  PAXBB  EyEy&. EyE EyE v"GFf8~ 3( ::${YQZ& %[A`-C  yJ~ EyE EyE EyE EyExxxxxxxxxxo  EyE C$PA JCJB^ ^y&.^^^^^^^^^^^^^^^^^^ 3B F" TIG|6n ::$::$mOLST>99ˉ 3B.e{V.e.e.e.e.e.e.e.e.e.e.e.e.e.e9.e{V.e{V.e99˔> Y99.e{.e{-%n.e{.e{.e.e{.e{.e{.e.e.e-%n.e{.e.e{.e{.e{V.e{V99.e 3B 3B.e.e.e.e 3B 3B.e.e.e.e{V99.e.e.e.e.e.e.e.e.e.e{V99.e 3B.e.e.e 3B.e.e.e.e.e.e.e{V99.e.e.e.e.e.e.e.e.e.e{V99.e{V.e{99.e.e.e.e.e.e.e.e.e.e.e.e9.e{V.e{V99.e.e.e.e.e.e.e.e.e.e.e.e.e.e99.e{.e{.e.e{.e{.e{V.e{V99.e{.e{.e.e{.e{.e{V.e{V99ˋtt> Y> Y> Yt9999.e9999.e{V.e.e 3B 3B.e{V99˔> Y.e{V9.e99.e.e.e9.e99.e9.e9.e.e.e.e.e99.e9999.e{V99˔> Y.e{V9ˉ 3B99.e99.e{V99.e999.e{V9.e{V999.e.e.e.e.e{.e{.e{.e.e{.e.e.e{.e.e.e.e{V.e{V99.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e9.e{V.e{V9M99M999M99{V9{V99.e.e.e9OLS.e99OLS99.e{V99ˋ.e99.e}"#9.e9˔> Y{#U99.e{V99.e.e9 3B9.e.e.e.e.e 3B9 3B9.e.e.e.e.e.e.e.e.e9.e{V.e{V99ˉ 3B.e.e.e.e.e.e.e.e.e.e.e{V99ˉ 3B.e.e.e.e.e.e.e.e.e.e.e{V99.e.e.e.e.e.e.e.e{V.e.e.e.e.e.e.e.e99.e.e{V.e99.e.e{V9.e9˔> Y99˔> Y.e.e.e9.e{V99.e{.e{.e.e{.e{.e{V.e{V99.e{.e{.e.e{.e{.e{V.e{V99.e{.e{.e.e{.e{.e{V.e{V 3B.e999.e{V9.e99999.e99.e{V9.e{V9.e99999.e{V9.e{V9.e99ˋ'n 6}"#99.e{V9.e{V99ˋ.e{V99.e{.e{.e.e{V99.e{.e{9.e{V.e{V99ˉ 3B.e.e 3B.e.e.e.e99˔> Y.e.e.e.e.e.e.e.e.e.e{V99.e.e 3B.e.e 3B.e.e99˔> Y.e 3B.e.e.e.e.e.e.e.e.e{V99.e.e.e.e99˔> Y.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e{V99.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e{V99.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e{V.e9.e99.e{V.e.e99.e9999.e 3B.e.e 3B.e.e.e.e.e.e{V99.e.e.e.e.e{V.e{V.e 3B.e.e 3B.e.e.e.e.e.e{V9˵M99˔> Y9*s3⥉i'ri'r9ˏ{#U99{V9{V{#U999.e.e.e.e{.e{999999999OLS99˔> Y99.e99.e99.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e'n 6'n 6'n 6'n 6'n 6'n 6'n 6'n 6.e.e9.e{V.e{V9*s39{V9*s39{V99*s39{V9*s39{V99*s39{V9999*s39{V9999*s39{V9999*s39{V9*s39{V99*s39{V9999*s39{V999*s39{V999*s39{V9*s39{V9*s39{V9*s39{V9*s39*s39*s39*s39*s39{V9*s39{V& K = ClientCore{!vCH1z 34L'; 롉 3B9^ 3B9^ 3B9^^ 3B9^'; ^^ 3B9^ 3B9^ 3B9^ 3B9^ 3B9^ 3B9 HFc-} [[tT9 CU K/U K/U K/Ԍ o@@f2 WjHGCG-` '; 롙'; ^y&.'; ^^^^^^^^^^K4^^^^o ~xx^xx Q *y*ȖR*RRRȖRdddRC$PATCNB_ )1ז^)1ז)1ז)1ז)1ז `D{-i2*s0 3Bl B$ DA$C@ @\+C@`w*j @zAv ;^De2v  3;B> YR@G/T2o .'l.'l.'l> YR@G/T2 8]mainx r r3LwED@@ f:>&=$:e{#U{#U{#U{#U{#Uxxxxxxxxxxxxxxxxxxxxxx99{V999{V9{V9999999{V9{V999{9{V9˔> Y9{V99999T>9T>9˔> Y99999{V9{V9{V9{9{9{99{V9{V9{V9{9{9{9{9{V9{V9{9{9{V9{9{9{V9{V9{9{9{9{V9{V99˔> Y'n 6999{V9{V9˔> Y'n 69{V9{V999T>9T>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxZ!Z!xxxxZ!Z!9˔> YR@GI&/9˔> YR@GI&/9˔> YR@GI&/xxx9˔> Y{#Ux> Y!flUxxx9˔> Y{#U9˔> Y{#U9˔> Y{#U9˔> Y{#U9˔> Y{#Uxx9˔> Y{#Ux9˔> Y{#U{#U{#U'n 6'n 6'n 6{#U{#U{#U{#U'n 6'n 6'n 6{#U{#U{#U{#U{#U{#U{#U{#U{#U{#U{#U{#U 3B 3B{#U{#U9˔> Y'n 6'n 6'n 6{#U'n 6{#U'n 6{#U'n 6{#U{#U'n 6'n 6{#U{#U{#U{#U{#U{#U{#U{#U{#U{#U{#UFz{#U{#U1{#U{#U{#U{#Uxx> Y!flUxxxxxxxxxxxxxxxxxxxxx9ˍxxxxxxxxxxxxxxxx=$:e99999{99{V9{V9{V9{9{9{V9{V99999{V9{V9˵M99˵M9& a *R*RRRRR^*ZZ^ZZ^ZZ^Z^^2^2^^ 2^ soC{)F2;B7`?{?{?{?{7`?{?{?{?{7`?{?{?{?{7`?{?{?{?{7`?{;B7`?{7`?{;B?{;B7`?{;B7`?{;B?{ 'BAR-A <`k'; ^ q @@(@@q@`R@DC@h Cn~> YR@G/T2o .'l.'l.'l> YR@G/T2᱘᱘tt᱘᱘᱘᱘᱘7XÔ> YR@G/T2o .'l.'l.'l ^e"Pf $AW $A\$A[$@@Z$@Y$AX$AN $@BW$@\0Qo6QP4wE *E QP oDd:M EP]1U[EP]1& Z | @FCN.x H,U[.e.e.e{V.e{V.e.e.e{V.e{V.e.e{V.e{V.e.e.e& N Tn)oRHIb ZMGz=m  ::$T>U[& s}$?m[wTNh(m=o=?;Q a\s ;-{%WClose-z%^.w^*V ^wV *V a/!iV 4V V  'UCO-a ['; ^)1ז)1זo o  x*w*ȖdRS)C @U @@p@j  @@rc @@l @@5-`8*./"0"+","1*"l"CkQ`9o]j<X<a @G}{i @@j# J uK zY@y4ErO@K~bP h@tg@efD@eC Xu|OIuu#c b#a#`#^#uYu_#c   i&EV@@w @Z]e6]_4r]W :_,4 MX@UMCm9]  S^SU[ډ 3B 3B 3B 3B& sHU p#\~ &N\{#[ l#k#j#h#\Y\i#\[   a @@Vy}Iyx#` g#f#e#c#yYyd#`   CCC.\ 2 Z60 3BCnCnCnCnCnʼn 3Bl B$CA$HCHs{Isp#r \#[#Z#X#sYsY#r   d!N Y B @bkuBy @aXT @@W @@$I'h @@AV)g_4h&yI  @@O G"u @@cG@Q6Tg~  @N@e @@gO @@pn'A}A @I-sl~)PEn @\@iTj Io7RDf UުxW rSC@\ E@@K"@\ @ytx} @@L @@EHCrq03, W f g dQr @~@jp4X @U  ]RxnR^)f @@fdE|Pp @_D }KM @E|BgRHmU aC@@ @@FZl@rN}s @x+RV Dxw @Y*uIpp@\@h z @u)rSfh~ @Q)Yd  L*BB @m(.ZM@g n B{u fU{D`W_  PMi @l"jY }g@N @tw @|Sz@qcC @Z @@{ @W 'kH5W5s5H J@Ms@@kg3v \x@E @N zw@@l @B@c< @]@J@@D@Ledv g @ s@p@[L XjhS C[zWA @P$@e^^ w@Ft#N@Uvwg@`BBB;K Zi'r9{V9{V{#U<`k<`k<`k<`kt9999{9{9^99999{V9{V^tOLS^tt9ˋ9ˋzZOLS|.Xڐ7X<`k<`kOLS^9{V9{V^<`k<`k<`kOLS^9{V9{V^<`k<`k<`ky&.<`k<`k<`ky&.<`k<`k<`kOLS^9{V9{V^<`k<`k<`kOLS^9{V9{V^<`k9OLS 3B99<`k<`k99ˉOLS4L4L4L4L9{V4L4L9{V4L4L4L4L9{V4L9{V4L4L9{V4L4L4L4L4L9{V4L9{V4L4L9{V4L<`k<`k7XÌ|.Xڐ7XÐ7XÐ7X99ˑtt 8= privatemsgo"y&V[@x G [@i Ix+R"@@V'@ :uY*h :] @u)@!:` @Q)Y!:h$o m(g GS~@bjR @@H @@S q!:[@E9@PfW@@pN_l @@ TxM  @@I@d  @@_mF2@\zG ! i @4 u]  @Q^g[ V^ U @e~}V@`@y b@mca\"EI {  @ Ri{?FGs@o:`@v&m&v @m _T_ @UW@hju0&!!!El!ElEl!!!!!!!!!!!!!!!!!!!> YR@GI&/I&/!!I&/:>&!a ,Oa ,O!!!u&O]@tM{ @@_v @@/@Fgce MGx J@bz!Pc\)_ZIk@u@]YN Q [T @Ud @z<@@c@} W h yF@cTg @wLl  @]"p  @@p+FG[Do 8o $䇘xW 3B9ˉ 3B99ˉ 3BUު99˵M99˵M9ˉ 3BUު9˵M99˵M99˵MUު 3BUު9˵M99˵M99˵M9M99ˉ 3B9ˉ 3B9 q  @R @v@s@M@~E@M^ y } ZoV@CF @X`@sG @t @@@s @|N y@LY @z@~O= ^@t  @] @_ @m [ @sT`abcd}@Ln @@y[ i @@m@XSI @s @Qj!Q!x a _:A+U Q K wjL^aKkNk P`+@E lV mwuoE f _e\KL d*@hs0L8 E1@V*@nK@[VG@M@h @@X@z;[Vu ^B@eD@jW LT OQR]z5@U} y o@i @r@E@AA|gu4@j J`P@c  Wa@V'{@YpyG x{q@qo A@k YDpFHNJKt@eO@Q^9TUVWAYM _]H@^`abdAhD@f#Dnp^rg@wt xb|YI@T@A@}CVGTIKY@eLH T@wPGSB @\@XBb {%@J@y%Mv%UF e f LnkKwmG@@e%;;P @J @_ @j  @k@UBa z~>B?#A DH%|] o C;kAd3E #I;B;B 3B;B;BI;B KoE@kKS@@U@D$@A$@XHx;!xJ x, J   [H [ ]Dj]d@{ cD}7N Vb{ [.e{V.e{V{V{V{V{V{V{V{V{VN'{V{V 3B.e{V{V.e{V{V{V{V{V{V 3B{V{Vv?{V{V{V{V.eEP]1.eEP]1EP]1{VEP]1EP]1EP]1EP]1EP]1EP]1EP]1EP]1.e 3B.e 3B 3B.e 3B.e 3B.e.e{V{V.e{V{V& n t h!@i!@Zq S|!@@"@I E xFU3r Vq3A"U[ 3B& |F@SDGj5p @v U[9ˉOLSOLS.e.e{.e{.e.e{99.e{.e{999.e{V99˔> Y.e.e.e{V99˔> Y{V99.e{{V.e{{V.e.e{V99˔> Y{V99{V9.e{V.e{{V99.e99.e{V.e9{V999{V.e{{V9{V.e{{V}"#{V9˔> Y{#U{V{V{V9{V{V.e{V.e{V.e.e.e.e.e99.e99.e9.e=$:e99{V9{V.e& x@N"@{Y} @|@@@sI F@D"X_@  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~hHYF~,w ^N'H,.e99.e99.e999999S^S999S^S99S^S999ˉ 3B99ˉ 3B99ˉ 3B99ˉ 3B99ˉ 3B9ˉ 3B99.e99999.e99999ˉ 3B9S^S9.e{9999S^S99S^S9.e9ˎԝX9999.e999.e.e.e& H ]Nexgen core controllerG ] ZeropointF ]1.08 build 1115eI d(@ SF@{ IFm-| T9 CU K/xxx l!EF\7 W]8釅m.e 3B.e{V 3B 3B.eTO& AFoC#@@^g!@k!UE`NER!fN!@z U#@@qn@W#u @r b c {O" u` x^ @ @T M ~BH @H@yDJC$#@pjX}#WvhWv|#X~#=#W&X^X,@hW  ZtR@U,ND @hLBQA %6dL,,?M%6dLD ,D ,&6dL&6dLD ,P,@A D PHP%6NLA Ph6oL6NLkPk,?D HPkk%,@h6oLkk,@A %$k%HPk6NLA D k  OT@RXO@G,fMU@[@gr,\@`@c`Y_ iJo +tsrV+@u@qsU+mynh@x@F8@ }U[999˔> Y{#U9˔> Y{#U9.e999ˋ}"#.e999999ˎԝX& EA`M DR@H~*y*@S Lv @P*@x MN@Q @a@y[v@{srCa@Qb%TkJm`@wqR@ FC@bj>Z @~@b>h4@{@D@e@@mv utO@jI>r@N@PkTTXqe@Z) Q @^@V P Y)kce@@V6@}%Qz NopqGxU)@vt@ar@<Y kK ~S~}vd ]FX[ K&@l<W~ iY&@LNR@f  O^&@SU@Vl r X\Q@@drfliA@`g@`jklG}@I8uopQ@m@d@qy@P@f|a@ta@XA `  F@J@[;@\@PJK@LMNQ|N@I(U} @~[m:L Wg:e@]^aC@_eZ'@hb r pmikl'@l~qv@{f@ye@lx'@t@w@~'@r@@['@\:@@ziG\@@HRK@@@u@R_;@WF@Z  [@]@wO@o@`@ @@\q@k@wU8@mnC@q@s@p@@v_t@B@Sz{@@C@E@@F@GKa@S"LMW@R)]&I@U@_@x(@@H@C{(@@_D\Z@e e@O7@b@a@(WP @D)j|mnoK@s[@@s@u@@v@T)@~(@y@x@v @|@CY uXDEF@H@I@eQ=Z6@lw ORSTZd@z%h=__@x=t@@@D@`hS y @>Vnl pm@Os%lq%lxFEbC@@` @^ @|@{@m%x@_H@b ^@u>QK@L@iNl@r3Ti3~UWW3o@V3@\Q3n P3@^@L3@@X@h%iD3@j gE@n@cqA\%~O2@V%@^@U%] {@2Z|@x}1@s?<s. ?,?, vY@t@M*@C@Z FR%RV @Qli*PNo M%L%YCi/[F c^ab@%Y/g`e^xL/ls$vC/@B/@M @tc$oF v.a$RKe CD.@@ABREt-|Ty+Ou@QBPF-l@Q-UXZf'oYn^{s,]@bB@W@cR$^C1l %^&^,^,^C$P$,@V%,xjV&, VV,,p $V,,νV,,|V,, *ƇGV,,F0V,,FV,,ؘiV, , DV, ,[V, ,\V, ,"kV, , qV,,CyV,,!IX&,b%X,, @@X, ,QZ^&X%,ǶX,,]/X, , SDX,,X,,X, ,!X,, 7X,, X,,ZEX, ,X,, X,,ogX, ,L*H,,B9H,, qH, ,"amH,, 8H&,D꾤H,, KH,,`KH, ,pH, ,~(H%, 'H,,0H,,H, ,9H, , H,,|H,,eV[%,D")[,, *C[,,#[,,9[, ,Y[e[,,  [, ,}[&,][,,O~o[,, ,[,,C[, ,N[,,~S[, , 5:[,,*[, ,ӆ%^&^,^,^  S@I,@m@Vmno@zLr@sI$TXs %^%^D$^T=s U,^&T=:=s U,,^,T=:=s U,,^,T=:=s U,,s ^,  ~#GJ fE@{R @\A B C @~@CF @G _ YCAz#[C@O N DC}@cC`W X Y @l#|\ @] g#vb#t@d `*di j k @@ln o p @\#rs @t [pz'fn%^nW#,op0123456789ABCDEFp,&opp,no  X@mpw@M8A { [D& i@| A!o{B!C!D!@rF!G!H!@@ghL!@M!R#@eeJ#d@T!X!@j@uZ![!\!@H#v_!`!a!@F#@f_e!@f!Q@E#@\MF@U@n!{r!s!t!@t7v!x!OO~!@Ml@~ B"@U(E"@O(p@BG@zCRGf?k 2H"&Cnť᱘᱘᱘ V]98Failed to login: you are banned/kicked from the server.]gThe server has refused to let you join the game because you have been banned or kicked, meaning you are probably not welcome for the time being. Please go play somewhere else, there are enough of other servers and games out there.R] Reason:Q] Period:N]e"O65-|'*./" 0" +" ," 1*";*"5@ =U}q>r˲ o8`tmxuejeD4 F,-Pnled;.:Oz2#&XĔHIW[Z'0Ԟ3M+Y9K~^Yb/)^]JwA $_\%A _QE i" {SGp*VRyC 욚1@C(0?<70g505|LA7hvT=fޫ!6BѦߤh06kascN7  z'@M"@[G~ 65-|'*./" 0" +" ," 1*;*"@q>r˲o8`tmxuejeD4F,-Pnled;.:Oz2#&XĔHIX[Z'0Ԟ2M9~^b^]w_[_Qi{SqRzC욚1>C0><70g505|LA7hvT=fޫ"6CѦߤh06l`rdN7 V'SQ"@HT"U"V"W"X"Y"Z"["@O^"_"`"a"b"D<@e"f"g"h"i"Z<@cUG@LGHG@rFs p"&,&imFt q"$BE& hFr"s"t"u"v"@`Fx"y"z"{"TFt1y 8}"Y>n9˔> Y 3B9˔> Y9˔> Y9˔> Y9˔> Ysss֨)ssssss9˔> Y9˔> Ys9{V9{V9{V9{V9{V9{V9{V9{V9{V9{Vsssssss `]mutate nsc balanceteams`mutate nsc setteam 0`mutate nsc setteam 1`mutate nsc setteam 2`mutate nsc setteam 3` suicide`"!mutate nsc openvote,mutate hz010`87mutate nsc openrcp,mutate asc#get#window,mutate hz0090`mutate nsc pauseDF@~E"zEEmE@Z@[@[E @c!@a@^!@c@S!@FE@BE@M#@N#@O#@P#@wD@O!@@S YDuAe 2V#*gM~tt> YMtU K/> Y7XÐ7XÑttt᱘᱘ I]CloseH]SendG]Say:QDX#Y#Z#[#~Cc ]#=u& M O S^#_#`#a#c#d#e#f#h#i#j#k#mCm#n#u#o#gn#o#m#  hCq#r#Rx#s#g bs#r#q#  V v#{#w#ew#V v#V   ^Cy#YL kL z#L y#  W|#}#@v@E$@IV@uB$ULI%Z%ZA$IB$:ZV:Z&V,:Z,V,:Z,V,IZ,  s @}Bh3Z F$Г 3B 3B 8]aboutwBU4Y WG$R99ˉOLS7X yJ EyE7XOLS yJ EyEttt7XOLSttt7XOLS yJ EyE7XOLS yJ EyE7XOLS yJ EyE9|.X yJ7XÐ7XÐ7XÐ7XÐ7X yJ7X EyE7X EyE7X EyE EyE7XÐ7X yJ yJ EyE9t9t9t9999|.Xڌ|.X yJK4 yJ99 yJK4 EyE9 EyE4L4L4L4L4L4L4L4L9{V4L9{V4L9{V4L4L4L4L4L4L4L94L9{V9{V|.X4L94L9{V4L9{V4L4L9{V4L9{V4L9{V4L9{Vttt yJ7XÐ7XÐ7XÐ7XÐ7XÐ7X 8] accounttypes` @QH$AES,m&7m,@SpS%mul6d` ,Q%6d` ,,?Q,8K,8QK,xQQ` SKRm%m,RpR:mlmQ` R,uH$6o` ,m%Cm,@m6N` $m  uBI5X zK$#7XÉ 3BtOLS 3Bt7XÉ 3BtOLS yJ EyE 3Bt7XOLS yJ EyE7X yJ yJt yJ EyE7X yJ yJt yJ EyE7XÉ 3Bt 3B yJK4 EyE7X yJK497X yJy&. yJ7XÐ7X yJK497XÉ 3Bt 3B yJK4 EyE7X yJK497X yJy&. yJ7XÐ7X yJK49)|.X yJ EyE EyE EyE EyE EyE yJ EyE EyE EyE EyE EyE9|.X[[9|.X9{V[[99ˉOLS[[9|.X[[9|.Xڑt9t999|.X9[[[[[[[[9[[[[[[[[9{V[[[[[[[[ yJK4 yJ9 yJK4 EyE7X yJK497X yJK4 yJ9 yJK4 EyE7X yJK497X yJ7XÐ7XÐ7XÐ7X yJ EyE yJK4 yJ99 yJK4 EyE9 EyE7X94L4L4L4L4L4L9{V4L9{V4L4L4L4L4L4L4L4L4L4L4L4L4L9{V4L4L4L4L4L9{V4L9{V4L9{V4L4L4L4L4L4L4L4L4L4L4L4L4L4L4L4L4L4L4L9{V4L4L4L9{V4L4L4L9{V4L9{V4L9{V4L9{V4L4L9{V4L9{V4L9{Vtttt[[[[[[ yJ yJ yJ7XÐ7XÐ7XÐ7XÐ7XÐ7XÐ7XÌ|.Xڌ|.Xڐ7XÐ7X[[9{V 8= bancontrolrBG6V uL$9OLS yJ yJy&. yJ yJ yJy&. yJ yJ yJK4 EyE yJ EyE EyE yJ EyE yJ yJy&. yJ EyE yJy&. yJ yJ yJ yJK4 EyE yJ EyE EyE yJ EyE yJ yJy&. yJ EyE yJy&. yJ yJ EyE EyE EyE EyE EyEOLS|.Xڌ|.Xں_4 3B[[ 3B[[ 3B[[9ˉOLS_4 3B9 3B[[_4 3B9 yJ EyE EyE 3B9 EyE EyE 3B[[ 3B[[zZzZ99_4 yJ EyE EyE yJK4 EyE EyE EyE EyE EyE yJK4 yJ yJ yJy&. yJ9 3B yJ yJK4 EyE EyE EyE EyE EyE yJK4[[9[[9[[9|.X9|.X999 3B9 yJK4 EyE EyE yJK499 3B9 3B_49{V9{V9{V9{V9{V4L4L4L4L4L4L4L9{V4L4L9{V4L4L4L4L4L4L4L4L9{V4L4L4L4L4L4L4L4L4L9{V4L4L9{V4L4L9{V4L4L9{V4L[[[[[[7X yJ yJ_4[[[[7XzZ yJ 8] bootcontroloBR6U kM$`jO9˵M9ˌ|.X9˵M9|.X999˵M9ˌ|.X9˵M9:>&|.X9˵M9ˌ|.X9˵M9:>&|.X9˵M9ˌ|.X9˵M9˵M9ˌ|.X9˵M9˵M9ˌ|.X9˵M4L4L9{V4L4L9{V4L9{V4L9{V4L9{V4L4L9{V4L4L9{V4L9{V|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.X9˵M9ˌ|.X9˵M9ˌ|.X9˵M9ˌ|.X9˵M9ˌ|.X9˵M9ˌ|.X9˵M9 8Mclientsettingsg$pCmBu6T FN$u@:9{99'; 99 3B9 3B yJK4 EyE᱘᱘9˔> Yv?᱘9˔> Yv?᱘9˔> Yv?9˔> Y᱘;(᱘;(᱘;(᱘9{|.X9{|.X9{|.X9{|.X9{9˔> Y'n 64L4L4L4L9{V4L9{V4L9{V4L9{V4L9{V4L9{V4L9{V4L9{V4L4L4L4L4L4L4L4L9{V9{V4L9˔> Y4L4L9˔> Y4L4L4L4L9{V4L9{V4L9{V4L9{V4L4L4L4L9{V|.Xڌ|.Xڌ|.Xڌ|.X 8] gameinfojBQ7S BO$i9᱘9)7X9˔> Y9˔> Y9˔> Y9˔> Y9˔> Y99˔> Y9˔> YM9˔> YM9˔> YM9˔> Y9˔> YM9˔> Y999˔> Y'n 69˔> Y'n 67XÐ7XÐ7X9{7XÐ7X4L94L4L4L4L9{V9{V994L9{V4L94L994L4L9᱘9{V9{V᱘᱘999᱘4L᱘9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9ː7X9{V7X9{V9{9{7X R*RRdRa*``*`_*@@@8] startpage@fBi7R RQ$Js97XOLS yJ EyE7XOLS yJ EyE 3B[[|.Xڌ|.X9ˉOLS yJ7XÐ7X EyE|.Xڌ|.X[[|.Xڌ|.X[[ EyE|.Xڌ|.X[[ 3B9 EyE|.X9|.X9[[ yJ yJK499 3B9 yJK4 EyE EyE9 yJK4 EyE9{V EyE9{V9{V9{V9{V9{V9{V[[ 8]ignoredweaponsettingsg$B`BB D$Ah%6dB %&6dB %%6oB #Eg&6oB ,6oB ܺ,6oB vT2  cB@^BA8P VT$2^sOLS|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڑtttt9)7XÌ|.X9|.X9|.X9|.X9|.X9|.X9|.X9t9t9t9t99ˉOLS9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{Vtttt 8]nexgenlogsettingsg$ C\Bn8O iU$?t_4h{h{ 3B 3B 3B 3B_4_4)7XOLSOLSOLSOLS'; ^OLS'; ^OLS'; ^OLS'; ^OLS'; ^OLS'; ^OLS'; ^OLS'; ^ 3Bt)|.XOLSOLSOLSOLS9ˉOLS99'; 9'; 9˔> Y'n 67XÐ7XÐ7XÐ7XÐ7XÐ7XÐ7X^^9{7XÐ7XÐ7XÐ7XÐ7X^7X^7X^9{9{|.X9{|.X9{|.X9{|.X9{4L4L4L4L9{V9{V9{V4L9{V4L9{V4L9{V4L9{V4L4L4L4L4L4L4L9{V4L9{V4L9{V4L4L9{V4L9{V4L9{V4L9{Vt'; 롌|.Xڌ|.Xڌ|.Xڌ|.Xں_4|.X9˔> Y'n 6|.X9˔> Y'n 6 R*RRdR8] matchcontrol[BV$VBZ9N JX$A^99ˉOLS)7X9))7X99'; 9OLS 3BtOLSttt|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.XOLS'; ^ 3Bt 3BtOLS7X'; 롑t9t9t99|.X9|.X9|.X9|.X9|.X9|.X9t997X9{V7X9{V7X4L4L4L4L4L9{V4L4L4L4L9{V4L9{V4L9{V4L9{V4L9{V4L4L4L9{V4L4L9{V4L4L9{V4L4L4L4L4L4L4L4L4L4L9{V4L4L4L4L4L4L4L4L9{V4Lt4L9{V4L4L4L9{V4L4L4L4L4L4L4L4L9{V4L4L9{V9{Vttttt'; ^^9{V^7X9˔> Y'n 67XÐ7XÐ7XÐ7X'; 롐7XÑttt|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڑt 8= matchsetupTB@LB|9M [Z$OyOLS|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.XOLSttttttttt9ˉOLS9)7XÌ|.X9|.X9|.X9|.X9|.X9|.X9|.X9|.X9|.X9|.X9|.X9t9t9t9t9t9t9t9t9t99{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{V9{Vtttttttttttttttttt 8]miscnexgensettingsg$fCCBe:L B[$ls9{99'; 9˨)7XOLS'; ^OLS'; ^ 3BtOLS'; ^ 3BtOLS 3Bt'; 롔> Y^'; 롔> Y^|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.XOLSOLS|.X9 3B[[|.X9 3B[[9OLS'; ^ 3Bt9ˉOLS[[|.X[[|.X'; 롐7XÐ7XÐ7XÐ7X99ː7XÐ7XÑt᱘᱘t^᱘^᱘^|.X9{|.X9{4L4L4L4L9{V4L4L9{V4L4L4L9{V4L4L9{V4L4L4L4L9{V4L4L9{V4L4L4L4L4L4L4L4L9{V4L4L9{V4L4L9{V4L4L4L4L4L4L9{V4L4L9{V4L4L9{V4L4L4L9{V4L9{V4L4L9{V4Ltt[[[[[[[[t'; 롐7XÐ7XÐ7XÐ7XÐ7XÌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.Xڌ|.X[[[[ 8] moderate@BW;J F\$:v97XOLS yJ EyE7XOLS yJ EyE 3B[[ 3B[[9ˉOLS yJ7XÐ7X EyE[[[[[[[[ EyE[[[[9 EyE[[[[ yJ yJK499 yJK4 EyE9 EyE9 yJK4 EyE9{V EyE9{V9{V9{V9{V9{V[[[[ 8]replacementhudsettingsg$B~A];I i]$E 3B9˔> Y4L'n 64L4L4L4L9{V4L9{V4L9{V4L4L4L4L9{V4L'n 64L'n 64L'n 64L'n 64L'n 64L'n 64L 3B94L4L9{V4L4L4L4L9{V4L9{V4L9{V4L9{V4L;(4L;(4L;(4L;(4L4L9{V4L4L4L4L9{V4L;(4L;(4L;(4L4L4L9{V4L;(4L;(4L;(4L9{V4L;(4L;(4L;( 8= serverinfo|Ay;H p^$htttttttttttttt|.XOLS)7X9ˉOLS9t9t9t9t9t99t99t9t9t9t9t9t9t9t9|.X99{V9{V4L4L4L4L4L4L4L4L9{V4L9{V4L9{V9{V4L9{V9{V4L9{V9{V4L9{V9{V4L4L4L4L4L4L4L4L4L4L4L9{V4L9{V4L9{V4L9{V4L9{V4L4L4L4L4L4L9{V4L9{V4L9{V4L9{V4L4L4L4L4Ltttttttttttttttttt 8MserversettingstAW=G z_$޶9ˉOLS99[)1ז9)1ז)1ז)1ז)1ז9)1ז)1ז)1ז9)1ז[K49999˺_49997XÐ7XÐ7XÐ7XÐ7XÐ7X[[[[9999|.Xڌ|.X9ˌ|.X9999999[[7XÐ7XÐ7X[[9_49[[[)1ז_4[[_4[[)1ז yJ[[)1ז yJ yJ EyE9|.X yJ yJy&. yJ[[y&.[7XOLS7XÉ 3B[[_4 3B[[OLS7X[)1ז 3B[[_4 3B[[OLS9[K49)1ז)1ז)1ז yJ yJ)1ז[ yJ EyE9[)1ז yJK4 EyE)1ז EyE9)1ז9)1ז yJK4[ EyE yJ[[9 yJK4 yJ99[9 yJK4 EyE EyE99 yJK4螺_4_49{V99_494L4L4L4L4L4L4L4L9{V4L9{V4L9{V4L4L4L4L4L4L4L94L9{V9{V4L94L9{V4L9{V4L9{V9{V9{V[[[[_4[ yJ7XÐ7Xú_4 8] useraccounts`$b$e$@i$@@f$@g$@j$@k$@@UA@l$@m$@@o$@IA@q$@r$@w$@@u$@x$@@A%Ts@@{$@@}$@~$@$@g@`@@P@@@D%@E%@F%@G%@P@J%@K%@@N%@@P%@Q%@@S%@T%@i?@]%@X%@Y%@Z%@[%@@o@_%@`%@a%@qph@f%@g%@@?@qIl%h>VY Ip%s@M@`@\u%c=@s x%aWT=@i V=b@M=B&@C&@D&@E&@K=@\D=@G&@fR@Q&M&N&O&P&j<R&K@T&@U&V&W&X&f<[&@@                !$ (+++,%!%!1!2!3! 5# 6%=&>&:2:0""!''(.2:32+97-444>><>>>F.L/D8 A:B<X- ^/ Q6 Q5 V1 W6V: Y7Y8Z8 Y? U?a/c4 c<f?h0r!I@SB^A OI&GC4JE2QD'SG%PL;PM?XP&UP:aBeAeB dCbHaMkBiG jOkK hQmP jQqG tK xJ rRyU}TlX1o[6vW y^*p]9zaxk0SYbQXdhaEqqMaehy|z^[[\bm ah`qq u { | dldhg"r/p>}3v?tC 9 4S^A]CHl|\&@{@O_&@`&@@b&@c&@O @e&@f&@G<Jg&b@v@j&g@k&@q&@j;@r&@h;@Q@@   -6( 6$3! $#!! ""$%#!%$"%%%'('(%")(")(%,)$+))./-0.)0/;30+30,51-74/210315630542952986=:5<<3==:Z$|! M=;ECK ]@4@>9J>:fjzn*d j><|(3z@x3WBD:EA=GD?DEEDBKIFBMGDHGKJHBMIFOLGKKMOQMOQPQB^RQGQPMURMXUORRQUPPWUPZVQYYS^[R^ZT_\WZZX\YY^^ZB@cEClIG`NGlIHlIDyNLpNIzcUPa^Yg_[e_\zEFba[f`Yea\kd\``bgcegfdjf`jhckignhammfeczoopnlxslcrmlqrlvqjuvlztlvvrxwzzzu{||72<<979;9;:;72596994=988@=D6C1B;U5MGVQb`~|{~~goprCBZSebzz':6<F!D&O{u|t|  18!.)5  ++..,-~f}~¼ƾn&b@u&@@      #%&)*!!/##/%%4((7))9)):**<--=22?..@33D22G33H88M99S::R::T<;U<;V==Q==U<=X>>X@?[AAPAAY@@Z@@\BA]BA^CC\BB^DD\DD_FF]FF_LL\CC`DC`DD`FEbFF`FFbFFdHGeHGfIIbHHeHHfJIdJIgJJeJJfLLfJIhJJiKJjLKjLLiLLjOOkMLlNMnOOlNNnPOoPOpQQkQPnRRmRRnVVlQPqQPrRQrSRpRRrSRtTSuTSvTTrWVpTTtUTwVUuVVtWWvVTxWVxXWtXVzYW|ZZsYXv]\s^^uYYxZY{ZZzYX|ZX}[Y[[|[Z\[|_^z\\|\\~^^``uccygg{\Z][\\]\^\^^_^^]_^_b`^`_``aabba`a`ccedddeeffb`cacbdbeceeffedfdgegfhgihkknmhhkjjjkkllllgfhfigihjjkikjkolkljljoomknlompnpntt~~qpqqssrpusttuuvvwwxwyxtyzzxx|z~~~~}{z&d;w&{&@k @     )'!*#-#,&/&2&3,5,4* 4,!4,#=2%@3'D4%C7*A:.D:-G>.M7)I>/X?0MC3SC-TE3TH4[F1fN6qT=uZ?_TAcUBdXEj[Ayb@bFfFmGmJlJnLnQqNwKtKuNrLuNxSqPvRwRvT{R{T|Q}TsMtKwNvL{N}T}T}PNPVSagTVXYXURUZ\\]_QSY[TVX]YY_eacacibeacfkhlmhmmkmoimpo_‘bŒ`ǔeÜnŘmȕcϜjϞmpsřrƞqʜrўlПqsväqġsĥqťtǡxǩsŨvɠsȡvΡr͢vˣyʥyʮyˬ|ͭyέ}ϲzҢr֣q֥uҥxЩ|Ҭש}ԯئwܩvڨxв}Ӵ~|ӭҬ׭ܯұԷչٺڻؼۼݻ߻᱀ഇṆ赂빆칆辏쾎œÏ’ƑƗ̚ƒɑɓ˙’ƔÑēƗɛ͙ə͚̜ɚҚљўԛ՜֞נԤءܣԢ٠٧ݢۡV;|&P;N;A'@PI;D'OD;J'@h P@P'ng@y@Q'R'@S'D@ U'@^:@@  !$(& )"-#3&2)7+5+9,=1"""%%%)%!*(&)))**-.-+---1,'7,!4/+20.;1'81*?4)=4.11122443366678::6;999;;*CA?LA5NEx[6}\8AABDCBEEECCLHFEHHHIJLNLKMMMOPRQGDXOCTNR\QGZTOQQPPPTUUUWWYXV__XSYYYZZ\\[[\\\]]a_`c`]\m_Rw_Gfa^pg^{gSaaabbfeeeffjhgejhgnhcihhhinoljllljkpkmrqqqvtsvvvuvywx}yxuzyzyy~}yxz|}}h>jNhEkJrFzWzz`yG~LRs]TbipŸpʥ}ϳؽӻa'@\'@]'^'@M:@`'@L:@c'@I:d'@G@f'@D:C:@W'h'@k'@9m'@n'@r'@p'@y9@p9@u'@o9w'uy'@{'@i9@z@@        "&! $ ) * &##, 3# 1& 2$6%9$9(:-?,=)'$ +&!0*$>4$421:60897998@'F( L* B-D+L,D1 E1 O8 E2G2L3U7 P8 Y0]: P0R5R:Q8W9T9];]9^>F6!J3 O;#K:*N>.@80C>8Y?#]?)`> j>c;e?g?"]A_A^G@@26b|YOOOMMRϺTd2,e25^RNQONLLKGGH4$8/26mLGILLIE &.*120JEEGIF &$,=E &F @        '3 $2!**!",?/$,)&'9.9 +0<0)&%*G'-R-3\66@07\-4f19i29v5D3@?L>S?M;;CyK=JLCVK]@P"EZ$YL S_26b|YOOOMMRϺTd2, e25^RNQONLLKGGH4$<8/26mLGILLIE &.!9*120JEEGIF &- (!$,=E &F   J(@M(L(K9@N(P(8Q(W(@  """"#####%%%&&&'''((()))***+++,,,---...000111222333444776777787888999::9:::;=<===Pd?BBBCCBCCCCDCDDCDEDFFFGGFHHGHHHIJHJJIJLKKLLNPOPPOPPPQQQRRRSSSUUSUUUVVVWWWYYYZZXZ[[[[Z\\\]_^^^^^_^___aaabbbcccbdcddceedeeeeefffeiihiiijjijjjkkjkkklllmmlmmmmmnnnnoooylkppov~opppqqqrrrssstttuutuuuvvuwwu{{{{{||||}}}~~}~~~Fcmw1/{R~Hxw¾{8V(x8X(Y(@[(@5-L*./"@0"@+"@,"@1*"Fĥ@@¿}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuussssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd@@\(@p8@Su `(} @log(@e(f( y!z\@l(n(u!co(p(q(r(s(t(u(v(@B@|(f AN E)@A)B)C)F)@G)@H)@J)K)L)M)O)P)R)]!cS)@t@w@@W)@B @d@c@e5Y@\4HQM4J4@b)@hf)@n3@W@g)@N@i)@j)MLK3@J3F3|V ' l@o)z2Xe r)hE!ct)v)d\ @y)@b*)z)T)$z\ s{xw\ *`w.\ *.\ Tz)y)\ \ } X @|)@})]A*~)<!X s{}wX *ew.X *.X <~)})|)X X } G jC*@*L'uG s{swG *[w.G *.G  L@*G G } A hE*B*3iuA s{swA *[w.A *.A  3B*A A } g2K2V$ I*tm{ s{-u  w{ *.{ i|jt-u 'a/!F-u .; mt-u s m { '{ { }-u  D*I2h H2D2[! G*B2b[# K*}!f{aP~a,%]a~a,va~a,&[]aJs{-^ wJ*{.;Jw{*|{j]-^'JJ}p-^v {!}|ZvJs} }|wJ*v .J{Zv jZv v #v  H*~1\& A@s.C s{-@  wC *L.C wL*t|Lj@-@ 'La/!FL.;L s@-@ wL*-@ C C }-@ L* A~@j1b1_1@Y1S*@ZK1lT*W*@X*@\*@l ckZ*']*@c*\@z ^*@_*']y0TU  q f*@g*@o*@h*n*OysVW 4$OQT <$T Oi*h* k*l*Dm*Wq+)wE *E Wm*l*k* q*@^0p LS0lO0t*Wu*w*}*@E0z*@{*@+@~/J@F+@R :x/B+C+@K+@r/G+H+@e/dQ+R+]/Q/@qyj.X+Y+Z+Wuf@]+@^+@_+@Cc+@d+@a+8f+@j+k+n-h+@j-o+m+^-u+@v+w+D cs+nT-N-E-@A-E y,v,D,B,]z#B,-j!J E,c_k,ppp[cX] cQ l,o,b]S&( KF,H,PG@L,! "Failed to create log file: %1"M,' "Nexgen Server Controller is active."N, "Restarting server on %1"O, "EXEC %1"P,9 "Nexgen boot sequence failed, resuming to normal mode."W,+ "Warning, attempt to load %1 has failed!"a,R,A~ S,Q,C\c@ L>@ /a0 :uRQwu*R10wu*C%CsuF6D C\6C C\6B C\6A C\Ces% FT,pCru*,s, VsstV&fVsV&\V\V3ss&6D V\p6C V\o6B V\n6A V\muponm X,C "Server crash/reboot detected, executing Nexgen boot sequence..."Y, "ServerID = %1"Z,7 "Configuration file has been automatically repaired."[,2 "Updating Nexgen configuration to version %1..."\,5 "First time run, executing default installation..."],8 "%1 has been detected, entering compatibility mode..."c,, "Failed, your server should be dedicated."5-_,*./"0"+","1*"758q@ 00#000000' !)00000000+" !'00000-'00." %,00000 000&$+000000000(")000000(00+"!'000000, 0/#&,000000$&$+000000&-000* @HHHQQQbbbnnnzzz|~~OG "Loading %1 %2..."t,Io`-rI-F-rIwGJtsfI-LsLIvwGJtCL!GJ JGC*WU^]. d3^JG^GVXG,F,?& K,-H,GJL. TrL*L ^,GV?KX?&LK,GW. Gb. GV?KX_.  d,( "Starting Nexgen Server Controller..."e, "%l, %F %j, %Y, %H:%i:%s"f, "[%d/%m/%Y %H:%i:%s]"i, "nsc_%Y_%m_%d_%H_%i_%s"`,* "Failed to initialize %1, destroying..."g,( "Failed to register %1, destroying..." "dd/mm/yyyy hh:mm"g-| A nL:w| *| a/!AF.| w} *1r} *} a/!{DZvURONrF*D$ppF: D$VH,wH%*-H%)A| } ZURONL-(Hw*)A| } ZURON' b,m,cM w* $  h,% "No replacement class found for %1"p,n, "Login request for %1"k,@@x,|> L-yE!B4-y|{EJC{E|* q, "IP = %1"C,A,W:A,qI, u}f'5}ru*\3u t} w,{,re. F z,@,j`E .^wE *BE e@,E kE .E  u, "ClientID = %1"J*B-_\'3J(n__name_/ |,+_Y@ .Uw@ *9@ e+@ @ .@ * }, "Password = %1"@ H*H-db$4J)nddteamSdj ,~+\U Y| .Uw| *9|| E~+| | .| * @-- "Illegal login parameters (possible hack)."| VC-\o[#ecw*f\r. Ufk*\ p f G*P-Q[!L J?`wL *L !<QC L plL L AQD#EQnameQ/QDEmutedTQ-{QD$EnoTeamSwitchTQ-y q@}+j,ID TICr@-~ XIU }wC*k-~@fCU }|Iasc#get#window|Ihz0090@fCOPENRCP}-T 'G-~ -T #,w%*%DI@Gw*DI@ E,J-|+b>+.)|+-l&(<%( D- "Client ID already in use."C,L-W]1C;W-2.)W-l}+8jAJ W-F`WxU Fj1F-FU ,RRU % U ,U , &[qFidF\ &[qFipF^ ([qFnameF/ )[qFtitleF{ [qFteamSU  +[qF countryF~ q D,{+_n{+Q M-" "Kicked/banned from the server."@XC?Y. S-6X$6X$6X$XPOy+s+?& 6X=,6X=,6X=, 6X$6X$6X$X%(XP?,O. S> S- "Invalid password."F*_-z+h%xnJ?vwn*V`nz+&<nC npjVnnA ` 24.0X- 16.0V- "Server is full."h-t+C?10.wA*At+u+v+w+ U-r+\?T Yy . Uwy *9y (r+y y . y * y  D*e-pVQ`pjJ?wj*nwjpj&<pC jpjQjjA%u - pD2pnameJ-A {u{up/p jup-{pD mmutedp-ypDmnoTeamSwitch \-a-cT w* $  ?yw. T-'?,o  b-MG,5.T-L-M--M ]-q)s-q+Xrq+ S  Ln y}o-L6n $6n $6n $n  n p+m+ M.HC7 l=rH:|V}TrH:|V}(wH*Ha/!AV.H-x wV*Ha/!|0|V}V&pV:-x  rHl-_a.HV}V&-x nrHwn*nh-G-i:4,nnFnnGrHlq-_'n<$-Unable to send message, you are muted.(-x  rHlV$mm-x  wl*1rl*la/!{JrV*V$mppV: V$M,wM%*-aM% CHlV-NT-a(Mmw* CHlV-NT' i-d-A0. o-n+c!c,&$w1*1cn+o+ k-l-f-l+ 6Il+h+?-}}$ |-i+e*.*w1*1ei+j+k+( Y- 12.0p- 4.0u Hy wu *;-IHu G?D?u @E?D?u @HGEu  v-QTQ pQJ?^wQ* -jGzQ~ o znQ^zo &!o  !BAD INPUTQ~ none !DISABLED-j' GG{o |o ,noneQ~ noneGQ~ o ,J+nQQ countryQ~ QQAn-jz* u-Gz-g?wz*T Fx-o)~-w-|&Jg ]$Jf N$Je U$/a0 F!E {zF{10a?@'' g+?pNexgen Server Controller vU _, 3@A?,?,8.3-'-' }-y-pl'' i.b+[5%+)w1*1[b+c+d+ - "noTeamSwitch"@. "muted"z 2.0q- 4.0e+? OpNexgen Server Controller vU _,-'-' B.SM8 0~S.%Spp S .S~CS f.C~@A?,?,f}%Bfi Af_@fhCf#`+_+^+]+f DZ- "No playing rights."E. "Login accepted."F. "Login denied. Reason: %1"G. "NEXGEN LOG FILE"I.@H. "engine-version: %1"J.O.L@2pwL*La/!{ rLRg !M g !R-Da.Le RrLwR*RhrLR-D'R<$-Unable to send message, you are muted.(BrL* wR*rR*e $wL* rLRwR*g !MP$g !RP$P$ppR: e PPN,wN%*-KN%%@LR}e g -F-K(Nw*%@LR}e g -' S.L.anQ( P.kMM-M? AKk,wk%*k" Q.TV+a>T,wT%*T%HT_wr*r^r* R.UH%,XF "The game has ended after %1 sec.SDXC?$-G`-G-y-G-G(]$U,wU%*U%HUb T.V`m"CThe game has started.$-G-y-G-G']$V,wV%*V%`V s.V O U.X fX, p-G-^X X$IWaiting for ready signals, press [Fire] to send your ready signal!n0-G:d %X X FX O$@Please wait, an administrator is going to start the game.nX 5$&Press [Fire] to start the game. r.X|"jP$x%xx%:4%-_4$V ?wV *fV V V Ax%:4,v-G:R% r?*#@?,<:R-G(b%$-y-G(:o%o:o%-D'bdebug gpfNX,wX%*X%|X l 4.0V. 4.0W. 0X. 1Y. 1Z. 2[. 2\. 4.0]. 16.0^. 12.0_. 16.0`. 13.0a. 18.0b. 16.0c. ","d.[+aLwo@ ,@6W@ +[+6Z@ +Z+6N@ +Y+6T@ +X+@ u K. "nexgen-version: %1"f. "server-id : %1"g. "server-name : %1"^/Eo -F E-Z-F Ww1*1$oEK ABH G zE-CG CEvK ABH CC!AB k.e.KeZ"a@@@@@@A@@ A l.lvZ"a@@@@@@@@@@ m.W+Q "W+ h. "server-port : %1"n.& " p.ZQ k-V+B-yP ?D6T"+BZBhP ?D6T"+ZB-yP 6T"+ZP Z[P ?D?m@@w6T"+[?m@o6Z"+uo[?mta6W"+o6N"+[a6W"+u6N"+w"t x.agOa?wa*T{a/a\aaj:abaaaA -:4,4$H^xxAMsss| y.|AL o. "game-class : %1"@P u.@J/x\s1zxF ?wF *orF *[F F F A<-r{x?%-r'V-~E-~' {.w.TJ .z.3wH q.\)k-U+B-qK ?D6N"+B\BhK ?D6N"+\B-qK 6N"+\K \]K ?D?u@@o6N"+]?u@v6W"+mv]?ulav6Z"+]6T"+am6Z"+o6T"+"l |.H/~.PF t. "level-name : %1"K sA/D/E/F/T/G/BD( }.H ! 2Qw6T"+?H &ky?Dw?H hwy?H ?H _6Z"+`%B`H E y^h^?&^^?E E ?fa6W"+_6N"+E `%ef__E ?k`|"e U/@as  p-M 's 0!r [!redDNSCSETTEAMS%@ b!b !blueDNSCSETTEAMS&@ !g !greenDNSCSETTEAMS,@ !y !yellow !goldDNSCSETTEAMS,@ !t !team B!teamsDNSCBALANCETEAMS@ I!p n!playDNSCPLAY@ u!s !specDNSCSPECTATE@ !l !startDNSCSTART@ !quit !exitDNSCEXIT@ !leave 0!byeDNSCDISCONNECT@ 7!o _!openDNSCOPENRCP@ f!v !voteDNSCOPENVOTE@ -M (N %-M  N ,wN %*-M N %a@pN -M  wK/M/N/O/` @/ "level-title : %1"P/X/S/GzA _/T+x{s` ?]-z w` *F|` \T+-z'Z` ` A o-z` q* I/B '\9Q@6N"+?B &cA?D@?B `@A?B ?B b6W"+c%BcB  Aa`a?&aa?  ?_ab6Z"+ 6T"+c%^_bb ?cc|"^ `/W/Dg= @@Z/@[/Z d/P `-M !BH-M Ew1*1P O ^CO P * j/S+YYsZ ?]-{ wZ *FZ C S+-{'ZZ Z A o-{Z q* g/\/JZ; a/@V/O+:%C>dy 6W"+G6N"+Gdn:\&y 6W"+:\,y 6W"+6N"+dy ?D6W"+6N"+d@#ex 6Z"+H6T"+HeS:[&x 6Z"+:[,x 6Z"+6T"+ex ?D6Z"+6T"+e@"O+y x GH f/P+L+)w1*1LP+Q+R+ d k/N+kV!w1*1 kN+ _0b/DK.9 y W0M+r7sd ?]-B wd *Frd M+-B'Zd d A o-Bd q* o/L+j!w1*1 jL+ h/l/c/I+gJSf.: H+AG+$f I+ff u/J+ax!w1*1 aJ+ R/ "Log started at %1"p/ "Log closed at %1"fq/ "The game has started."n/E+7Nhv .: @A $v E+L-C+v k&v B+v  v/S fK w1*1--1--1--1--1--1--1--1111111D-Q D-D-(1-'1 fS D--Q A tS   {/D+`9!w1*1 `D+  s/$ "The game has ended after %1 sec."v w/ "(+%2) %1"t/*RJ?%KJ0K6N"+g.: JA~*$gKg  *g 1B|!1BAA+BR %hR ,B*!R ;R %R , B o ]#R o m/y/ "(-%2) %1"gK-T % Z:T (]  jT S?wS*S53T :T (] :T (SSSA6~,w~%*~% 3T ~ |/z/|*!wWht .: PA $t {*t |*`-z*t k&t  /v*] ^adW?_wW*WLv*dWWA }/+ "%1 has changed his/her name to %2."C0 "%1 has left the server."t D0# "Failed to switch team, %1."A0H0b8Z'V.:  A $V V@0G0x*TNt^Vh.L : L u*t*$$hu x*h-I-w*h F0 "the teams are locked"M0Z Crx*,w, `wwt`&f`w`&d`d`3ww&6l `dZ 6k `dY 6j `dX 6i `dW xZ Y X W  T0v\jh L>h /a0 :xRQwx*R10wx*v%vwxF6l vd6k vd6j vd6i vdvew% hL I0J0s*caQr .: r Ws*r lr E r  N0r j0R0A~ Q0V0OxdTq .: A $q q q Q(q  q p0r*^"IL  Ly*}*@+F+r*$LK+ U0Y0N,hT.: o .P BBo mo -(T Oo o  TP0K0# "Failed to change name, %1."X0p*AkeS.: N.Pp*BBS ONN [0! "name change has been disabled"SZ1Z0A=6' ]0. "Invalid Nexgen command or parameters."`0b0a0' "Failed to execute command, %1."d0 "internal error"e0# "Failed to switch team, %1."f0 "spectators can't switch team"B0g n1 a| [m `*^*\?zw\*\&<g C g acm \\A) c0@o0}o`-\ }-F-\ }a v{^ ] f}-|] |}va v{^ C|!v{ g0- "team switching is disabled on this server"k0+ "you are not allowed to change from team"l0 "team doesn't exist"m0 "you are already on %1"x0e > L-b t!B4-b e d tJCd te * M1idi  id*f*g*o* fi$p ?wp *p $iq*p p AU i0n0( "this server doesn't allow spectators"v0w0@@xf f'5f rx*\3x tf  z0@@b*a*qg^Ha*rHe_*HQ Hz nameHX Hz titleHw  Hz ipHE Hz idHA  Hz  countryHqI Hz teamH t0! "all spectator slots are taken"|01 "you do not have playing rights on this server"}0 "all player slots are taken"H~0% "Failed to balance teams, %1."{0[*peX[* _Z*]wX*[ Hknamen{[XQ[[ Hktitle{[XX[[ Hk country{[XA [Xm[ Hkteam]{[XqI[X @11 "this command is only available for team games"B1 "team balancing is disabled"C1" "the teams are already balanced"XD1# "%1 has balanced the teams."F1! "%1 has started the game."A1U*@sjF .:U*F WT*F E F F #F  G1 "%1 is READY!"I1' "The game has been forced to an end."F r0O1Myl  lV*W*X*\* fl$-N-c*MMM-Bq ?wq *-Nq -_ q $l]*q q A L1X1XU wq*qaq* [1q Cr{*,z, lzztl&flzl&olol3zz&6E!loq 6D!lop 6C!loo 6B!lon {q p o n  A "|"Q1 "keyname"R1 "keybinding"S1 "set input"rT1V#$lA Keybinds'$',, ,  Balance teams Switch to red Switch to blue Switch to green Switch to yellow Suicide Open map vote Open control panel Pause gameV%V, VAbVA$VA-(VA-'VA-'VA-'VAVVAVB h0y IE 0d%The game has been forced to an end.pwp*p-pq!Hpqppc-'-'Ba/!e.@@/a0 y !Yy J *10 U1mBS m%m,nkeynameSm{nKkeybindingnV %V , DKV PV AnV m a1R*YgZa EZ\"R*Z["S*ZZ"ZY"ZX"ZW"ZV"ZU"ZT"rq*qZe\ qQw\ D*\ \ D&\ DZ u1N1@"0' i1s\i@!L>@!/a0 :{RQw{*R10w{*s%sz{F6E!so6D!so6C!so6B!sosez% W1RDuRRNP*{N -Vp~N,dp%UNNUNpNNp},-V~RU%-V U]1g1Q*ZQ* $-G 1$-t F$-J [$-J p$-s $-P ' @`1D\1q/DqG-:G,wq*qa/!I Z .q-R vZ vA-(-R(-RvA-(Z A-'vZ Z A-'vZ -R'---RvA-(-R( ^1c1 @p1O*p{ZO* !$ [ EVENT ] ;$ [MESSAGE] U$ [ SAY ] o$ [TEAMSAY] $ [ PMSAY ] $ [ ADMIN ]  [NSC-SYS] n*d1N*WrkeynameSN*-R {rEvAvPGrvPvA-(vAr-R( M2e1A~ k1rf1h1EE;e~keybindingEMM*1{MM~M,{M%sMMsMMMMM},M~~s.M%~p~M~M}s.z~M}||~p~M~M}|>set inputE~ l1J1 "Root Admin"q1WbZQ Tr*-Y :Q %pQ W!x r*-t  rr*YWQ wr*r\WQ  s1vF:P-_3:4&:]%3:4,x%4$.-(.` `NN:4&:]%4$x:] y1r13"r*(  m1DG)%h keybindingD[~t,%wt~t,fwtz  h h wh pph |wset inputDh  v1n1C K,' w1H( J*`N' o1 "Spectator"L2y[ Fyj:yv@(X@( yn?%v@'X@ry*RA!PyB A!c A!^;vv@ya ?%SyI?%<A!P A!c A!^ rL**X@yI?%$ry*RA!PyB A!c A!^ x1, "The server has prevented you from dying."z1@C |1{1/ "Unable to send message, you are muted."s 1 "Account type %1"A2* "%1 had a team kill attempt against %2."C2 "HTTP client error: %1"JF*G2V E2 "[NSC-SYS]"X2\Bts r\ws * y!u[s [y~;~s ,d;qs s 9$*The server has prevented you from dying.'>x,wx%*4x%B\[yz'xNr*(rB\[yz W2|o`-s |-F-s |x mqu t f|-rt r|vx mqu Cr!mq J2 "[ EVENT ]"{ @P2@Q2@N2 "[MESSAGE]"S2 "[ SAY ]"R2T2 "[TEAMSAY]"Z2} > L-z i!B4-z } | iJC| i} * a2q G9^rD Lw^* A!u[^iAq _s;_q %;sq^^9$*The server has prevented you from dying.LwD i"- wi*(ia/!{ia/!e 3:D :i^I<X L;_q %^a <~L-j d(%1 had a team kill attempt against %2.iD  i~,w~%*~%$Gq D ipqA~Lw*$Gq D ipqA V2 "[ PMSAY ]"{~ f'5~ r{*\3{ t~  V1 Pj hnH{,w{%*{%P A!{lw*P A! Y2 "[ ADMIN ]"U2\2 "Allowed to play on server."^2 "VIP slot access."_2 "Admin slot access."l2@bWF-u&@`@?5@@x?&@@x@`@?@`-b':-CC+team switching is disabled on this servert-GCthe teams are locked@-yC)you are not allowed to change from team-b'\-b@j:@@cYB,wB%*B% b@B@z=@j^@!Failed to switch team, %1.C  22Ab2?mes. ?,?, V#?,?,A c2V(4 d2x)4<d.wd*Qd-f wQ*-fwQQQ-QQK-f.N-x) `2 "Password immune."y2]2^3 h2@i2@j2@m2M\&+OM]M?M]-i'-e'-Akname change has been disabled-i'-iEM/M/M4D,wD%*D%\ME-eD-e({EMd)%1 has changed his/her name to %2.EM/ M)MjM/^M!Failed to change name, %1.k n2U[zBWUUDCQUUFd @F,wF%*F% [UF\d(-%2) %1U/U{6 %1 has left the server.U/-G-] w?*:4,:d %U-Fserver{:d %4-_-^:4&lSUa E3h hCG,wG%*G% hh G'-C:4,-_h Y$JThe server was restarted by an administrator and has been reloaded!'h C$4The server has crashed and has been reloaded!-Yh c$TThe Nexgen boot sequence has failed, please contact the server administrator!:4&fh  k2g  4.0p2 6.0q2 288.0Ar2?#Xg . ?,?,@. ?,@A?,@-'H@@j@ s2w)j >* h?, j H8j j @-f'g hj -f@#Aj @ A@A@g @w)v)t) f2 "Can be idle."u2 "Game supervisor."[2J!H 9kJ!get Engine.GameEngine ServerActors-{~J!.UTPURESA"%-{M 6%1 has been detected, entering compatibility mode...UTPure o2q)x2K}' t2s)l _ s)r) v2 "Setup matches."|2 "Moderate."j*{22yo @3A3K*~2es:rI* W NN|W ]jeIW ]W ]a/!FI.;W ] seW I }2 "Ban operator."IM3~V~A??~>~-Fa/!_-G-[>-g K,$K.4~~/K_%-g'1:~K~ z=K;Kz~j:~~c~-^ ':4,~-F~n<}~~Fd K%^K,wK%*K% V~Kd(+%2) %1~/~{ ~-_-^:4&S I*B3p)!{` (`_^-] n Nn ]a/!F!|.;n ]j_-]'V.;n ]!p)lm^n *N, R le*R ?%R CV.g 4l@jg ?,@R N]V{mVjmVV#HR N%H@jR @NV C3 "Manage accounts."Z3I!Cr*,~, r~~tr&fr~r&urur3~~&6]!ruI!6\!ruH!6[!ruG!6Z!ruF!I!H!G!F! Jc#/a0 JwJ*10grJ*CRITICAL EXCEPTION, Nexgen controller not detected.a ~J-Ba JLJ4 JLoading %1 %2...n)aX|UJvJXJ@ J&Failed to register %1, destroying...VaJZJB J(Failed to initialize %1, destroying...VaJlJMJq a@VS3kDVHv-_.-:4%4$`JrkwJ*J]L,wL%*L% VJL'L,wL%*L% DkLFw* Dk H3 "Server administrator."A. 2.0];O3 0.70X3KW7S&r?K?KAT ?-Z wT *rT AK-Z'T AKAT T A1 E*G3Ym -o  Q NE|Q ]jY-o 'Q ]a/!F-o .;Q ] mY-o @Q ]Q -o  ^3|Nn6F!:|:[|['DB:|:\|\'D( Q c3nU".Wnnana C*T3m)3$<L%:LNL] 3m)L a3`\pY!L>Y!/a0 :RQw*R10w*`%`~F6]!`u6\!`u6[!`u6Z!`u`e~% R3 2A*Y3l)L#&<M%:MNM] Ll)M m3}RU2B}V}V'@>}W}W'@( )\3k)<8G*FN%DNNN]<k)j)i)N ]3N4`3A~ N3 "Ban registered players."l3smq~f-r wq*O|qR"s\-r'cqqQ" -rqa SqR"s\qQ"~~qsDq {)_3h)TI,AW%?WNW]Th)g)W b3 "Hidden admin."e3( "Press [Fire] to start the game."qr#K',T j3,Nexgen Server Controller'$versionU _,buildS U# $Copyright 2006-2008 Zeropoint productions $d.scheerens@gmail.com $Development'$Daan "Defrost" Scheerens $Credits and thanks to'$Mickal "ATHoS" DEHEZ $Matthew "MSuLL" Sullivan $David "The_Dave" Schwartzstein $Zohar "SuB" Zada $*TNT*CryptKeeper $[BOSS]Snipes $ 6-k3*./"0"+","1*iy;*"غ-D@VdfwȽpnbZpÿnKNbnZXPXXSSXZXSKNKIDDDKXSSSSKICAA""A"A"AAAQY\[EEE"RRRRUQWW[W\))****BBBDDV[[enunuwww|~|~ddofpppp}}~}~~~}}pwpppfoîx~xwwwuGFFFFFFDFDDDDBBBBBBB***)*)DLKKKKKGKGGGGGEDDFSKJHe­ׅܹ{؎uy󬀣zyeyTXczzUcUQQQGEUQGGEB\\EGD"QURUVBW[[[eBFLV[gxKѾߎþЮߧڧ~ߎuwuwZeneeewLVVVRVTLLRLGgLgvxvWg[UKD[NJ5.fzÎǫ|unSYyyxUcUQQGQEQQGGEA\\EG"BRUTUWAW[eggDELR[euDìwЬо~~йѐuufw|Teee[[uLVVTRTLLLRLFWRexg|vxU\WRKCbJ.++wأɰǨ⺩ynXUyczT\SQQGQAUGGGEA\\EGQTUUWWBU\eguBDLR[guGì~ЏߥϏڅϹ{~ufuu|Leee[[eKVTTLTLLLLLFWL[vgxxvuQx\VLI*N.+ 嬰ΫǯۯyXXXyYnzzYITQQKE"AGKGEAA\\\IARUUTD"D[geT))LRVefDg|оw~Ў؏o~o徧Ⴥ~{~{wKnefdKFZZWVVDFTLLKKLKKB*KK[|gKTxxugKK\[TKC(H+#װɴۨnNDSNybyySYKDID"GECAI"DKE\[)DBAKFA"VRKLKBBFFNK.),JNZd{zw~{~}}ϊ}ϊ~}~~ஹᮏڎp~~~~DFZVKVVDKPFKL.BFKDDKD*DKD)TLK|eTuXFTYKIKC+! ⯫պPNIHrynbXX[SKDC"EDDDA)DCIICL"CC*,"JHKLKNDY)DD.# '44}d}oϊ}}ooޫ}~ndn{{~XVJKNLPLDHINKKK*.DD.)FIFFDFLn[ZTKFXVLKID., Ư믗PNP,bbbXXNSNIHC)DCCCCD)IHIC.! DJKN.Z).F( #334P3'M^s_qdP_}omϤZ~tmZtLoorKPFJKJKMDFJFJHK,.DD).FD5D5DZXLNLSJLKPIH).( ӞƴկėOJM.N_PPPMNIHC,))H)))!C!D.  ,HFJ.P(,D+ &'&3mts&''''3#4Mh}sP_}PoiPP}mtttmPpNZmZ9K.JHJHF.HHFHDJ!...)(F..*.DNLNKKJHKNHFD(- ŵʖ쳳ŚҴėՙM9H59PNNNHMHHC(().)(!)(.,$HHJ,P!,.+ &&&0trPi&0'0''31'1kPsOis}sOdPOt_ttstsZ_osNOPJPPZ9H,HH5HH-.5...H!,,,).,,,,,NJIJJF.NHJH,(( ִĖ՝֞řėiaššęk_55+5PNMJ5HJH.!!(),!(!!C(  .(((N!+,$  %&&OJPO�''11114li}_is9ssssiMOPMimtPiOmmmPqPOmMMMJJPN.,,(-5.5)!(...((!(,,,+(,HJF.(.5JD,(, 읖ֱ웙⛞ů띙_kikՠ떕kP]M55H]P9HNPJ5)! !!,!)(!,!  (.J5.!!((    qtt;'0# #&#&&ii}ssMoiiPP9JMmP}imttPP_mmssNJPMPPJM..5H+.(,5H!!.!+.,(!,,!((!HF5,.,NHHD.,., 얞䞳ĕ䝝֞ijį_엛_ii֙ęՕO]NJMH5PJHONNH$  !(C()!!!J    HD.JJ+,$  &'s4'1#&0'#'1'4_08hPOPi_PPO9o_mssoJqmmsqMMNOPdH5.5HH(....5H!(.,-H.,+,!,..JI,JNHJH.(K,,$ ힳ쳳䞖❛k՛ճPs톙ik_k뙝ԝ]OMJOH7MMqNM;&  !(,((!!!D$ ...JM+(( %&0r}0'#0'0'111<81'0#8iMPsPPOP9}PqsqsJqmmqimMNMNPm55D5.J(-....J!,,,,.,+)+!.(.JJ,NHJHHD(J.,( ųijl䝛kӗʝPqq흗k_햙ĝkkOOOMN9JM_M713&  !.("!!H(    +..HJ(+)$  %&0tts#&'01''11hJOP0'0'00';iMO_PPOO9sPsoqqMdmqmmMJMNONmH.5.5F,....J!.,,,,+,+,(!,).JF.NMHHH.,J5,+ ų䳳kձaț_iqq񝗗ęi_9P]M]JMNJ6444&  (,((!!.) $,.H9(((( % 0siN9&&''1'4'4lii_OM<1'0'#0&0&'5OPPOO5d_qosmqmJ_smmiMJJNMNPH.5.55!(....,!!,-,+)+)(!(+.FM(PHHHH,,H5.+  ҙ쳳͖͝_䁈PsONqq_kkęP^_^OMMMM_r^44;   !,((,,( ..5!(!! #&0PP<'0&'111#114sssO410&&0#&&#&<9OOOJ9PitiOmoPimMPmP_ZJ9NONJ_+(...,((..!+.!(((((!+)!(.J,H,HHH.5J.J5+$ ųŴֱ끖sq_m_qaikaki__MOMM_kk6;1&   !!!!C,) !,(5+!(  &0&mP}q8''1 &&000'_s'10&000&##%0isPJJ9JiPsPmttiPPmmsJJPP_JMHMJNPH(!-,.5..(,,,.,((((,,)(!..NJHHHHMNNMHM33  lij񗁈sqsq񗖈sN_tkkkiO_]qkqa44  !)(!,,,  (J5(!($  &0&tPJMNq301'0'''1;s4000&0&&4osPMMJ9___sostMmm_qoOmPPJNNJMPN.M!....H!+..,-.!((+.(+()-(XHIJJJHiP__]MM3   ų䳳䳖qsqis웗ti_웕k^q^_q]kq7%0   (+(,),  H(-((!  #&0&HPt0''0'1141^Oi'0'&0000&&OioPJM99dPioqqMd_m_mPPPPJJPHPON.J!.....)-,,,,(((.)((.(+NHJJMMJbŝ>1 ű󛖳lkOOPOP__񗗁i흆iaiakkklkkak>;>^i_MP_k_1  !),)  5(-  &&MPs0'0&'1'1#14h_O1000%0&0#OM95555-_$8simmtMPiPPsNPPPH55J5D9(+!!(!((!!!!!!!((.,.5HJMk__^N;7&  ŝlkikMPOPPPi񗖁is헖a_aiakkklkk?;;:a_MM]__4 !)((+--  q-&0''1114lsOJJP;000&0%'OO5555..i9#&Ji_mJNiPP_JJONH5HJ5.J(.!(((-!(!!!!((!..HJJMqk__^^]60  ӳ󳳚li__9hPPOP_s`>>:?]M^qiOM5 !!, -!.-   & O}M90&&&0'1141isO#00000%&%5OM55.55.O#%##'MP_OMP_MNPJJ5HJ55.J.(++$.!(!(!!!(!!(+!5,9HPkkk__kƝ?1 Ş󴞝liMOMsOPPPsʗՁk^OO^_kkla``?`>;??]k__aN5.J   !)()+!5+ &&OPt4&'&&'1'1#16is}}<##0&0#&&&#JMM555..9'####-PiPOJNNNMN5J5D.5M(,+)+.((,+((!!!(!((!!+(!,HJHPkb__]7&  llkOMOMP___˖▖P_]MM]ikkk````?^?`>;;kq_PM.NJ+  (!(! $!--  %%qZOJMt}&0&0'#%&1_s}4&0&&%8J9O.55.5-##&9POMMJMMF5J5.5.J,,,+,5(((().((!((!!++!,!,HJJkqq_]M6&  󞝝l䖁____O_iks՗՗얕O^M9MMai?;:;`P]NH,J.5    !!!!5-   &&m9PoiNi&&&'%&010#1^ssi&&%#&%&MMOO55..9&JJJJJJHJ5.5.J5,,,,.,(,)+(.(((()(,+!!,!.JMMqka_];;?1 ų읖i_OOOik՝욙lks__hqƖjj^??>;>?kOH.+!!(  !   $HO5..+  (5JJJ99#### ##%&&<sis^'%&&&0#0&#OJMJOMOPM &.HH55.-,.-,.+!H55J5H,....D!(.,,,5.,,!,,(,..M]_q]OMM] Ğs_iŠĠ띕k_q흝`^`>>^;;_.)(!! ,  $MMJM..    .9FJJM+ ###%%&Oss}'0'0'000&%&%9P_PPimii0 ..+.5D.J((,.(!$.HNJJJ,HJJH,N.,HHJ.HD()H,H(,+,MPa]MM;_$ Şlsl̝q얝Ԝj```?;6'5(H.(!)   .MPMH-   .9HJ9J# ##%&&isiss_00&0000&%&&#P_PPOmiP9#&   $59J+(-9...(.((.PNHHJNP..FJHNN,HHJH..JNHH,(!(O]Jk_MMa5& 鳝lҖř̘j^j;640&M.,)(. !    PPPMH.     +55H9J%#%0_siP<'1&0000&%&&'_PiPOoi_&#% 59HHN(J5..5+(((.Pm5JNNNPHHJJNJXJHNJDHKJNJ.(HJJPM_kJa53$̝͖qⳝj`443&.,,((!)$ !   MPOMJ.   +.J9J.'POOi'00&000&%&&%9_PiOPms8#&# $HHH5J-....H+!+(.PZ.NNNNNZ.NKMJX.MJJ.HJNHJIJHMP]kaaJ+3 ՞`k볠?;40& .,()!,,  !+   5MOPJH     +++-,+  '_O9_O0&&&%&%%#%&&PPiPOPiM%%%  #-HH5HM-...5+5+!9NP,NNNNMZ+KMKPN5HJI,NNJIJJJHJ__PM5+ŝj;ik❙j>;: 0 $,(,,!  !!,   +H_PJ5  -HONP-% &%&&&&0OO?666&1141#009sim_P'#&%  -.9HH5J(..,$ $(-.MN,HNNNNZ(NJPJX(.JJ(PJIHJJJHM_^_]5.$ҳ?;6iƝՒ`>74&%  +)((( .)   $.k_P+# # -MNPO-##%#&&&0&iis4441144110%'smidiO%&&& #.,(55J5.+($  +!.NNZ5,KNJH.,NJNJH).)5DHHHD(.HM7MOM.( ųš̓j>;6:ŝę>64&&& # !.)  ,!!C,    ,MJ5.   JOPOP'&&0&&}^'11&412'11'0_Pismi_'&%#  .9MN,.9.HH5  !(NPSmN.JJFPbH.HDJN(NNHD.).S.,5Okq_]5+-ŝšŠj`?;664^⛛ij]0&& & H)+)!+  ,.D  5__H,-4(-9.4$$'JOONP### %&&0&4O84440%44111%9qiqi8&%&#%# #JJJJP.5HH5 (P_NNXZ,PNNNb(HJHF_NHJHH(SM,5M,___PJ. ҝl鞗֝Ğ֝j`j>>>6644;ŗq0 #   .,)+(.  +..$ $-5H,Mi_JHN_.OPHP55M59Odim9-8''##&0&8_44644064441%tims%&& %  -JJJJP(H5.#   ONNNPJHNNMNX(HHHNPNJJMJ,PN.JM7___].+𱖈ih❚қ͖`?`?>;>6:64:kPtqq9#  ++)(),   .C+..HIH,,,.HN__JMHJ.HMJ_5HMJJ-...._mimmi;309_9OM<4444410%&%&8si3%#% % -JOJMJ(H5    #HNNX.HNN_JX!HINJNNNNNM5bNMMJ-P___.(յҵŴhӚkl??``?>>>;6666:kaNmbPb  (,(.$ !.),)HHH,H5HHHN__NMHM.MMHP55MJ.5JMMJPimmisOs}s&64444&0000#htsO%%&% 5.JJ9MJ,($   5NP..H_KHN(.JJJHJPb_NJb_MJ55__aM,+ ҴŵʵsՈ՚s֝``jj???>6:868OPqJ_NMM.    ((( !((HJ...,.IJIHJNP_NM._.MNJP5HPJ,JJMJJ5osi}Oi&6464400000&mi'#&#&#%-JJ55H.JM-    +ODMNJ.JC.H5555JO___J55NMaJ5+!+ҵŵŵ䁁Ӗiӗ_՝j??>:`6'3__tHPMNJ    .)!!(  !(!IHHDH,)HJJJJKM___DJP.MNMMH-.+9JJMMONJiosPtsil;0&1000&8sO]%##%& &&$JJOH5.HH-    5.JJPN)-.DJHX,MPkėqHOPa^M7M5+ ȵŵݖklsՈ̘j`?`>40&3ZP9NNJN.%   .(!!+ .,HD5HDMHHIJJN,9J_MJNP.HPMM(-9.HJJMMN55559is}ssi;4000&_sssiO0#%& &#& HJOJ(JHJ.   N.NHPHI5)HH.HN.NaՙMabaO]]M+( ӞҞҳ◖՗k՝jj>:1&& 4PbMHPMJM+  (!!!!($(!+HDHDHHMJDJJKN,NJi_JJP.H_JM.J955JJMMO.NOPPOOik81's};## %%%% MJPJ9JJ5#   HJHHOHHJN,..C5N.Pk]_MMMN].($ ų֛⚈ț?:40% #5PiJJPMJJ  !!!!((++HD5DHHIPJ,,X.HMJq_M.b.J_HP.M5JJ.Omi.9OOPOih__sOi&##&# %&#+NM(-MHH$  JJ,MDHJHM!,..DH.O_MJ7H^J-+$+ ҳ⚁֝llk֝`;:61&&5PM5HN5H+   !!-!.D.DHHDP.,.,HIJNk_NHH9HOMP5J9JJ-..-JOPP_iOOJOiiMMMsisi_s8% ##&%%-55,5(JJ$   ,5HM,(5DH,,!),.,MMkM7J.57M]53++& Ş֝ųŞ]i՞j>6110& %#JPiHJHHJ   (!!!($,!HDHJX-HJJH,JNq_N5N9JPMP5M9J.9JMNJOOP_s^issssiiii0#%#%#%%#H.MJM.+-    5J.DJ(5)..D,(,.H__͝PJ5..MJ9754733 ҳ̳֞;<ݚ靖`>:4&&&%%%#MNs5MJF+  !!((!!.(!DMKNHHJIJJJHPq5JNJJ_JOHMM55MMMOOMOdssOlsssPss8## %&%&-.MHMHH   !J.,H,M(,,(H!,.MMՠM7H5]5_JJM;];7: ҝj>::_kśkk440%% %##$MmJJM9H# 5!!!!!!,(!(!(.MIJJNN,J^NJNNMJ_PP.95HJMNONPPJPOissssssiP_P' %$+5MMHH-    -,,5,.D(( .!)5J]PkkJ593M_J7J]^a>̓j>:6;llhkq<& %##&NOHMHH5  (+.!!!!!+-).(HHHHMJJJN.HP_qNJPJMiPP9MMJ.O_OPPMMi__ssssoiO_9#%%% O59JJH.    (,5).,. .H.J_ab9M55]7559]a`=/ j>;:6;hs_OMlP4#%+5MHJ5H$+.((,.!DHHIJNXXPXHNPikPNPNPqN_JPMOJPsmqiJ_s_kOssqsmMdPM9##-PM.OMJ#    (.).((  +(.JPk͝O7J75--.5=6/ `::::8^ihMO^m9PiO&-MJ.J5,-!!($-$+!!,(DHHJN.H,H.JNPqqbbJPMi_rM_PPPMOMJPiissl_P_siP_Ossis_isismPOMOO9' N_J5((    +!,(( (.9_͙k^MM5+((+35]aj=/잠j`;6:6:64Ph9M;MsPsP&#&#&%#MHMJJ.J.,+.+.!,(!!(!(!!+!!,HHIN.MPNNNNPbqrPPbNkPiJOMNMM_ONOPPiisl_iPkOii_OPssidisi_iPiMJMJMO_OPM__.M.   ++(!J((   -.MOPM,+!!(5JM^k=ɱŞ霓jj?;6>>;14_sPisM59O5% #  J5-.,!NJJJNJ5q5D.H(ND.5..(,H.+5!!,.H.(JJHHJ.JPP_JPOJiMPPtl_i_OMOOOOM9O9.---.59.-JOJOM5### (JM,,.,H(!9__MHH+NJJM_akkj>/ ɱšճųŒjj``>>8:::10'OkPi9599O.#  #   3!M-5JJPJJN+sH.D..J..,HJ(()M,5((!!),DH_JNMNNJP_X__NsPiqPhiMOPOP^O5JM--,.5-55-.5-M5  (.9)(5,5, 5]kPkNHH.),HHMOaaa?6 ۴š՞ųj^????>>:::410&%Ji_OM9JMO-  #+O(555!MNJNJN,bM55.5JD.HH5HC,H.5J!,).D.IJP_NXNNPqiqrbmtss__M___OPOO55---9+...-95.-+ !.5+,5)5)  3MkʕaMrJM..,,HHMN_ka^6 ՞Şh;>??>;:::810&##8JOO59MO#   #..-+(9(-,JJPH.H5JOD,,J..J,JN5H5MHN((,JDHJJNbqbb_brrrȱȼҼsiP__s_PMM9J-,5..+.-9.9-5JH#   !.5(,H,5(   Ma̙kM7MM..,(+.HM_akkj?% ȵġ𞞠Ğk:>>;::::610&%#8_JJ^95JO&  #..-,J.(9.JH5HMNMJJM..JJ.D.JMMPIH_JP,..NJNNSPbrbrqtӲʼȪ}sqi_^s_OJMJ--9+.+-5.9+(.M.9    -5((H,5, (H5]^M_H5,,(HHHJM_= ݲȴŴŚ^?>:>;>;41&&#&$^JJO99MO#  -O9-9.-9H.95NJHPNHMHJ_NMHMHHNNP_PH_PbPINbbbybƪɼӴӼisPO9_OMJ.9J9+(..MJ55NOPN_-$.J,(,D5(  ++]_kkaJJH.,JHHMO_j=/ ɵɴҵҳl?;>>>:410&%#&#OJ9OJJMM# '5J999..-J-J5OJHNOJJMJqJJNNMJJ__bNbrkSXby˫ƻʺɼӱsi_9POJ9J9.55M+PH5JJNNbPP. 5J,,.(H+  (+5M_k̙OMMJ5.HHMNk?62  ޵ұ?>?;>:610&###%%&9OJ^J9O^+O59999M..M-,OJOJPJNJNH__MPNPNPrXbryƻȪsmiOJP_O.95.J9M+MOJJmbPPNi. HM.,.,,   !!-]kř__PMJ5JMMj=/  伵Şl?;>>;:40&%#% 9OJ^JJO--99O5-5M.M-.5.5_HMJNONJMNsNPbbrPXqrκdzk^?:2022216>`jݱs_PN5955.5J9J9,.!-JmPSNN+ ..J,5,,  !.]ka_PHJM_k=  l>?j:810&%#% 4OJ_99_9.9J95-95.M-,55MMPJMNMJP_PXsbbƣƇƝ`@2=/// 6⵴tiPON_9.-5MJ9.+J5JO.bPNNP555J.5H,. !!5]_MP]Oabkikj?/  Ȟj?:61'0%%##4iJOJMiO559MMP5595-,JmNPMJJrmb_rۯ˰Ơ==222222////////// 2似_OiO9.-9JM5.9HJ5NJHbb_HMMNJ,.H.   -_iqP_k_k^:%  Ӽұj>640&%%%&#8Oi9Js999JM5P99^..MZOqP_Nqmmsư˰ꢟ@@@=@=@@@@=222/2/22/2/  6ӽȵPOOm_P.559M,,.HJ5JJPJJ.H,ONMJHJ,(   !,9MkʖkaqkP_P_akk`:% ۶ɳ̢硜ȝҪ^611&&%%19MO_JOOO99OHM99J^.J_PPsqPmtꢟ@=22/// 2lɵqO_N..M99-9(!OJ.JPqHJPJHPMHJ.+   ,+-J_̓_ai__i_j^/  ת̟űյ<1'00%#10&%4MJOiiP5PM9JOM.J9M^J_mqqsqꢢ@=2  ?ȴss_s_5J5M..9,+MHHJqJNPPN5PNPHH      !!!(!+H>`jaOk_aqakŢ=  ɪšjŝh00&&%&#.OMMOO9O5.OO95O..MM9_PmsbȻ͢@=2 >ұsk1&&&&##8OOMMJOOMOO5OM99_-.M_isbst롑2/ 2@=2 %26:?h<;6:21& ^m_OMM9.59.5.5++9(,_PPPNHHH,((H,    4>`]O_ab_@:/ ȯҠjjj```?^??^>`ȴs1&%0#'iOOO9JMO_9OP9_9JJiOO_iqtԑ2%; / ;lȵqOP_99-.59.5,-P.JMJXiPJ5H..,,..)  (- &:``_kbkaۖꡑ=0  kҜ`>????66>>><>^j?>;:6'6Ossh#%MiOOO_O9iOOOJJhMOM9iMO_iqʽ̑2  4ռʴ_Mq_O.5,55,,+OHH9HNOHNJH,J..,.5("! ()!( &:jj`kkȖaa;6 ɪȴj>::::?^j?>;:44144:ssssh<}<_iOO_MMi__MMMi9_MOO_sȰ՘= l%ֵŝimOON9.5-5.,M!MMHP_HNMHHHHJKH5!(!!( .,++   4?`?qkk^]& j?::;>>^>:>8641%%&#%8}OMhPiOi_O_MMMJ9_OO_OiOiim͑/ aί յӳқibi__5.5+95,.],..!(..PMHFJ.(!+.!,(,+!!!(!(  &6?j`??;kq͕͖M950 Ӫ훗kœ?::>;``>4:6411%&&&&%1;ss_i_OsiOi_OOiOOMJ_OOiPi@ư&ȵұňii_q_OJO..-..M-5+5HMH5NNHJH),JH(,.!!!,,".04:>;:;]]ȕƕk_OM7+ }l֙h;?`?>;61841&&&&&&%&01OsiiPhsiiPhO_OOMmsPPiͼ@ ^ǻưջǴ?˯ƱȱŵʳʛŴŪs_riiJO9H9(._5H,59M.JN,P_N(.M.)(((,)(,!,,!!!()$  0014]J_kՕˆP]M5+ d훚šk^>:8161''&%&%&%#0&&&'PPiiOOisiPhPhOiOOi_Piiż= ŰĩͰ˕kʺůĪűҝŵűұsi__kiiMOMM9,+^.,55.M.HM..SHHJ5.,)((()(((!+!(,+(   +-M]MʖkkOOJ5&Tkl;6000400&0#%#%#%0%&&0is9MOhiP_hiPOihPOsii_sst=  ˺ƺůư;ƺƯŚŞiOOk___kNMM95-+P..9H9M(,+!,PJHNN.,!+(,,+(((  --..Oakq_J53 ZҞ_^4'#%#%# #4Jssslsisiʵ= ˺ĨįƯǰ ʯěk_lksi_qq_PJqJM]JMNMP,_PNJM),.7,+(,+5 -9.+5]OMO^___OM59-+ wlisii8##&%##OOss2 4ɴqũưůėiillh_tsPbMJP__iH_rPXJ.,HOH..((+!   -J5,HM9MM9__^__^J9.+#uν󲝨ĞOiss__O&# #-9Mis2 6˯bůęikkhh__qPkibOb_biJPXqbHH5].!!(((!-    +95,JJ5.5JOJP_^__M953e񯝛jŞkq___PiiPO4& &O99Ps= 6͵kqģņ_llklslkkhiqqsPiqqaNrqbrbbNiN.(,.)!((!!(!(    $H.(9H(+,,.JOJ^_k^_^OM5-&e񯛗Ş_OMPPPPO9ss;###OOJ9O}= :ʯkqqģįĩěl_kkllll_sPqbqrrrqrrNMS_JH+,+(+!+!((!!    -9J.MOJJ5D5H5XO9_^;^kM]M73 e񯛕jԙiO9JOMJ.miiPO' 8PMJ9M}i@ 0ɵƛPk_qk^illlllik_rrq_N__NINHH,!+,!(!!   995MOJNH,JJ.H9J_5:;;>aia_M;8& [jj^iOPO595-JPPPiiO-#MMOO9MPP_isӳƛqii_kr_kh_aklllliklir_PrXPPNM((.5+.  7J5__JMM5H5.,HHJ5406:;>lk^^]80 W`;6&-+_iPPPJPPPP9o<%8OMJ9MOsss= ʪa_iaqk^lklklhlqkiưybrrb_CNNNM(!(+!.,,(,$&;bMaqPa_bMONJJ59+ 10;>>>kkh> W񳛗^``:4&-O_P9NPOH.PPPM'OOOO95isssist Ũ_i_kkhlkkklkl_ƴrNJXPM,,,5-((((5,7O_kis_i_M5% #&00:>;;lkl=  V|h:>`j>61$5M9--.PMMMPO.MO99MOsssissqȱ@ ȯś__a__k^lkhlkhlkqİưǻǰbSPqqJHDJ,(!((((,535_kqk?61 & &11>:?>^kȕj=/  Vq?>?`^j?;40% 4PsP9MMMM5O_OOP9Mssiʼ2 ųkikk__kalkaʯƳƺۯPNPP5,.)!!!+((.PNMP__ęšj?600&&041>>>;`ȕ?6/ V퉆_O446:?``j?>:4'& ##8.9MMJ.J99MJ9OsssPs}֟=0śqkklalllaůƺ˰۴= krXbN,.)(!((!(!--H]MP_`>4&&1&146>;>?klꡑ=0  V_M013446;::410&%  ##4M.5J95559O_iiis@// >մėkkkkkkklhŰŜ@@2 ƩqPJDH,,-!(!!-..JJMO_`:1&&011::?>>aʕl=  Vq400&003&4110&%  # $9M59555..2  յ͙kkalkllkŴ颡@22 6bXMJHH,,-(M5H-++JHJOk`;1&%414;>>>>?ʕjj% V_M4& &0&% # %&  9J-9M55..Oois՚:  2śllƳꢟ@/ κrbPMJH(,-(J5-+(!5HJPa??:'& 444:;>>>>kkka`? Vś_iPJ3+% %   HMM-.999-Md_m_s^  ?ʞ̴= rbMHHM-.9(-,(!5HJO?j̒>610&441:;??askkah6&&%&46:>klkkkia^>> [qPNMHMJ+    +.-5.5-99-5iiPPmmil  /ěĚĞ2 P_M,.-(+((!+-HHJHMa?j瓓`;41%&&014^aakkkkah>> {kPNMJ.HJ...(  -.-(+(.4+9MOPiimqss^///ijĠ̝/6ɩq_P9,()+(.-()JM5_iq;??jjj^?61&0004;9skklkka^? [ʖqPMMIH.5.....,  3+((+(+((5-9iii_ss; 22/ Šĝġ̡̙̠̠Š;ƨNJ5,,+.-(((+MqOO__];;`?`?`>:41&33MiMkkʕkȖʖl`j%V_PJJ5.JH......,(!$   -P__((((+(+5mPiqt4 /==/ /ġ̡̡̡̡̡̞ > 6ƇmPMHJ55.((.99PiJMPiP::>;;>>;643;_bis^ҁkՖ͖=  Vՙ_OJ5.H5D.,...,,.,,$   J5H5O9-((++.PO_sst׼ /=@2 =̡̡ ;k ۰r_PN5..5.9..5_POJOP;4:::;>8?kj^>/  KQRTYDHN     -JJ.5JH_NNNPNJHMobNHJNMPJH+,.HMPlʳ2@`:222ƲPNMOMMO..99JO9O__kO____^P_klh``j```??>la]6&  GKQLFDK,   +-5JJMP_PHHmZPPPHJJNJJH+.5JMPrƝ;Ư 2@ 2 @ 6@2 l⼺sq_PMJOM595JO_MOikO_iqh^^>`???^````^?;:h^;1  GEB"FD$    #$-J5ib_PNP.5H.HJH,.5JNsql6ͼ6 =@=2 2@@Dz_P_POJ_59OM9M_O_^^?j=::>`?>>>jj`?`j??:6>?`?;;:?`>??`j```j^??:6;_<4  QRUUT**    ##JJPP_NPNNt5_qbiP.NN_t=/κ/=@2 @2 /@/ =k⼺m_ZOmMPM_kk>?``;>>`????j?j``k`>?>848aM1  QQRURQ"     MNHNNPPPbb,PP_bPOP_q2ۺtkijŝ /@=/  / @2@/㴼mm_PPaa?```;?``>j`j`?j``?>?>641_;3 QQRRRD     KMJ(NNNNPqHP_bmMHP_s2ɺ2=/ /=22/  碢=2 /@Ƕsslkk횝klj`?j>j`??`j`??j`?;;>>840M<1  KQQQQ"H    $JJD.HJNXXHHJNZPHO_PbȠ2 κqqij 222  `=// 6=ۺȗO_l훚j`````???j`???jj>;;>:;:40<5'  KQQQB.   +J.HJNHN,,JJJS5JJNPqɟ/ ɨiijĴ 22/  =/ `/aӯmiq񛕖`??j??``>`?^;;>;>>>;64<;&  G""QD+   $,HJJKJ,.JNJNP5XJMSmbӟ2k⨳ͳj /// ==2/  2כll`?jj```jj^?`j`?:?;>??`>>;^M0 GKQGAEFDC,),(  .,5IJJN,.MKJNZ5PJNNbqӟ/?ίƯ /22/    2 /=@2  ר񝝳󝝖`jj``jj`?>`?<6>;?k՞i<&%  GQGQGCDD.,),()()   .,DHJJJS.IJJNPHNJNPbr26ȺƳʴ /22/    /@꟔@22 =Ӳ잛𞝝j```j`?`?^?`h]̝k<0% GGQGQE)C)),()()(( H,5DHNK...JKMX.NJNPbr2ʺƯį/ /2@=/  2@@=// /ӯ♝񚛝j?``j```llŝl]0& GGGQGA)!)((((!)!!(C!.,$ (,D.H((,H,HJ,HHN.NNXr/ƯƯ2/2=@/  /222 @ ׼ӛ𛝝j```kllis֞l<4 EGGGEEDD.H(,,.,!!((!C.C,(!!.D5.D(5D5D.).HJNHNJbt2Ǩ=/=@2 2/ //  /^Ӽl쳞𳖖𛝝񝗗_^4  (.H55.H.!,..C.H,D.H.H,HIJNHJNbr2۰ƺ@@/    񡔟@4Ӽ֝䝝󖝝󗚝󛛈_Ŝ^6%  ,5..55N.MMJMMJ5HHD.N(HHJNIHNbr=kܺƯ򑔟@2   /  2222@@/ ӵݞ֝󳖛񳖛񞝚kh6% EDED)),)(!!!)),,D(),..CJ.HHMMPHNP__PMbrr=6򟡢=/  = /    /2/ ȼձ➞Ş쳳엗ss鞚`8 AAAA"   (!(((,C.!,,,D.(,.HJNNNbq˝@=/ /// l  ӵ֞֝l՚kklkslshOO_O̜h:%  AAAA"   +5,,(.5.,,,(D+DJH,JNXr@2/  2 /// : ױՖ֚lh:6::;:6&4&%%&1046?j>^6% AAAA   (H(.5H5H.!.HHHMHMJNbr̠@2 2/ 22/ %  ȵ՝❝Ӛh:::::641%%%&141;?袜``6% DAAA     !(!((((+,,5H5JNbj@a`@@=/ 22 /22// / ȼ❝֛՛l;:6:::41&&%0146>`@`:%  ,!(...-5..5HHMJMJN_?@ %j22@2/ =@ /2=2 6 ӵ֚֞֞l:::::64100%&114>`j`:/ EEGE,    (,((,+,,,(,,,.H5H5HNby==//==/ 2 /=@//   ҵ֝֝Ӛh:666:640&#%&116>`ꡜ`6/ EAEA!!!!!),),C(.IPb:2/ 2@ꢟ@2/ 2 /@@2/ 6 ų֛֝Ֆkh66666610%%#&146;jj:0 EAEA)  !((!!("(!,HC+DJSb6224/2@@22/ 22=@2/  ֝՝֗lk:6666611&&%%%11:>`韑j;/ QQQQC    !(!DHNby6/@   2 2@@2/  ŞŞ֞֝h::46:4400%%%&06?ꢜj?6%  "v\\\A!!!)!HNXr2/=k  22=@2/ ` Ҟ֛֞h:::::6410&%&%04>j`4/ "vv\QC    !!!(,.Prr2/= / 6 2@=2/  ՞ȝl>>:>>:>66414%114>``6% "UQQDY !!!-..+-.H]Jbk62kj  22=@2/  촛k>>:;;;;6441'%%16:`顜`2% "\UIJH5!.H5H.,+5!.!(..(++.($,-+(+!++5HH_r:/@/  2 2@@2/ ^ Ş֚֝llՖkklihOO< == 6/ / 22=@2/   Śśⳝݖ䝱_鞓^1 AvvcNCMH5NNPNNPN(NNNJJ!,(!(!,,.,H(!!!!!((.!,Nbra /@@ =//22/ 2 2@=2/  ̳ŝֳݝ䱝i֡<1& EIznSNIHKJKNNP,NPNNN+H()(((.!!!!,!!()5(,NZr =@/򜔑@2/ 62=@2/ /  Şś䝯ձ쳳kҡ͞<1/ AvvUQ[XSNNMNPPKNN!KJKMI!.()().!!!!!.((()5!,Nb 2=2 ꟔=/ 2 2@=2/ j  4ⳳҞijӛųŝk>1& AEACAvYXSNKHJHKNNI.HKNJ)H!! (!!((!(.H95.Pr  /=2;>@@=/ 22=@2/   ^ųk䝞❞>`jա?2% AASXSNHIJKH.).NI,N!HJH,,,!(,-,+!!.,N& 22/ j͒22==2/ 2/2@@2/  lŝ՛֛h>?``j̘lŞh>1 Avvv\\LSNNKNNNS(NKNJJ(   35HH!(()(.5MMJqbb> /22 0 /2@@/ ///=@@2/  ̞䝯֛k?`jj眔j`?`:1466;`Ŝl?0 Bv\c\RITHH.+  #   #HHJJ(+(,.(5HJ5bPq۝ /2/ / //// 2=2/  ҡ񳝝➙`?jԘ?>:01146>`Ԙ^1/  Bvgc\DU      ..HH(..5H5!.D,PXm/// `?  @2/ ˴ >󞝛❈>?`;6&0016;`^1%  DUQGBKN     JM(,.-(.,.H+5.P /2/ 62  `矑2/ ƴʺ 󝞛䗖;?`;60%04;`Ř?1 DRSx|DK+     $,HHF!.JMP_Mist: //     ꟟ꢑ2 Ƽ/ ŝ͈l>?`>:11&02;`;0 Dgg\\DU-     HHHHHM(MMOON(,Pb/ 6  @2 6 2ҝĝ❛k>`ꠓ`?:0&&&1;`k;& ETUUgD[5   #  #  HMJOPM(5.H5M5Jk    =@/ ۯ= ̙䗗]?`硘j?;10&&&6:`jlh;&  ELR[vFYJ  ## ## OJMHHH.JOM_..Pbt     2 ɯȺ=/ ŝ񛗈;?`ꢘ`>6100&16:?`?`>h]4#  EFEEEDD)      5HHHM5JMJ5HJ5)JNPmt6 / // @/ Ư?/0̖qkihhaŒ?>1&%&&04466194&  EQW\gQeX #  #### $-.HH(HMPONJ+5H5HM,.Pbt ` /2/ 2/ ƺ=/j;640&&&004141O5'  EWW\xF[Z+# #   # 3$(+,(!HHJMP-PMJJJJ!5HHHM,5i /2=// >=/ ˯ƺ:  ۴ę衘?:44000%&&%&3J4$  Ggg\xIeY5  # # '49JP,PH...(-PP_MJ!JJJJMJ!HHJMPM5imt602@@2 =/ ƺ2 jƵŝk<610&&&%%49-$  GxFndJ# '39M5NZbZ_d.PH9MJ5+5JJMJ!5JMMMN9NOOP++_stݯ/2=@2 /@@2/ ˯ͺĝikĝ͞_P^M8'#95-#  GGGKILIZb599H.PPDmHHdmbHMOMJ.,,((.J!.- 5N-5O55JM.M9Jmq2@@2   /=2  ȯ۴ 6˰Ġġ^?^įkqM_P55M.55$# "RLQXHX..!,JJK.HJN-5OJJOH.JKHMHqH.& &&JPP_PbMsPObMHO__ql%@@2   //@2 ͺƯ˴ĝ^O_ai:646>k̝tsm_OJ53#  "unnRB)KJCJJJJ(.J__NM(HJHHNPHNM-5  &&_5PPbiPNZiO5JO_o 򑑔=/  /2==2 ʺ6;44446?왈qi_P5M3  "WWUVLDH.JJJJP.5OJJMNJHF9NP.-& #%&& &4MbiPPm_mMH5JOZi}h4蔑=/       ʩͯĞ̝ęks_kki6410116>jĞ񛕆miP9M3  )eW[WV)FJKJONMK(JNJKM!JHJ.- &# && P_ZsP_dmMHJ9OPms k@2/    ʯ^6ƴŝa___6400016;`̠ﳯm_99-  *vwggVKNNJJKMJNHJMJN,(-#&##&&%&+is_ZJMPmO55.MPmstj2  > ΰƴܻ2Ưĝkakk<4100116:?jj`kk񝛝qO993#  )KD[xZVXNNJ,JMKM(,J.-$  %& #%3JMPPPmqPPN.9OJO_m&2  ˰̩6ĝęa_k_a6644404::>`j``?>;j񝗖iiP93#  *gRG)FeXNNM.HNMNNJ ## & &%% PMbmmmMN9JMPMPm/6/  / ǯƯƴΰ Ɨįękiai^66::6:64:>?`jjj`??>???`뙛񕛕i<4   *|xxuDnZPNJJNMJ- # #& &00# #5JmimqtPOH9MOMOshj  ? ˯ʩkaƕʠ̡̡??``jki]7&%  *x|xKdbXPNH5$ #&&& &# # mMmmqiNP9JMOsOs`?>:1  ƯƯ]㯣ʖi__:446666;4::>;>;>>>;:``^`?j^`kO;&  B||eF,(!,J# # %& &&# #%&MMoiqosOP9JO_i^s  6 ƺưƣ⣗_Ŗka_O;114464646:6::6:::::>```>jjj`h;' B||KZZXN.O$  %##& &39_mPMmqm_PO5OM_PiOi^ 2: ưįǺܗPNk̝_O_]N756446464166464444::>j`?^`jkĖ?6 Bx|ndPNJ.5### ##'9OZPPqsoP__m}_5MhP^msii k%%ʰƩĩbqbqb_ę^PMOPPMM7866:664664668:::>`j`^?ja?4/ Bu\[De{ZZPNJ- ### #%&#-OqoOPmqmstPsssi_OOMPiik 4:l˰ƨƨbq_X_qkřkM]P^__O^ia]>;6:666;:::;;>`j```󝛖k?6% BC{mXPNMN' # ##-OMmmqmtMPmqo_t_siOO9_iisOl >>0ʰP__N_bk̡̡?;`^jj`kŘ^6 DeKdmPNMP5 # #-J_oMmmmqqqPiP3'ti_59M;>60&00:?a__O]M쁖^6 F~|fNdPPJJJJJJ5JMJ-----.JPmiP9'%&&&#&&0&0%&0&&'_sOiOOOOOisssi_ss% ʺůʳ:tqPrP__b9a͕ġĠ`>;4:400&6>j]_OOMM__헗l]6/ F~||wwN....JJ9JJJ5.5-.-.-.O_PO4&# &%&&%#&&00#%&%&;OOMOOsi_isOOűȵ1 ʯǺŴijkrXPk_PNMPbMƙēa>;40664406?j`>;79M9i_]_Ę`6 F|wwwZKPPMH-J9J9J599-.-..-P_5#&%&&&%&&&00&0&00'%001sOPPOsissl? 6˺ưʰƺŴŴĴ?%ƩtXNPr_NO_q˕Ւ?;14&77464:?j`>:666M_____aį`6& FfwfZPVNNJJ.9JJ9559H-.-.-.9& %%&&0#&&&0&1&0&00'%'>;666616;^_aiaĝl^6/ IVLLKVNMJH95...-5555.-..+#%&&&&&#&&0000&0&0014sM_iOsMsi   `Ͱưƺ˴2>ȯqXN_MXH__N_b͖ĕk50&360404776014Ř`6 {VPbmqPMmPPPOJ'# #%#####'OOiiiisissO_m& /˻ƵŴƵҵ/ ƯbPNO.!+MMH]a_MMH$&0336 4666&366;:667^9kqakkah;4 ڃ¶t_Ptt9%## #####&<_POOOiiis_Ȱ]  ?ȴ@/ƴbrb5J.!(M^O]ak]M.N_,3444&06640041:666414__a^kkaa_^^;1  Љtq_mOP###%%%%%####&'OiPssPi6'iȼ  ۵ʼ2 2ۼȴb_J(.M,!(^M_k̜]HMNN_.5314033146474101040HMaʝ?1 ߏPP_o8#%&####%4iOssssOO1'08sssa /ʺ韑2 ǰtrqNq,!.M5H5ak렙j>757PNONq]446444447640& 317MM9˕qk_kJ^O]]7& ߬tOmts0&##%%##%#&OsssPss8&00&&;isq4 2˵ԟ@2ȭPPqNM(!.MM5Pka͙`?`>6&.bNNN__b_M6764467643&&&JM]9_aiPP5M9977%  ڷt_Pi_t<&####%%%%##&08isssM_sss<%0000&hiOsqi˻ۙ 1顔=/ ?ȰqiPP_.M!5MaJaƖkk``;>>4&H___bM__]6667>761&&H5HM-_OMP],55754;0 ­ioqssst4%%#%%&%&%%-OOPssssOMsss8&#00&0%0kOttiqiPttƼa  2蟔@/ 6ۺӼƯtb_PbPNM.M.59a͖j`?;:44&&J_iq7;;>??;4& 3N.H5.MOJJH-5..437  î׮m}i_0&0&&00004ilssiOsOi_O1&&0&&&%0kmit_smqsqiskPiMO_NqƩƫ˺^  2Ԣ@2/ %ɶƲrmPqHNN9H-_OiՙŘ̒`?;:400% 3M͕k_^^?j`?60&.J,(+95MH,(-53$6 ׹_t<10&0046:hsssiOs}0#%%&&%00%&1%&Oliiqmt_s_q5mPOP_ik :̟@2/ 2ӰZNm_iPH9MMšj?;:0 %014:ařk˕j^77JH,5H.+J-,!37  ~mPPm41144:^sissh&0%&%&1&%&&&0%0itPimssm_sPi_5PJMPqHbt0 2`ҡ̟@=2  hȼqmiN_Zi_MP++Pa?>?6& &6:>j͙ʖj;NJHH.55,(,.(!+36 描iŝh66:^s<&&0&%%&1%0&%&&&&<OPss_s_Pi_OHJJ9_OMq_ibb1 /226@``j`@@@=2;ȼӼȲmq_MZPMPiPM-.`;6:6&06?`˕ĝaPJHH....(,!(+-&7 ᥫsiҝ<ӼȼӼtqsq_MJPJPNi_5OOM_?61&&6;>?`͕aN5,(,-(((! 7 ᦹ}՞śil'&&%&%%#1PssP_OMsmmi_PO_OPP.+559HJ9._,_OJHPJNNibXrqλܯ<0  /4`ӵӼsqbiPPJiJMNP_M5O_k^;1 %6;?jʕkPH,H(!((.,(3ڃqŞֳ^%'0&&%%#%#3PsiimqM&%9iq_O_N_PPM,,9-M.M.M.HP.5JJJHMNON_rrΰȼțӵss_PPOOP_JbPqM99k_Ġ՘ki40 &:?`jj`akʖkM,MH!(+(,+!3 иsҞԳh'&&%&%%8Oqmid_d&%#9_JPPPMPJJ,9(5.55+O(M5,.M.5,MHHHNXyǻ˰ȺӵiPMP_PMPP_H..-._Ma;;?`?j`kk_].$ %3?^``j```>;^]Naka_PMJ,J.!(+(++!!4 ڦtűҞ^40%%#&%% #%9O_PsP_P4#%#8ZMOPMJPJJ..5+5.+9.55((.,.((95,HHbbNrbbɰƼɺƴȼ˱ƼӼȵtlsssOJNOO_PJPNN.8H]4114:;::7]OM_M-(% 06??^?>:7:66MMMJ_]OH5,.H,!(++-,(4ᥤmԳҞh:60&%%&%%%%# &MO_O_PPP8##'J9OJJMJM.-+(9(5-!.-(!,!,!+,,(..MNJrNyyűsimskPJPPM_MMPNM.(M&&0&&11003H9J](($(0 &067??;:6434647H5H9H5+!(,5!(.-5-(!3®揮sҞŚj:0&&%&%%%#&OP_OPOON955H9H5O!-(++!9-(((-(+!MJ(.bHJbbrbrsimPss_qOJMMMOPMPN-  %%$(+(](+.5611667?;6410040&5,,((,+.((+,55795,!3 sqiiiss䞝֚?:40&%0%%%& #9oiPiosmM##&%## 55JJ_HOH55(5+(]),!(.+(++(,+MC.NXbrNbbrrttstsslslii_issi_PPOO_OiOOM9HHPM_ONO$   !(]M9^_j;>667>>46314:4&H,,(!H.H.5,Mkk_^5+,(!3}ssss𱝛?;640&&%%&&#  &5MPiiPmi'% & &MOP9M,9,59O.!!(9(!-(!!5+!H,,NPXbJbbqbrPrrmqtiiq_sqsissks_ssiOsiii_POPPiOJO9H9PJM_NM-     !+9kaĕ`>646>>4444:775.,(H,HHJO._aqM-(!3ϧss󝛛k6600&&%-5.9PiPd_i&0##%&%## -N_JMH55+9.P!!!+5(((!((!(),H.JJ_PPPb_bq_Pbqq_qiPPiiP_isimsksissholii_sim__dMiiOOMM9HOPOM9PJ+    (-Mař`6444;666:>^MMH.((C,.MN]O.+!!4ϊsim񞝛s:40&&%%85599iP__m'&& %%0#M5_J_.HJ+9.,+J!+!+!-!!$(!!!)5.HMHNMNPNHM_NPP_PiP_PMOM_POPPi_iOhOOlsii^ssiiiski_ilsii_i__MO9OO99M9&OM.!+$   5];4114>:76;]MP^M,!H..Ha]kaH+!3حsh800%&%#999M5Oiimi8&#%%& #-NJMMM#.+9(-(!!+!$!(+!!(.,5HHJHJNHJO_MMNOO_MPMJ9MOMOOO_POOkPPhsiisOiksiOslsi_iqi_OPiJOMMM5'-OO+$5    4_ŕa;0311;::;kNMM5,..HMNa_7,!3᮸sllsh'0&0%%#%#1M9O99MOisq<0%&&0%%%% &JMP5M+ -(.-(((!(!,(.+.+H..95HJOJJJMMOMMM59MM9M_PiMOOiOPhiii9siOsitsOP_iJO9M4M9.(-$  &^M7&&03>;_]_]HPM.,HJ]aH+!4Ϯ׸sssss՛k440&1108sO^OOOM_i401111400%% ##9JPJ_M#  9.!!((+((!(5,5HJ(J5MH.OM_NMN5MOJOM959MM-.599POhOOi^OOO:>>?>>;;>01%-+.5+9!  +-,J,.5.((!5HH(!!+!-++.,-$,J5.-59---5MiiJP__i_PsslPl_s9i_hOJ^OMM99O4   $OO_PP_J  %06?kO9M+,7 +.JMMMM]bM59Ma_Ė_MH5M537 p~oiOOssաĞœjj?>60+5(9+9(    $,,9).(,!,(!!! (-(+.+.($!(--9+5$,M5OOP_Pi_POOk_ilh9OM<9OOOOMM9-   '_iPP_O5   %&0:^qaM_M9,+,7,-5-OMMM;aMJ7aę_O..3&7 pϮ}sOs록բ`;44H.M!+-    +,...((,(++$! ,((.-$+.+($+-7+-$+MOMOPPPi_Miik_PlslsO }ss_s՚lųŖM^.M+(     5,,J.+  ($+(--(+-+'+$+9M<99JMM]Oi_hiiMkii ӲӲl󝁚ksʳȞůjj`jj??k_OOM55&%&1114&  5(,H(.$    '$4-'  9;<88 #'#####  3333&$&$$ 3$$-+$(++599.--+(5!!(!+5H5NHO_a^;4 յ󝝚l񛗵ųͳ󝗙j??ahM_kP94016:>?>60 -,)+JHH    # &&'3  8888'  &#  433'  + $$-+.$!+--M95.--+7(!(!(.JJJ,HONPPP_aP;& 욚l𛝪󵳴ų󚗗󝗗`>>ak_M^99744:?jjj?:0&,-,.,+,   # &  4'411       # $(+++-M955-5.5$(,((,(,,.5JMJM5-3  ųĞlʳ󞝝󝛗k^^ak_M_]M:6>`j?:&35(,H+(.      %&%&%    +$+($-5MOMMM997,,(,.,)(,...M,,.(ŝҞ񚗛󝝨흕얕__aOO]::`j>4HJ(H,,,(             +-+(--7_aaaa_9_+(.,,,(,C.H.,,+$ 읗ȝ񝝝lki?7O.(H.!.  $$(+++5]kĕaP],5,,)((,,,C!-(! ҝšĞ󝗝ų񝖯񝖗kkii^_]6:^j?O].(HH((   +($+5MaaO.H.,(!)()(.!!(Şŝʳų񛙖kikq_O^466?ja_M.+9,(!  ($$++5;a^M.,,()(,()()$+ʳijʳ񛙖l_Oa<67;>`硘a_J..5,(( $$($+-MaO5MJHH!()+()!,,(ҞŞŞų󵝝񛙖ka_]k466:>`ԙk]J.7).( #+$(+9_]JJ..C5!)))(,,($šŞĝқŝ񙗖k_^O_O;;:>jkM95,.((    (+-]a_9HH5,,D((!((),(! ҳŝĞ횗kaOJM,M!   #(++-+5]_kĕa5.H..,(,DHHC(,((ŞŞĞĝŝŗ󝗝񙁗kki_M]_OM`^kMMH+,,     $(--..M^_^OM-^(5.,,)+,,,.!,($ ŞŞŝ󝝙ksakkkk_]PMMMk^akO55(@/읞ﯙkkaqP_qbiaPi̙kM5,! =2ŞĝĞkk흗q_MqNMqJOP_iiPJ+-(   3+-+-....J-J9HH5MHHH.)-)/ŞŝŞ읗iai얆iPMM_JJiHJMMPiqPaMJJ+!!q_;3   % 4^ǻܻq;/񗚖ikiPOOMJJ_N55HJOPNMJH55!- q_]M0   %% 6]kǰǻǽ_72ҝĞkkaiitPNO.HN_....HP..J.,55!bPM7&   %% 4]qq]4/ͳĞҝijlia_aaqPMOJHHJ-.H..H,,J.,..5!NMM4&   %& %8]^iyrrPJ42떕kka_kqO_NMMHHN5D5.D5.,...,D9)! JJ]-$    &%0 &8]_it{rr{rrtbNP7&/ŞijĞki_kOMJMHH5_...D.J,,.,,,J.)!JJH4$    %&&0 07P^bqstbbbbZmbbbXPX_M5 /ҴųŠĞękkaikqqkPMJJHHHJN,D...H,,,,,)O)( ..-.            &30   49^O_ibqqqrtrtyttrttPXPNPXPNSkNNNJH-$/k_^PaO^__NJ.H.HHNH.,.DJD+),,CH5,C-+++          # &-'&$&37MOMOP_rq_Z_bmtrqtmb_ZrNMJIHPSMJIPHJH-+ 2ȴġĠka_OOaO9JaNMH5H.DM,,..,H((,.(,.5!!((!($        %%  #& $'3&&$ $34999MOMPPMP_PPbNMNMZPZ_P_JH.H..,H..DM.,+$/弸ġššĠkaaP61>0&&&6                ##  #  s84&%%%%/0=====2=====2=>===%0 &              @ = @  = = =  = = =  = @ @  = = = = @  = = >_<$ /2/////2///2/    /2//2//2/2////2 /2/2/2 /  2/2/2/ /2/2/2///22//2// /2/2/22//2/22/2/2//_M4%          iM8&     s_<4&                @   # ) 5%2 =")%7&&'#,8(2;789ES "K &R)G,T1M3Y +d-`5d;s!-A)6F%9X3=I2>Q$aE\B^KdHhLpTjTlYr[uJkMpSmRsStUyUyZuYv[zZ|\|anhvezr|q~e}`^]_]niyu~ebedklmmabeejjjjkluuyurtt{|}||glqu}q3mSm-F'q?wq*wqmq-F-E qmB-F qmC-E -F Q-E S-F Rp qqA-E mmB-F mmC-E -F p SRQ:K:n:r-E O%Q:rRSp O:K:nn-F O%Q:nSRp O:K:rp :K w3t?/,(}tRttR'*(  f3B "Please wait, an administrator is going to start the game."o3K "Waiting for ready signals, press [Fire] to send your ready signal!"t3y oz  0ry /y ^y \ %-j '^ y ^y \%$e) Tf)a -P t -P Y[3 60{3OpuDv-Y'y ?z-Y wy *cwy O|y \O\-Y(y y A-Y Is3 4B4x V%+0}x ],x ]-I'-I-Ix d , -I-Ix ^ ,d-I-Ix ] ,d-I rSK#+ K@C,B, 'KK BK BKA 'K,K,K ',,K 6CK' Account name'K( Account title'K# Password'z K| K} KK ,,K ,,K ',,Ko%o,\o ez\oq KX$ >  Privilege %1 (not defined).So&oq -'oq K.$\~\ ,&on K Savep K Resetg.A KK,XK* Add account type} K- Delete account type| K! Move upz K# Move downz ,| ,} , gX} | z p n ML v3 20y3 0.5A4SK*f.Z 'HSl S Password3 Login request for %1S/, IP = %1S^; ClientID = %1 _S\;{Z# Password = %1Z~ WS-o'l!w g+Illegal login parameters (possible hack).kNexgenIllegalLoginDialog5-opS-o'l!v gClient ID already in use.kNexgenIDUsedDialog-ooSJK-o'l!u g Kicked/banned from the server.kNexgenBannedDialog%\J&\K -o-G{{%{ZU{SSD&S-F-f-o'l!t gInvalid password.-LS-F-fkNexgenPasswordDialog%\T-L-oSS-o'l!s gServer is full.kNexgenServerFullDialog%\RK&\Rn,\RrU-oS-FSSA-o'l!q gNo playing rights.kNexgenNoPlayRightDialogmS-o i,wi%*-oi%KSlgk\i`-oi%4i,wi%*i%^Slgk\ir{kS)Mk%\&\,\,\US. Login denied. Reason: %1g,S-_ nMNSIMNLogin accepted.VS x3f MsgZg*f , "{f  =v. g F v>f  =v(f f (Sf  Dz3 64|3J L&zJ  = z # J  =| # J  i} ;  #UJ  WGJ  qc %c ,^c  eAz^c q -(~E^~^,c q -GEc  p36 "The server has crashed and has been reloaded!"O4d)rv`]-u h,ShN %h|pA-u'Zh-uhN h|d)dNXOCShN  R4P o c'-D-DNP |$$d-D-D%NP `$P |}P {,P {-D'@%@,-D-D@P _,@-D ~3 32C4 "\\n"3K L`lrg*} -'| -'z -'p -'n -'(K . g} -K (&| -K (,z -K (&K (&Sp -(n -(^wg*L. g( JE4c)1~pc),pb),% @4V "The Nexgen boot sequence has failed, please contact the server administrator!"w2W!b nj?Fwj*jTW!X!jjA y,wy%*y%TW!X!yF G4L4Gy M %M ,^ M  eM q - {^ zL L ^ ~^ ,L ppL ,^ ~^ ,M L  L @wc5zo`-L!z-F-L!zQ!efN!M!fz-hM!hzvQ!efN!Ch!ef X4a)s\-w -F b ,b N a)bb |pA-w'j-F'b N b |b -w DK4t]$Dtz :rtg :z,Lrtp p - :z,L. g(0rtn n - :z, w~*~V=. g(z  G|  }  rtXX- :z, w~*~9Wz  G|  }   rt} } - :z, w~*~#I=. g(rt| | - :z, w~*~$J=. g((rtz z - :z, w~*~$J=. g(' D4 0a4_ s#"B,{B_ b-J-JB_ b, -J-JB_ l,-J-JB_ h,-J-JB_ T,-J-JB_ , B-J P4T4 :M-C?r~*~.> lw~*A' hS4`)3P 0,*:`) &ML H4L "The server was restarted by an administrator and has been reloaded!"Q4 1Z4`JX r`* CoxNXOCj-GsC` _,w_%*_%J`Ax_jw*J`Ax V4A "Welcome to Nexgen, type !open to open the control panel."^4_)thDSQa _)QFO QRQ Q!Q QC (QG~Q] V~ a,wa%*a% XQa W4 2yY4& "New settings have been saved."c4^)vMPPYP-y b,Frb%*-y'b%^)Mb-y C[4 3l4pv`t, {tp=-C-Ctp=,-C-Ctpq,-C-Ctpi,}tpW,tpW-C'tt%Wt,{tpa-C-Ctpa, -C-Ctpc, -C-Ctpb,-C-Ctp},Mp Ot%(z  tp}-C'tp}Player*t-C ~d4b4xH~V[-d A,{A]c~A]=Xc%|A]cQ||~ ||*~A]c&Z ~ GwZ *1rZ a  aZ -d'NAXA{-dUZ 7 #No replacement class found for %1~-d e4{iCa 4$x:j Aa/!_B=.-C-l -g-p -A-q d&a/!e-^.--_S k4M=set Engine.GameInfo GamePasswordUZ set Engine.GameInfo AdminPasswordUb sI [M :K:n:r:X-i set IpServer.UdpServerUplink DoUplink True9set IpServer.UdpServerUplink DoUplink False%_&_,_,_ `4 4y{rY #0VK C,B, ' f  B,& B  , , & Player name' * Ban/kick reason'X  [   Qf Q B,f "& ?  : i B , B, % Ban period'   , ,%e #$ Forever&e '$ 'x' matches,e ($ Until 'date' H E  Qf Q ?, : i ,, `C, ' `C, ' `BR. A  `BU. A  ,  ,  ' IP addresses' ',,e  % Client IDs' ',,| K  AddF  RemoveL  AddG  Remove].A   ,U  ( Create new banP  $ Update banO  $ Remove bane,|, X , [ ,H,H@'E,]RUKFLGU P O g%g,gegK%e-'HF-'G-'E*  dd/mm/yyyy hh:mm f4 5w4AP:y@~A.0@%` A@;` A` p` .LevelSummary{` 'w{* t4m y;0}m I,@-y'm I-y-ym H,-y-ym l,-y-ym i,-y i4{ K]Z]*{ ,"{{  bG. ] F G>{  bG({ { (v{ U -v,I j4 6n4 7Vm4I9r]*P -'O -'}P -(O -(O. ]( q4e O X # e  b[ # e  T -Ne  jJO%O,Oe-O:jO=:j &HG(H  JEG'E :j ,HG'H EG(E#  kJHG'H EG'E RZR*Me  l{MO~M,O%w MMw MOMMO},i. R F i>w IK-RC ,F-'UZU*Me  h1{MO~M,O%w MMw MOMMO},i. U F i>w L-UC ,G-' o4 8D5q-V-VNqj $$<-V-VNq]$$-V-VNqA$$<-V-VRqp%'-V-VRqt%'-V-VNq}$$<-V-VNq~$$-V-VNqX $$<-V-VNqR$$xw%w,-V-Vwqv,@ww%w,-V-Vwq],w-V x @B@K5v4S4Bb%hB-x '{B-x  {Bh-x (|B}H&pH- rBHBbB&!H%xHBb%hB-x '{B-x  {Bh zE-x (|B}H&pH- rBuxEBuBbB&E s4 9Mr4H0&kH+G &e-E,G ,e- z4|4 A)C?rY *Y .> lwY *A' h{4])3Xh,'%:]) ,K ]4 "[PM] %3 %1: %2"}4@5Gf=.> &e-pMH  ,e-pU DE  @4B5B|"1_. Rw_*|{_>\zh h _>|h pph ,_>_. _h  _@x4 'ssoffline'V5zs_ Hzzs-M'zs[NEXGEN] Another UT Server-M-Mzs,-M-MzI , -M-Mz[,@-M-MzM ,@-M-M%z_,-M-M&z_,-M-M,z_,-M-M,z_,-M-MNzK$$ -M-MNzn$$-M-MNzr$$-M-MNzX$$:zK:zn:zr%-M'zK$-M A5F5A34c. Uwc*|{c>\zk k c>|k ppk ,c>c. ck  c@C5 'sswaiting'QDE5zB8Dzg rzU  :g,U -c   X  {c Y ;mc BA  [  GrzP  :g,P -c   X  {c Y X^=. ](c BA  [  GrzO  :g,O -Y #f=. ](rz] :g,IrzR :g,F-rR*wR*e" . R>rzU :g,G-rU*wU*|" . U>rzK :g,K-e   e  de o. R F o>e K-RC ,rzF :g,F-RkR*F-'K-RC ,rzL :g,L-e   |  be o. U F o>e L-UC ,+rzG :g,G-UkU*G-'L-UC ,:g,za/!xu u % C,rzCeu CCYu %C%C,Ce-Cu CH mM5J5U/+r-s$-] -GQzn(@n(mI rm*(G-l r,l S={l -lPl =-l= )Warning, attempt to load %1 has failed!l rT-l(mi{m (mPm  EXEC %1PNP{Np> Nhopppppl ?game=I?mutator=Rl- Restarting server on %1l  @o(' N5L5D) zb# O5nca &Starting Nexgen Server Controller...::$*Failed, your server should be dedicated.a H-z(a p"a q"--}3First time run, executing default installation...I^  jR 0Updating Nexgen configuration to version %1...U _,H}5Configuration file has been automatically repaired.-]-C^D-]-C'-_-DD8-t$-] -GAServer crash/reboot detected, executing Nexgen boot sequence...-BU?-B-C'b 7Nexgen boot sequence failed, resuming to normal mode.-Y'-C-D(N; ServerID = %1 _O !-t ra bXM{-_.-(-{.&-Z -{x@a va/!_a }i {n%n,nN nZa Nlkx~ o -Fha WhXa? A' P5 "*"Q5 60R5 'noplayright'S5 'serverfull'T5 'invalidpass'X5 'banned'G5 'ssready'\5cn@ $-Qcw%}ck, -QcpcZ c.Xget Engine.GameInfo GamePasswordcb c/Xget Engine.GameInfo AdminPasswordc{P%P, PcWP-Q SY5 'duplicateid'Z5 'illegallogin']5 6U5 'ssstaring'd} -Snd sd d yd vd sd od Vd-Sdb-S _5 5rh7#*KA,',A, 'A,A,7DC 'W$ Enable Nexgen boot control'X$ Restart game on last map'R RebootB$',,k SaveV Reset7BD?,@'r7"77,7 A7 A7( Mutators used'X.7 A 7, Mutators not used'N.7 A 7 Qr7Q7 C,r7"77 A73 Server boot command line'q7N7 Qr7Q7 ,,7 B7 B7 A7 A7$ Game type'S7O7% Map prefix'T77: !Additional command line options'j77= $Pre switch server console commands'p7T,j,p,VXNSTjRyUTq-'X-' `5 4b5 3^5C yC , "{C  F -(C  F\)Y (YYeSleSC C  g5 2i5U!> L-R!d!B4-R!U!T!dJCT!dU!* d5@a5F UF ,"{F  M -(F  MZ)cZ. N F Z>cZ(F F Nx l5 1~4& "Match password received: '%1'"V!f'5V!r*\3 tV! v5[)jF*0.[)> lF' f5tTV(t r IS Stt. Xwt*t(%U . N F U >t>U (t(t. tbXZX*ewN*N-(N*s WF{s (sastJatN \tU . X F U >t>U (t(tkNxT  Hj  lp  iW- -tX- -s m5 0n5 'CorrodedMessage'p5 'Burned'br5 'Fell'k5Mt_S@V_% -(_ F[^P . XwP *P (% 6(P ( M\^zQ Q \Q pppQ , \P . P oMp  T -*.unrv{[MppM ?game=[{Q MppM ?mutator=Q MppM   j qZq )M t5 'Suicided'o5@x5 "debug gpf"[5 'ssended'U6ei % 9-_S:949&9-G:9d %eeF9-^-b' -b9-^e-Fe-ue-u'9JdL%1 is READY!e/ e9S 9v9VdL%1 has started the game.e/ e' u5 'sspaused'A6 "PAUSE"q5`b%`S@w`% -(` FY)e (ededT  d Th5$ "The password has been send."w5 'ssmatch'|5 'ssnormal'y55 0[(C?rh*h.> lwh*A' ~5J XH*;u. Xwu*u(%\zJ J Su(J pppJ , Su(u. uhXW-X-S@J   T   j   p  B6 "OPENVOTE"D6 "OPENRCP"D@6whM0CDwx :rwV :x,Tkrwk :x, wh*XrwR :x, wh*hPrwX :x,wN*N-(N*``rwN :x,`wX*X-(X*rwX :x, wX*n. N F n>. X>n(. X(@wN*N-(Nnn-'XkX*NxrwN :x, wN*n. X F n>. N>n(. N(wX*X-(Xnn-'NkN*rwS :x&brwT :x&Arwj :x& E6 "DISCONNECT"F6 "EXIT"I6 "START"hC6X)3^<'%:X) ,T {5X "You are not allowed to kick or ban players that have an account on this server."J6 "SPECTATE"K6 "PLAY"L6 "BALANCETEAMS"M6 "SETTEAM"N6 "NSC"P6 5rJ x#"lDC,@ }"B,xx Ax+ User interface'$x,J x5$ Enable Nexgen message HUDI x9$ Enable message 'flash' effectH xD$ *Show player location on teamsay messagesO xG$ -Play a sound when a private message arrivesB,xx Ax3 Miscellaneous settings'$x,Q xF$ ,Auto screenshot at the end of normal gamesT xA$ 'Auto screenshot at the end of matchesJ I H O Q T J -|2#2UseNexgenHUDtruetrueI -|2$2FlashMessagestruetrueH -|2$2ShowPlayerLoctruetrueO -|2"2 PlayPMSoundtruetrueQ -|2(2AutoSSNormalGamefalsetrueT -|2"2 AutoSSMatchtruetrue S6 "server"DO6zZ<dDz{rzJ  :{,2-EUseNexgenHUDTJ -2bw U*J -I'I(orzI  :{,2.EFlashMessagesTI -2bN-oI - rzH  :{,2.EShowPlayerLocTH -2bN-gH -{rzO  :{,2,E PlayPMSoundTO -2brzQ  :{,21EAutoSSNormalGameTQ -2bbrzT  :{,2,E AutoSSMatchTT -2b T6 10.0F7 'NSC'X6VhF `a/!_UL/this command is only available for team gamesVVF-T'9-GULthe teams are locked9-gULteam balancing is disabled-T'-T-]9-]9XdL!%1 has balanced the teams.V/ VUL the teams are already balanced-T-] f}5 'cslogin'[6C f3aC C AgL/you do not have playing rights on this serverC -F9 SC gLall player slots are taken9-GgLthe teams are locked-f'*-fC EC $-f W6 'csidle'i`6kbKBw 9?Oww *8w -FQw w A:9X%NL&this server doesn't allow spectatorsQ:9Xk-FNLall spectator slots are taken-i'9-ikEk$-i nY6 'csprotected']6 'csmuted'^6 'csdead'i6[aGVB[-Fx Lspectators can't switch team/9-Cx L+team switching is disabled on this server/[-yx L)you are not allowed to change from team/*9-Gx Lthe teams are locked/i %Ca/!_$i . i ,x Lteam doesn't exist/'i :[x L: Lyou are already on %1L Xi /-n'M-n[ z=i -n _6 'csnormal'a6c=!.h -o|2$2FlashMessagestruetrue-g|2$2ShowPlayerLoctruetrue ru5#)K',, C,B,AAAA'$55HC?, '5,5,5% Time limit'5& Score limit'5+ Team score limit'5% Game speed'5. Team switch enabled'51 Team balancing enabled'5+ Teams are locked'5. Name change allowed'T5S5R5O5l5$o5$p5$q5$ Mutators'$V.A  Level'$55 B,r*5: iBB$$51T'BB5 @B5,5,5 File'5  Title'5! Author'5" Players'G5F5E5D5 Players'$u.'A 'l-'o-'p-'q-'~kU b6\ f6$Gw* f\ E\ rS[-H : A%+r?,:!o%xD< Ar?&Yx%W\  G  )Connection lost!\nReconnecting in %1...Sx%R%RW\  'Connection lost!\nReconnecting now...%R%REu:!o%x:!oW\  ^  @Warning, reboot sequence activated!\nRebooting server in %1...Sx,RcEEw%w,xwW\  ^  @Idle / camper detection activated!\nMove or be kicked in %1...Sx,Rc c6A~{nAY .AwY *TSY SSY RSY O+pSDB!A%l-!-Co-!-gp-!-Gq-!-A H6 "%1 has moved %2 to %3."I4U S `rU ?awU *JU -Fk!JU -ubU U A `=,:k!YbZ`:b:`v e6Z kKZ VZ pZ ~Z ..unrG Z F/E/D/ k6nf5]L%Failed to execute command, %1.rLinternal errorV) 9 SETTEAM-uanJ%W)r]L!Failed to switch team, %1. )9BALANCETEAMS-uhnr]L#Failed to balance teams, %1. U9PLAY-ufnr 9 SPECTATE-ubnr 9START-uinr 9PAUSE-ujnr 9EXIT-u'nKnExit a9 DISCONNECT-u'nKn Disconnect 9 OPENRCP-u'ne 9 OPENVOTE-u')DBDBMAPVOTE VOTEMENUn -B'P-Bn;$L,Invalid Nexgen command or parameters.-un$L ]r h6}Uv y{v (v{vyJ{ M (yT)w}. V F }>w l6c&9.L9 9 "BDBMAPVOTE VOTEMENU"d60Wc,0.0c 0Kz[ % {KV~K\nV%S KVKKV}\nS KK0aS ^FDDD^[ gD,, |D?[ F?,, fD0?,aD0?|,0$0b(A>0?f?a0(a Z(??|BB0b(AL?0 ?f@@?a?0.a c@?|@??0(?f???a?0.a c@?|@??0 ?f@@?a@0&a cA???0(?f@@?a??|0&a cA???0-?f@@??,?a@0&a cA???05?f@@??,?a??|0&a cA???-kS??0b(A?k`DkI@??,@bD?,?,,@k00?f@@?`?a??`0:a c@?|@?,`??08?f???`?a??`0:a c@?|@?,`??00?f@@?`?a@?`0'a c?b???08?f@@?`?a??|?`0'a c?b???0@?f@@??b?`?a@?`0'a c?b???0H?f@@??b?`?a??|?`0'a c?b???Kz[ %0~({KV~K\nSV%S KVKKV}\nfS KK0)?f, ?a, ?[ F0 aS ([  f6 "%1 has paused the game."n6! "%1 has resumed the game."o6# "%1 has restarted the game."]j6u<W,zvpjquutWzvplu juzvacpuut p6! "%1 has stopped the game."r6/ "%1 has disabled team switching for %2."s6. "%1 has enabled team switching for %2."jq6N)Lg&$:N)!%~ t6) "%1 has reconnected %2 as player."v6, "%1 has reconnected %2 as spectator."w6 "%1 has send %2 to %3."{6b!CrK*,B, vBBtv&fvBv&{v{v3BB&6u!v{b!6t!v{a!6s!v{`!6r!v{_!Kb!a!`!_! x6( "%1 has disabled team switching."6U\_q!L>q!/a0 :KRQwK*R10wK*U%UBKF6u!U{6t!U{6s!U{6r!U{UeB% J7I)^Lx"  J lz  ttnsc_%Y_%m_%d_%H_%i_%s-G-I)-t-J)-J-K)-s-L)-P-M)-Y-O)-t -P)]R)d S)^ t] U)b%$  <$$New settings have been saved.(%1 has modified the log settings. }6R7~6A} z6' "%1 has enabled team switching."@7( "%1 has disabled team balancing."A7' "%1 has enabled team balancing."B7! "%1 has locked the teams."C7# "%1 has unlocked the teams."rwD#/ KB, 'B,,DjD  s'$DD B,D,D ',,Ds Z  &Welcome %1, you are logged in as %2./{'D` HThe following privileges / rights are available to you on this server:D,,,D,,,^ %W^ ,}D$K } '$W ^  ezW K V >  Privilege %1 (not defined).S^ &K mA)MK ,W ~W  ,&9)W ~W  ,K mC)}W$MK mB)^ w Team balance%a Red&a Blue,a Green,a Yellowxp Reconnecto Disconnectn Exitm Open mapvote| Start gamek Admin logink-Fx Playx" Spectatel:!4!&|-' H7 "NXOC"D7( "%1 has added %2 to the banlist."z( 15.0G7, "%1 has removed %2 from the banlist."M7@)uFUJ @) /E)F)G) -H) E7L7l{S-F$-q-w-'c%c,ca-'cxc%c,ca-c:!Bcc%Qc,Gca-camcD)c cS7[?u"  F -^-^.--^set Botpack.DeathMatchPlus bTournamentT-^`-_-^.:4&lS-_.--^]$4-^&%1 has enabled tournament mode.s'%1 has disabled tournament mode. DK7b Db cwb * :c,b a/!w.b -b wmutate nsc balanceteams %amutate nsc setteam 0 &amutate nsc setteam 1 T,amutate nsc setteam 2 ,amutate nsc setteam 3 #x-Fmutate nsc playmutate nsc spectate. 4 lpreconnect. 4 odisconnect. 4 n exit 3mmutate nsc openvote. 4 i|mutate nsc start kMNexgenAdminLoginDialog  @I7- "%1 has set the server in match mode."hN7}(3FD:}( %j s _7xo_-e!x-F-e!xj!\^g!f!fx-_f!_xvj!\^g!C_!\^ T7HYlA:j  J H,z  dz  e H%H%H,"{H]H|H, H]pp  d=  eb%$0%1 has modified the HUD replacement list. ' W7BSHj4H?  J :B% :B, d :Bd ,&d ]d &]d K,&]b%$0%1 has modified the HUD replacement list. ' P70 "%1 has reset the server in normal mode."U7 "%1 has muted %2."^7OR!.M  J O,z  [ O%O%O,"{OvO_O, f p  [,-{(f pf P-|(f pf SOvf b%$=%1 has modified the spawn protect ignored weapon list. ' V7 "%1 has unmuted %2."@rd #"lC,T.A , %Weapons ignored by spawn protector.'$B',D?,@@C Weapon classak $ Ignore primary firen $ Ignore alternate fire',, SaveA Removea,@} Z7o }H kT*TZo ,"{o  v -(o  vVx(p . T F p >Vp (o o (co ,p . T F p > p (| a7Y7UI  X7! "%1 has renamed %2 to %3."b7JQ'U?  J :J% :J, ~ :J~ ,&~ v~ &v~ K,&vb%$=%1 has modified the spawn protect ignored weapon list. ' k7o!> L-l!U!B4-l!o!n!UJCn!Uo!* [7P|pP. T-rP*A-rP*P(%k -rP*n -rP*aGrP*rP*k -(n -(a 1P(%k -(n -(a  6(P( vU_k -~_ P%n -~_ S%a  U g7y(P<OLD 5 LHTTP client error: %1Sy( c7P$x"  J "%1 has rebooted the server.o$  x7]Ld"  J ]=:]%,<\=:\%,X=:X%,<VV%'UU%'S=:S%,<O=:O%,N=:N%,<L=:L%,xj ]]\AXpVtU}S~OX NRLb%$ <$$New settings have been saved.3%1 has modified the general Nexgen settings. `7e7 C?rd *d .> lwd *A' Dd7EOGDEF4rET :F,|rEA :F,A- d #Q=. T(ErE :F,- d ^R. T(  a k -n - h7m X,~m pppp/regnscsvr.php?ver=S j&port=SO&name=m pm  (rLs,}m V130.89.163.70m ,P u7cL. hf7w(3*'%:w( ,} ]7* "%1 has kicked %2 from the server."Kp!f'5p!rK*\3K tp! g6rl u@r?>wr*r-u(rrA  l7E ZwPN-Bq%Nexgen Server Controller is active. j7* "%1 has banned %2 from the server."O// MD5 Hash generator -- Copyright 2004 (c) Petr Jelinek -- Modified for UT99. // Source: UnrealWiki: MD5 -- http://wiki.beyondunreal.com/wiki/MD5 class MD5Hash extends Object; /** MD5 context */ struct MD5_CTX { /** state (ABCD) */ var int state[64]; /** number of bits, modulo 2^64 (lsb first) */ var int count[64]; /** input buffer */ var byte buffer[64]; }; /** return the MD5 of the input string */ static function string MD5String (string str) { local MD5_CTX context; local byte digest[64]; local string Hex; local int i; MD5Init (context); MD5Update (context, str, Len(str)); MD5Final (digest, context); for (i = 0; i < 16; i++) Hex = Hex $ DecToHex(digest[i], 1); return Hex; } /** initialize the MD5 context */ static final function MD5Init(out MD5_CTX context) { context.count[0] = 0; context.count[1] = 0; context.state[0] = 0x67452301; context.state[1] = 0xefcdab89; context.state[2] = 0x98badcfe; context.state[3] = 0x10325476; } static final function MD5Transform(out int Buf[64], byte block[64]) { local int A,B,C,D; local int x[64]; A = Buf[0]; B = Buf[1]; C = Buf[2]; D = Buf[3]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], 9, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */ Buf[0] += A; Buf[1] += B; Buf[2] += C; Buf[3] += D; } /** update MD5 context */ static final function MD5Update(out MD5_CTX Context, string Data, int inputLen) { local int i, index, partlen; local byte tmpbuf[64]; index = ((context.count[0] >>> 3) & 0x3F); if ((context.count[0] += (inputLen << 3)) < (inputLen << 3)) context.count[1]++; context.count[1] += (inputLen >>> 29); partLen = 64 - index; if (inputLen >= partLen) { MD5Move(Data, 0, context.buffer, index, partLen); MD5Transform (context.state, context.buffer); for (i = partLen; i + 63 < inputLen; i += 64) { MD5Move(Data, i, tmpbuf, 0, 64); MD5Transform (context.state, tmpbuf); } index = 0; } else i = 0; MD5Move(Data, i, context.buffer, index, inputLen-i); } /** finalize the MD5 context */ static final function MD5Final (out byte digest[64], out MD5_CTX context) { local byte bits[64]; local int i, index, padLen; local string strbits; local string PADDING; PADDING = chr(0x80); for (i = 1; i < 64; i++) PADDING = PADDING$chr(0); Encode (bits, context.count, 8); index = ((context.count[0] >>> 3) & 0x3f); if (index < 56) padLen = (56 - index); else padLen = (120 - index); MD5Update (context, PADDING, padLen); strbits = ""; for (i=0;i<8;i++) strbits = strbits$Chr(bits[i]); MD5Update (context, strbits, 8); Encode (digest, context.state, 16); for (i = 0; i < 64; i++) { context.buffer[i] = 0; } } static final function Encode (out byte output[64], int input[64], int len) { local int i, j; i = 0; for (j = 0; j < len; j += 4) { output[j] = (input[i] & 0xff); output[j+1] = ((input[i] >> 8) & 0xff); output[j+2] = ((input[i] >> 16) & 0xff); output[j+3] = ((input[i] >> 24) & 0xff); i++; } } static final function Decode(out int output[64], byte input[64], int len) { local int i, j; i = 0; for (j = 0; j < len; j += 4) { output[i] = ((input[j]) | (int(input[j+1]) << 8) | (int(input[j+2]) << 16) | (int(input[j+3]) << 24)); i++; } } static final function MD5Move(string src, int srcindex, out byte buffer[64], int bufindex, int len) { local int i,j; j = bufindex; for (i = srcindex; i < srcindex+len; i++) { buffer[j] = Asc(Mid(src, i, 1)); j++; if (j == 64) break; } } static final function int ROTATE_LEFT (int x, int n) { return (((x) << (n)) | ((x) >>> (32-(n)))); } static final function int F (int x, int y, int z) { return (((x) & (y)) | ((~x) & (z))); } static final function int G (int x, int y, int z) { return ((x & z) | (y & (~z))); } static final function int H (int x, int y, int z) { return (x ^ y ^ z); } static final function int I (int x, int y, int z) { return (y ^ (x | (~z))); } static final function FF(out int a, int b, int c, int d, int x, int s, int ac) { a += F(b, c, d) + x + ac; a = ROTATE_LEFT (a, s); a += b; } static final function GG(out int a, int b, int c, int d, int x, int s, int ac) { a += G(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function HH(out int a, int b, int c, int d, int x, int s, int ac) { a += H(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function II(out int a, int b, int c, int d, int x, int s, int ac) { a += I(b, c, d) + x + ac; a = rotate_left (a, s) +b; } static final function string DecToHex(int dec, int size) { const hex = "0123456789ABCDEF"; local string s; local int i; for (i = 0; i < size*2; i++) { s = mid(hex, dec & 0xf, 1) $ s; dec = dec >>> 4; } return s; } n7" "%1 has muted all players."p7$ "%1 has unmuted all players."q78 "%1 has allowed nickname changing on the server."r79 "%1 has disabled nickname changing on the server."v7 80L "130.89.163.70"s7$ "%1 has rebooted the server."~7k(Kz"  J -j -k(-v-l(-B-n(-j -o(-Z-p(-_-q(-u-r(-l -s(-p -t(-q -u(-F-v(b%$ rJz7#+BJ.> ll@A, Log settings'$?,: i',D?,@,,a$ (Write log messages to console (stdout)'d$ 'Write Nexgen events to the server log'f$ )Write system messages to the server log'i$ 'Write chat messages to the server log'j$ *Write private messages to the server log'k$ /Write administrator actions to the server log'l$ Write log messages to file'BBBBDC ' Log path'G Log extension'H Log file name'N Time format'O',,H SaveS" ResetG,H, N,dO,dB y7Bi'a- -Yd- -Gf- -ti- -Jj- -sk- -Pl- -t G  ]H  d N  ^ O  ]  D{7v Dv ~wv * :~,v a/!w.v -v sS"B Hi  B8\rp-}9z@=d p@p=B Z@d p@ D8J] 5"  G -A-A]$-A7%1 has disabled nickname changing on the server.36%1 has allowed nickname changing on the server. h|7j(3*'%:j( , B w7( "%1 has enabled tournament mode."7iJ^d-f-i-j-k-a-l-G H N O  C8^|z-}='=BR BLog closed at %1B(ZB%l, %F %j, %Y, %H:%i:%s'g J8Jdaj =BNEXGEN LOG FILE'='=B1 Bengine-version: %1'=B1 Bnexgen-version: %1 Na'=BI Bserver-id : %1 _dO '=B: Bserver-name : %1ds'=B< Bserver-port : %1SO'=B; Bgame-class : %1V'=B7 Blevel-name : %1 o'=B: Blevel-title : %1'='=BS BLog started at %1B(ZB%l, %F %j, %Y, %H:%i:%s'='JdqwJ*PdpJ["J\"{@PBmn@JZ"JY"JX"JW"JV"JU"JT"P=P'JJDe G8BD  "  G -` -` ]$-`  %1 has muted all players."%1 has unmuted all players. m6aK<_w!u~a:%e_U, wUe*UUe-N|a}Ue&pUe:3-n0}Ue}YYUeU-n'YUeU>-nwY*`YF,pY: ,a}Y&wY* {aw!M w!RzHY?wY*`Yw!RY-Y-G ,G zY-wY*@YwY*@YC;Y-Y-G ,CG , {@-gFzY,pp (@): G aFzpY: G a{a~aYh( R8w!P'~zw!cv!c$rv!*$ww!*|& Y8i(@-k"  G \w\*\a/!{.\D .\ z@.\ i(%\\6 @8) "%1 has disabled tournament mode."eK8^c*|d.BdC  "mdd^ C  pC {C M  dd zM&.MM&zMMlog^  d]a{^^ ^\/az^&/^^}^&mpppp^/C .Msppm.tmp-}hsm/-}@d] ddEB6 B"Log file created, logging to: %1szdBB3 BFailed to create log file: %1C  L8 "tmp"d "log"P8e[5 -} H85 "%1 has created a new account type named: %2."N8- "%1 has modified the %2 account type."Q8c(=M '%-}ic(%-g( \8g<U-}-}( u8Pg'#Hx!Pf(Py!e(Py!x! z=d( E8R~sIb  kR- % , R fR}RCDWrwC*pCIHCwD*[DJHDRr%rW W%c[`rOJ[prWJIpcW`IOW% r%KRWtRW}prW}pdRr}[( W%KRWtRW}p[(p[KRz KIp tJ[ d rdP#0K@C,k.'A '8C,PP C,P,P,H%H,HIP[ C  Switch to %1 XHHSP% Send to URLTP- Reconnect as playerUP0 Reconnect as spectatorVP0 (Dis)allow team switchPPPM POWP C,PP,|P$ Pause game{P" End gamezP& Restart gamePP,AP0$ Allow team switchingCP0$ Allow team balancingXP)$ Lock the gameYP+$ Tournament modeW,kACXYM A--C--IBp O8, "%1 has removed the %2 account type."T8BnA-!-CC-!-gX-!-GY-!-^ jW8jL~~FD:j!%:j!&B _8a(\D  G  H C Ya(krC* C-_   L n$VYou are not allowed to kick or ban players that have an account on this server. M:l&}pMS&m)):l,qpouv _&mqpo}pU aqpouvq a}C(MNexgenJustBannedDialognqCa(%1 has banned %2 from the server.C/_$rC^C\:_%-y'-y'A_%-y _,z_b-y'_A-y _bC/-y'^_C^C\i_lC^_hC\_Tn_}b%$ X8}IC}.k-u-r}*b%b,bI-'bI mobSS-'T-'U-'V-'Ab%b,bI--u}Tb:}q b:!BbI-bI mobImbb(bS-(T-}TU-}TV-}T -u V81 "%1 has repositioned the %2 account type."e8hh'AO'zh zk-}(hk-}'' [8. "%1 has deleted the account %3 for %2."]Z8{<,z|pjqk{a]z|plk j{Iz|acpk{aI a8_(~ "  G }  Y_(Ir} * } -_   L n$VYou are not allowed to kick or ban players that have an account on this server. } &MNexgenJustBannedDialog`(-} a(%1 has kicked %2 from the server.} / @3f3333f333ff3fffff3f3f3f3333f3333333333f333333f3f33f3ff3f3f33333f3333333f3333333f333ff3fffff3f3f33ff3f3f3fffff3fffffffffff3fffffff3fffffff3fffff3f3333f333ff3fffff3f̙̙3̙f̙̙̙3f3f̙3333f3̙33ff3fff̙ff3f̙3f̙3f̙3f3333f333ff3fffff3f3f3fl8^(|.u   u J  G zu  M Y^(qrM* eM/M ju %1 has renamed %2 to %3.eu  ^8c8 C?rd*d.> lwd*A' Db8y"DyR wy* :R,ya/!w.y- wd*y |dF {dY zdc %Id#e.ke% 5&Id#e.ke& p,Id$e.ke, ,Id$e.ke, Vd"W.ke Td#C.ke( OUd#C.ke' Sd@Y.ke  W  pwy* :R,ya/!x.y- wd*y Ad[p 9Cd_p SXdap mYd[p ryk :R,IryM  :R&t }b h8 1d8Zp]-#_ Z%Z_wZ  LwVw\ Lw^w\ Lw]w\V  V{VM .lVpppunreal://^:SJ]&ZM l j8 05-L*./" 0" +" ," 1*"@;@þ}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxssssssssssssssssssssssssssssssssnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnniiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiidddddddddddddddddddddddddddddddd k8 4o8 3m8](zgf"  G S Y](IrS* S-{S-{S-{%1 has muted %2.S/%1 has unmuted %2.S/ T9[B"  F -G-GbC-y/:4,3-G-G-G-G]$%$-G+%1 has set the server in match mode. .%1 has reset the server in normal mode. g8tsG1 W M {  q8 2s8 1]8, "%1 has modified the account for %2."d 0r80 "%1 has created a new %3 account for %2."|8eKU~ %S~ ,~ !k[ E?Iw[ *2[ j~  k, ke[ -M(hk&+-Mh~ !Pheh&e%-M'(}!heheh&eh&e}!hk[ [ A8~  65-R(*./" 0" +" ," 1* ;*"`o+C@  YZ5 +Ozek;;u̿6/r`ԀC]*0rb|}~j ئ h䵢ҭǁ$#߶xc3lӟG=< 5^%sɽhUa{tr?HM&F !Wm n&fo{->8)-Lghcn_('VŰJSm9 [%miy{wx\! R4vamqpdPEFFKToBLIf[26cXQ1":Lq.7L@, D_G NA.  t8. "%1 has modified the ban entry for %2."Kw83 "%1 has modified the boot control settings."}8T(bT(L V( J9A"hPN_&F_U(<_A"KA"K__K C9S(OS(L  y83 "%1 has separated the players by their tag."Ju 4r@9i#2 K@C, '## C,# Aj#"##+ Match settings'$#,#',# B[#P  7Allow spectators to enter the game without a password]#:  !Mute spectators during the game^#N  5Switch back to last map when the server has crashed`#Q  8Automatically lock teams at the beginning of each gamea#L  3Automatically pause the game when a player leaves# B# B#" Passworda ##) Number of gamesX # @B$#& Current game_ # @B$# Qj#Q# B,# ?j#"## : i# A#4 Separate players by tag'$#A, '#,# A#',#',b#$# @CD%FD,#& XD $DD%D,DW # B$DW ,DM#7 Automatically separate by tag#B '#d#& Separate now# Qj#Q# ?# : i#A '## ',,}# Savet#~# ResetA, 'a.'A <h Send passwordX @'X ,_ @'_ ,a , iarieiQ iq$d--}t~dahX _ a []^`abD% D,DW De MI E@G9V U}5}u,  zV ?}V ?,?&%J =e,&J =:=e,,,J =:=e,,,J =:=e,,J}V ,&J%S ~uV J,&&S %W=S S ~uV J,&S %5W=:WS ,rJ}V ,&C=:W:D?J?,J W=:W:C:D?J?,J Ep:WECWJE ~8, "%1 has modified the match settings."G@A9vMNYX  R |_  R `a 5  U {[- -f]- -i^- -] `- -ya- -]b- -[v%v,vW # v _v -Gt$ Stop matchFt% Start matcht-( V9NXS}u,  zN%q=e,&q=:=e,,,q=:=e,,,q=:=e,,\%\}N\}NSpp$$\}N&S=N\&p=$S=N\&p=N\&&S=:S:D?\?,q:pGppGu:S,,&u:S,&\G F9I#h-ra* H9SYp   u%Zu,uS  uW  uu YS \9C"jNX&FXO(<XC"JC"JXXJ I9Z?X   u@Z.ae  a   L9ni!   m%Zm,mn  mW  muxJX  J_  a  [-]-^-`-a-b-%n&n,n,n D9? "%1 has modified the spawn protect ignored weapon list."N92 "%1 has modified the HUD replacement list."M9[_%   u[ ]P9H<$(,zPpjqaHN]zPpla jHIzPacpaHN O93 "%1 has modified the basic server settings."65-R(*./" 0" +" ," 1*;*"ExY@YZ5+Ozek;;u̿6/r`ԀC]*0rb|}~jئ h䵢ҭǁ߶xc3lӟG=< 5^%sɽhUa{tr?HM&F !Wmn&fo{->8)-Lghcn_('VŰJSm9[%miy{wx\!R4vamqpdPEFFKToBLIf[26cXQ1":Lq.7L@,D_GNA. ]9Qx "  F Q&QQQ&Q|=Q`=Q{ XI(-f-J(-i-M(-] -N(-y-P(-]-Q(-[-W(%_X(&_Y(,_[(,_\(b%$ <$$New settings have been saved.*%1 has modified the match settings. ' DQ9b@_,UDbkwb* :k,ba/!w.b-b s~M }i dY hZ t[ rba :k,IS:k& -G wb*(ba/!~ ba/!xt-' j9K(n>K(eL(u R95 "%1 has modified the general Nexgen settings."U9Y9 h1C?ru*u.> lwu*A' hX9B(3|4'%:B( ,M vc9[9=r=Ba/!_z.D(vE?wv*v-F%vjvj,vjuG"vvAXjuzuhuzu,(@G"zKGI_%9_z/_u@_u@&_u@V juz_uV ugV _GID",V huz_uV ug_V GID"G_8' a9My v ] F v ,E F PasswordM v b Y$  A   $Match password received: '%1'M @S8O}kU>EyTbw`*~O`%`*r`* wr*`rr*w`* wr*#}r}`~``rr~r`*s%s, wsE*~sEf~O~f%0r`*#}~}``~yfsy~O`bw`* rr*s%_s, wsE*~sEf~O~y% f% yffy}`f~Oy}`~f%fy}`Uf%0rr*#}~}rr~Tfs<<wr*f~Or1y% f% yffy}`f~Oy}`r1f%fy}`Tf e9FZKz"  F fzG\U{q\GF%H YFR.H >lR y\5H?5wH*R.H >lR y\HHA :$"The password has been send. _9@(Fcgw!g,yggiy&[ygy&FyFy(yg&%6YyF'&6YyFi',6YyFW',6YyFS',6YyFQ'%6CyF@(&6CyF}',6CyFh',6CyFU',6CyFR'6D yFl f9c:(r*a/!ha E. F@v9d9YLC  Fa/!_ F?iwF*RF-Fb%-\(R-\ b,$b.H~F/b_%-\'E:FbF z=bObFFAW1%1 has separated the players by their tag. ' E 4W9* "%1 has modified the log settings."g9< "Unable to login as administrator, invalid password."Yl9A(I2eA(uE( h9 "%1 has logged in as %2."r9xq {y/a0 xrl{Vxql%YzyySlqyppy,Sl10k q9J"EdH-| Y,@>|6DYtJ"-|'EY-|Y%-| Y,@z6DYt-|'YZ-|6DYtJ"6`Ytz'-| k9, "Connection lost!\\nReconnecting in %1..."D^}9t'|>D  2t'5zDu'<LD E:s'r5-s ^, {^Fw^F (w{wv|{s'-s'}^-s^ rg#4l@A, General Nexgen settings'$?,: i',D?,@, , R$ "Automatically update ban entries'U$ *Automatically remove expired ban entries'[$ 0Broadcast administrator actions to all players']$ Broadcast team kill attempts'^$ Enable the Nexgen message HUD'a$ "Let Nexgen handle the game start'e$ %Don't decrease score on team switch'h$ Team switch allowed by default'p$ !Team balance allowed by default'q$ Name change allowed by default'v$ $Register server in Nexgen database'B 'B 'B 'B 'B 'B 'B 'B 'B 'DC ' Game wait time (sec)'e Game start delay (sec)'d Auto reconnect time (sec)'c Max idle time (sec)'b &Max idle time in control panel (sec)'a Spawn protect time (sec)'` %Team kill damage protect time (sec)'T #Team kill push protect time (sec)'O $Auto disable match mode time (min)'L',,K SaveL Resete,d,c,b,a,`,T,O,L,e@'d@'c@'b@'a@'`@'T@'O@'L@'B s9BR- -j U- -v[- -B]- -j ^- -Za- -_e- -uh- -l p- -p q- -q v- -Fe R j d R ]c R Ab S pa S t` R }T R ~O R X L R R Dt9K'DK|wK* :|,Ka/!w.K-K sLB Ki  A:v'XiO"  J -t-v'-s-w'%u u,  -(u  FzfIzIHx'WyRfyI{f (f}f_J}F%_ _, -(_  Mt~' zRRtFRppR,tHly'i{'b%$ <$$New settings have been saved.1%1 has modified the boot control settings. ' n9* "Connection lost!\\nReconnecting now..."hu9q'3*'%:q' ,B Cx9{9 A-C?rg*g.> lwg*A' z9iv/i   gKR-U-[-]-^-a-e-h-p-q-v-gL=Je =Jd =Jc Jb Ja =J` =JT =JO =JL  @:o'}>C  2o'5zCp'<JC w9C "Warning, reboot sequence activated!\\nRebooting server in %1..."BF:j'~q>B  2j'5zBk'<IB [:_^9(u  Hz  m :_,#z:_b :_bm:_ll':_hm':_Tn':_r'b%$,%1 has modified the ban entry for %2.m ' ~9C "Idle / camper detection activated!\\nMove or be kicked in %1..."~ @H:g'{9-o ~ ,{~ Mr~ M (r~rv|~g'-o'}~ -o~  N:e'm[V B@  2e'6z@-f'@|@T' B: "Waiting [%1]"K:t N4szt s$q@|t &Ms$t &qi|t &Us$t &qs$  W@G: "Waiting..."V:b'uW,{We-b' ;WyyW%tppt,We~We,tWe~We,Wt F@kP:_'2=i mH- F,@>|6DFt_'-'EFe-6`Ftk`' 65-Y'*./" 0" +" ," 1*,*);*"́@/i~xaGi]ʾ~-,ǿyG¹p'}n[b{|WOqӌc9Kx`)eg8.tyk,vѐd"Ls{k,HU`Q2`ri*VfSZJ+%C@Wa]'XmT34;: MYVKl=<$F5 IPI&v#A6R?GI)zu71Dғ?0 (/,Nwuh :BEE>0 ))^w{uhX! &&)Vorj\OH!&&&GX__VL/,)(&/ILI/)& tGC&><G,@6DGt6`GtG J: "Ready..."rB]K@C,A.'A 'DB,&&,& B& B&% IP address'&@B '&$ Client ID'&@B 'n&k& Copyl&i& CopyLB,&&,& B,& B,m&" (Un)mute&n&" Set namer &B,&&,& B,& B,& B,& B,& B,&) Ban/kick reasonv&p& Kick&r& Ban& B,&& B,&& B,b &#$ Forever&S&'$ 'x' matchest &g&$$ 'x' daysm &B&&,w&,$ Mute all playersx&>$ $Allow players to change their name& B,g&& Show messagef&r , v,t ,t @'m ,m @'f,Amnprgb SgwxS-'t  3m  7IYB Q: "Ready [%1/%2]"R:BtTw-!-` x-!-A T:GI>eG.Am-rG*n-rG*p-rG*r-rG*Hk-rG*i-rG* rG*r  nlcr  GQnGw lGE `:@zl`kN@'k%-g 'nk%n-g  k,dzka-g 'kk.-g ka@kc['kL \'kb]'k}^'-g k U:YBt GS-m Gg- W:Y: pC?rB*B.> lwB*A' X:y\TS-y $bJ  t  g-y $bJ  m  y $BJ\.Aeyb  v  c:dmq?  Hz  d -j :G,z:Gb-j'G?-j :Gbd:GlZ':Gha':GTc':Gd'b%$&%1 has added %2 to the banlist.d ' DZ:BR#DB_ 9wB* :_,Ba/!w.B- wB*B mB"z.Ae9 nB@|.Ae  r  9 SpB@~.Ae  v 9 dr\9 gB$@  f 9 k.Aw*@w 9 6i.A3w*@E9 ]rBA :_,IrBb  :_,b -'S-(g-(YhrBS :_,b -(S-'g-(YhhrBg :_,b -(S-(g-'YrBw :_, wB*BBrBx :_, wB*BJ [ S: "Starting [%1]"j:X'N;pqW-c [ ,{[ aM|[ aX'-c'T[ i-c[ o ]]:`0 ))^w{uhX! &&OVorj\OH!&&&)GX__VL/,)(&/ILI/)& i:cfeX  H :c,#z:cb A:cb u:cb%$*%1 has removed %2 from the banlist.A ' D`& wD*DaD* ja:T'L}2&$:T'!%B _: "Online [%1]"@f: "Offline"k:ZhgRzZ [zZUb -e'-D'  -e z, "{z={zW=z#UzWZ-e'B=zz[-e-Dz^z \ /u' Root Adminz(z \ /:Bz%{d%1 has logged in as %2. / g=z   "MNexgenAccountUpdatedDialog ab%$P R$:Unable to login as administrator, invalid password. o:Yn!b2#z:Yi:Y=0:Yi l:aw "  F -G-G]$-G%1 has locked the teams.!%1 has unlocked the teams. n:_^"  F -g-g]$-g&%1 has disabled team balancing.%%1 has enabled team balancing. @~:[E"  F -C-C]$-C&%1 has disabled team switching.%%1 has enabled team switching. w:Vge8":VL %:V}6n=:VL  rJO#$K@C,OO ,,O AO AO$ Players'$i.<O A <O$ Blocked'$OB, 'j.<O A <O,XO) Block / UnblockzO6$ Block all private messagesB,OO ,,O$ Message'$YOO CO ',,OWO( Send normal PML O* Send windowed PMOO AO$ History'$EONijXzWL L -GYY, h: "Offline [%1]"p:s: ` C?rJ*J.> lwJ*A' ]r:T   %1 has been unblocked.fQj oirGi :D,wj*j-(j*!rGj :D,!wi*i-(i*rGi :D, wi* wJ*f.iJZfE~ <  %1 has been blocked.fQi ojrGj :D, wj* wJ*f.jJQfE~ >  %1 has been unblocked.fQj oi v: "Logging in"E;O'u>nO'n,on&,nbnlnhnTnnbn&bnln&lnhn&hnTn&Tnn&n  y: "Idle [%1]"x:M'~P"vPpppp[p0S,:p0S,]E)PM' {: "Muted"H;N'Y+f  P'g YN'q  F rg* zf gKopenf%1 has send %2 to %3.g/f |:L'Q$" rJ* =wi*h .ijjwj*h .j wh *{Y h -G -i/:!4!,-Fh TFG~ E  Send to %1: %2h QY J)yh eY -L'Y  }: "Protected [%1]"| :K'w+$+M~ 3  %1: %2J'K' 65-~&*./" 0" +" ," 1*?3%;*"j'@ 0 )$"&.UV/! 1ڞ(oƏu9涍az}νőmp ӮwgԤ񻖉Хᓒ¾דt|k̼Ѣjqr S]fDzsOM@'Q{ѯvnYQ*=eaf\M?5dPL=8 ,CIRC7 %AD3# @U;A;}WN| ,{| bDt| u| -J'K| -J b9G'zow!I,vIIiv&[vIv&DvDv(vI&%6YvDD'&6YvD|&,6YvDw&,6YvDn&,6YvDj&%6CvDG'&6CvDA',6CvD{&,6CvDu&,6CvDk&6D vDl J;I'I' &Jan ,Feb ',Mar 2,Apr =,May H,Jun S,Jul ^,Aug i, Sep t, Oct , Nov , Dec   M;H'Cd| YH'e  F r|*|-F-O -O|E|$ r||E|$-O*%1 has reconnected %2 as spectator.|/b'%1 has reconnected %2 as player.|/ @Q;F'|tF' &January %,February 2,March ?,April J,May V,June b,July p,August , September , October , November , December   rm #!~lC,F.A , HUD replacement classes.'$CC@C Original HUD classY Replacement HUD classU',,{ Save| RemoveY,@U,@w K;B wDb MF*FZB ,"{B  ]D . F F D >B  ]D (B B (EB ,D . F F D > D (v \;E'W&P YE'I  F rP* P-yP-yP-y-%1 has disabled team switching for %2.P/$,%1 has enabled team switching for %2.P/ @L;KvigK. F{-rK*|-rK*K(%YGrK*UGrK*rK*Y U eK(%Y U e_ K( ]Y _ ~_ =U _ ~_ =& }X;C'y\̊WC' %Sun &Mon &,Tue 1,Wed <,Thu G,Fri R,Sat   O;S; C?rm *m .> lwm *A' DR;~GD~4r~F :,vr~| :,|- m #S=. F(Er~{ :,{- m ^Y. F(  Y   U  c;B't-tNB'IJ::I&-}JJ%kc:I, $`JKLMNQ-}KKLL~MdMJN0NQk-}(-} @hT;&3U'%:& ,w i;@'qDgt@' %Sunday !&Monday 0,Tuesday A,Wednesday Q,Thursday _,Friday o,Saturday   rP#Pj.PKB,$$P'$C,$$ C$,$,$( Administrator'$* Contact address'$- Message of the day'$$$$$ Server ID'$SP$SP$SP$SP$SP$SP$# _Fwj* C,$$ A '$" Stats'$$ B$,$,$' Games hosted'$& Total frags'$' Total deaths'$% Total caps'$Sj$Sj$Sj$Sj$$ A '$( Top players'$$ C '$,$ @B$& Player name'$S%j$S&j$S,j$,$,$ FPH'$S%j $S&j $S,j $ Date'$S%j$S&j$S,j 65-~&*./" 0" +" ," 1*;0";*"#r@ڞƏu趍az}νƑmpӮugգ򺖉Х䑔בtzk̺УkprS]fDzsOM@Q{ЯumYQ=eaf]M=5=iapRK:S=xtxBF2[bcyuyyaH6;W[aYkk]bcNFHWBdOYdKNHW=hBNRKFN>dOK=BIRBAB D^;}&e dh Y}&  F rh*$- k % k ,Ca/!_$k .hjk  h z=k %1 has moved %2 to %3.h/ Xk  Y;USoa*"z  U-(U  `;c w"  F @?restart(!%1 has restarted the game. @b;YU  F/:4, I%1 has stopped the game. 65-x&*./" 0" +" ," 1*((6;*"L_@+O]<" =Ԙ O8(-&0Io>yljHrfJ.*(D2KRE95r-0vLJQLF\700`ܬeWZcbZSG?0A!_wbgqutqgZJ.zn O؇sm}Ȕ~m[6;Жaxk^\iӠ̤iUA0% M:CESdtҸ}bF1MCIhBWii6pYCBFZq»q9pV D)5EZrrS9Nf(1EXqqR6,`'@rShûJ5;QJ\tֶ}Bln(6BSgב~dP6'T-6P{tdQ?e=D.6R\grqgL?n OU05@FfWRJW6# 3D/19d?91Yp!`|jfp3'Ujj^D$  m;z FnV2U  F/:4, -z {-z  /-z -z -z %1 has paused the game.0%1 has resumed the game. l;b^P}bl-D {} (}B}B  B||BD-D'T-D T,zblblDblppbl,D}bhn-C {} (}A}A  Ad|AF-C'kR-C R,zbhbhFbhppbh,F-D -Cb'( @65-x&*./" 0" +" ," 1*%%2;*"CPS@O]ԘO8(-&0IyljHrfJ.*(DKRE95r-0vLJQLF\700ܬeWZcbZSG?0A_wbgqutqgZJ.z؇sm}Ȕ~m[6;Жaxk^\iӠ̤iUA0M:CESdtҸ}bF1CIhBWii6pYCBFZq»q9pVD)5EZrrS9f(1EXqqR6'@rShûJ5;QJ\tֶ}Bln(6BSgב~dP6T-6P{tdQ?eD.6R\grqgL?nU05@FfWRJW6D/19d?91Yp|jfpjj^ @; "Dead"65-t&*./" 0" +" ," 1*(!;*"U'I@ ap\ !xM.! EXLUb:#'-:+&"U}v,#(/:FJ/) XbU%(/FMNNPt{'WJ('JOV^-M5~RQ_F GW]lF%)$xO&:nswV&.H˛IciAK[:1XŲIdhjBƭ;[MH룙Sgfɬ:wV?D>qszVyC8 ^mz^rᘒ¨4T_Z[vɭ7eMMYU ऋ= 9o10Gu }ڿ63k`6*2ʻ@{6L|<>H1  ck;s&nx&s&T~%Y% )%Y 7SQ,0~%y% +%y 7SQ,0,~%L% %%LR@ EQ-~%m% )%m 7Sk ,0b~%n% %nSk ~%F% %F|k ~%M% %Mk ~%t% )%tS PQk h~%d% )%d 7S]",0~%j% %jS]"~%w% %wSO ~%N% %NSO&D~%l% %lqO~~%D% %DyO~%H% )%H 7Sk,05~%h%oD?k?, o%o,  )%h 7So,0j~%G% %GSk~%g%oD?k?, o%o,  %gSo(~%a%k% k, G amG pm %aG ~%A%_k% k, G AMiG PM %AG ~%i% )%i 7Sy&,0~%s% )%s 7Sz&,0  @H<p&Ztpnp& |;o&rS-z c,{cb-f|cbo&-\~clq&%-[~chr&%-f-\-[-z'c-zc {;xT':  Iz  y P P, 3P"zP=*Pz  b T- -@ ^,z^a-'QJ|^ax-@'Q^l--@ o xxwo*o"MNexgenAccountUpdatedDialogoa^ax^cyb^PP%^bv&^}bX g=^b%$.%1 has created a new %3 account for %2.yX ' f; "Match [%1]"rE-#+.\KA, '-DC '',,W SaveV Reset- \B '- B-A,'-,-,- B- B-' Server name:'-- Short server name:'-A (  MOTD line %1S&'-B )  MOTD line %1S,'-B )  MOTD line %1S,'-B )  MOTD line %1S,'F-G-%T-&T-,T-,T--,-,-,-,-( Player slots:'-% VIP slots:'-' Admin slots:'-+ Spectator slots:'-, Advertise server:'H - @B$D - @B$B - @B$A - @B$v-$-& Admin name:'-' Admin email:'-+ Server password:'-* Admin password:'-H-I-K-L-F,G, %T,&T,,T,,T,H,@I,@K,L,H @'D @'B @'A @'H ,D ,B ,A ,B n; "Client"o;BF  sG  I H  [I  M K5  U Z L5  U b %T % _&T & _,T  , _,T  , _H  R KD  R nB  R rA  R Xv- -i  hq;l&3&$:l& %B p; "Home"r;u; _C?rE*E.> lwE*A' Dt;s Ds pws * :p,s a/!w.s -s sVB Wi  is; "Settings"v;rik    rF sG %p%T &p&T ,p,T ,p,T xH BI CK EL FJH  GJD  KJB  LJA  -Mv-E\wrs%p&p,p,pxBCEFGKL-M x; "Private message";YR:u  I :Y% :Y,#z:Yaz  r Q Q, 3Q"zQ=*Qz  g q$x:Yawq*q"MNexgenAccountUpdatedDialogqa:Yc  rb:YQ1Q%:Ybm&:Y}gs:Yb:Y}b%$*%1 has modified the account for %2.r ' K<w;}:wa q-iw}wa-i F;OEgtrO*O OOD{! lUU-A[OZO*:X:-AO.Od OOOOaOnline [00:00]]b-$-m,QI%:&:,m:Qm,cmt#[OZOX`?DO@?t?Db@ht?,ng%l6D %FAm&Umgm&FmFm"gng%`*I%l6D %D@Am&mIm&DmDmI{! };6t+~tE6\?Dh@@@hvgur ph6\pg6\6$6b(c>6??64a Z(`@h@BB6b(cL?66&a c`???6h?6&a c`???6%`?r \?6&a cr ???6?6-a c?h@??6`??6-a c?h@??6h??6-a c?h@??6`@r ?6-a c?h@??qh6\`r ??qg6\`r ?\@w`*6$6l"6??6Ha`h@h@?`?`6$6b(c>6??6Ha h@h@? ? Y-x6pp(>_@h@@tb@A%Ago6@@AF@tA@?@h@&-@tA%rAIo6@@AD@bA- B<xK$kf  I :x% :x,#z:xa u$x:xawu*u"MNexgenAccountUpdatedDialoguaw:xct gxs:x2s,s&,sasc bs%sbs}(sas&ascs&c+bsOs&sbs&bs}s&}s7b%$,%1 has deleted the account %3 for %2.wt ' ~;Ix#I$Ib(c>I?hI4a i8`@t?BBIb(cL?Iht?I&a c`???IhI-a c?t???I`?hI-a c?t???btb@IOI$I,RIbbhI ai&( z; "Game"E<]JF^  J :], #z:]=C-c8z:]&=:]&, -c :], l:]-cd:]&d:]&r,"{raw Orlbrd-e' Ordbrl-e'rD:]=El=l=d=d=EElqlqdqdqEElilididiEElWlWdWdWEb%$^-e%$/%1 has repositioned the %2 account type.D ' @<qow;Ol6D Jx-o O@w?O?,I@@@Q?wCYh&qOq$l %9l ,qYg&ql 6CJR-o O@qb(q333?6q=D<6qwQ6q=D<6qwQ6q=D<6qwQqal 6YJ(qal 6YJQ]&YQl  c"@L<kIESf  J :k,  :k&#z:k= `Q,"{Qa OQ:k bQ%-^'VV OQ:k+bQ OQ&-^'QfI:k=Q:kVQ, "{Q=TQ&, Q=QqQiQWLQ=Q&=QqQ&qQiQ&iQWQ&WQb%$-^%$*%1 has removed the %2 account type.I ' A< "Players"Z^<d"D3rh-c"Od"b"a"`"_"^"`-c" ab"a"`"_"^"fd" F< "Moderator"I< "Match control"T<d&J6{r-Z r ,hzr e-Z'r eppd&,e&r y@-f&or -Z \<HNC?Aw*[N\[%-Fg  Spectatorg n$g  g=[*{{g {g  ntitleg A J< "Match setup"M< "Server"N< "Info"f -1P< 0Q< 1rR<W#,oK@C,,,WW BW BWA 'W,W,W ',,W @CW$ User name'W' Account type'W% User title'sWGWON WW ,,W ,,W ',,WT%T,NT eyzNTH WX$ >  Privilege %1 (not defined).ST&TH W.$N~N ,&TxW  UpdateZW Addl W  DeleteAA Online'$U.[A [ Offline'$m.A s, N ,GQ(Umxl G~MJ U<a&Wҟus-N O , izO =-N'O =a&O qb&O ic&pO  V<E -F' a<F,-_'WHidden AdminA,B,C,D,E,F,G,H,K,M-P-G-B'-Y'-t (]../Logsd log^ Pnsc_%Y_%m_%d_%H_%i_%s] P[%d/%m/%Y %H:%i:%s] S<k MoGZG(l Sk , "{k  =G)lk  =Sk k @ O< "Ban control"W<O~kYmZm*9O,"{O aP.)U#\O a/rP*R. m F R(OR>ppp[ g=O] O cO(mxD j"@X< "Accounts"h<MI"  I a{T:M=  T:Mq^&:Mi  _&:MW X`&b%$H+%1 has modified the %2 account type.T ' ]Y<P<"zPpjI.)qUPRUxS NIE"S%I-l'Im=S~m \Srm~I-'UIm*~kzPplI.)U _PwI*I-l~. m F ~(:Im~>ppp[ gIm] :Im cmxrUI~-'m~U*U jPzPacpUPR i<k"kIoh-j" $`k"i"h"g"f"e"`-j"Qi"h"g"f"e"fk" 65-t&*./" 0" +" ," 1*' ;*"%hm@@ap\xM.! EXLUb:#'-:+&U}v,#(/:FJ/XbU%(/FMNNPt{'WJ('JOV^-M5~RQ_F GW]lF%)$xO&:nswV&.H˛IciAK[:1XŲIdhjBƭ;[MH룙Sgfɬ:wV?D>qszVyC8 ^mz^rᘒ¨4T_Z[vɭ7eMMYUऋ= 9o10Gu}ڿ63k`6*2ʻ@{6L|<>H1 65-Z&*./" 0" +" ," 1* ;*ƥ"#@I LTH 5j6z2J 3M.}KN."Om/'Qq-7Pt&~py|Y(4nXgaZxs?rokdc[`A0l]wvhWS)+u{_e)$U ERB,=iV 8D!1@,Cb<%> \:*9f^F;G# b<QGO S Wpp*=O.NexgenHUDWrapper-Q f ,s|f ]W-Q'zf ]f ]W-Q'f : d<HnO^ ,jG$^ ,kF6^ ,lE^  jb Sg<c I,xc  S z wpWAPlayerWVIPA,BWLevel 3 AdminA,B,C,D,FL3 AdminWLevel 4 AdminA,B,C,D,F,GL4 AdminWLevel 5 AdminA,B,C,D,E,F,G,H,KL5 AdminWLevel 6 AdminA,B,C,D,E,F,G,H,I,K,LL6 AdminWLevel 7 AdminA,B,C,D,E,F,G,H,I,J,K,LL7 AdminsI [M {-i |get IpServer.UdpServerUplink DoUplinkTrue-f(K=n$r$X=-j 'j $ ]$A$ p,(t,x}$ ~$X $-j (%_&_,_,_%vBotpack.Translocator,PS&vBotpack.SniperRifle,S-t(n-s'-G(|$`${-f'-i'-] '-y'-](-[(-l '-p '-q 'R$-Z'%]ppBotPack.ChallengeHUD=c .NexgenHUDxDM&]ppBotPack.ChallengeTeamHUD=c .NexgenHUDxTDM,]ppBotPack.ChallengeCTFHUD=c .NexgenHUDxCTF,]ppBotPack.AssaultHUD=c .NexgenHUDxAS,]ppBotPack.ChallengeDominationHUD=c .NexgenHUDxDOM-}'b D]<s"`DsY xrsU :Y,rwm*m-(m*Jrsm :Y,wU*U-(U*JrsG :Y&LTrsl  :Y,l - wf*f K=]Irsx :Y,x- wf*U  s B G@&B %w Gu   N  w u f!R=]UB w u ^rsZ :Y,Z- wf*T.)UEU  s B G@&&B %w Gu   N  6w u fTTUB w u  [k<TpÂw,k}k, T=,],!-S:T,"~k:T%0-Skpk:T"Iwk w<MW~C6H  J"{, &= v{{={HzM{=2 Account type %1S{&{=  M{qY&{i  [&{W X\&b%$3%1 has created a new account type named: %2.M ' o<S&ODi-['}  S&O~}/hO%V&J}O}}O&p-[(-[O~}/O%U&J}O}  }O&-[(--[O~} %O%T&J}O}}O&--[(-[O~}:|O%W&J}O}}O&-[(-[X&J}-[  @t<VUpMutator%V\{V I,[~\,%\\~\,WppV,\IMWMutatorIV\ @e<n  Waiting [%1]S!x6p,%R6{, I"6E, P"M:!4!&B![ 1!-^6^, W  Ready [%1/%2]R!YR!ZX6^, Ready...6p,, R-6f,'6{, I"6E, P"Mv:!4!,B!Z 6^, ?  Starting [%1]S!x6p,,R-6f,'6{, I"6E, P"M :!4!,B!Y 6^, Ended6p,,R6{, H(6E, F(M{B!X 6^, Paused6p,,R-6f,'6{, D(6E, C(M  -GB!W JRJ%?J,?,?&WJ,<XD?J?,<Cppp0SW,:p0SX,CppR `/R |6^, )  Match [%1]C6p,,R6{, `<6E, q<MB!V JJ%WJ,<XD?J?,<Cppp0SW,:p0SX,Cppp0S,:p0S,6^, *  Online [%1]C6p,,R6{, H(6E, F({@-6f, Y ,wY @I*Y @I-BY @I8lB6^,6p,6{,6E,{Y _-6f,;{, 65-Z&*./" 0" +" ," 1* ;*ƥ"|,-@ILTHjzM}NmQqPt~py|YnXgaZxsrokdc[`l]wvhWSu{_eUERiVb\f^FG m<s<]ea0:wU*Y:.)Um~swm*Y. m(~YY YF=G y!{TournamentGameInfo%G F {G  E, HG z  F F HXppppG ,H,F EFXTournamentGameInfoEG F % r<v[PM] %3 %1: %2RGR&R u<mJI7Q ]-[ s-mQ %sG-mx--m -[ zQ l --m -[ zQ Z--m-[ B-[ -ms# Q  cG&S OQ &-[ -ms" .)UQGS&s G S ,@`\ @Q J=I&y"34A  G h-b e  YI&6-G-i/:4, -Fe -F  F  G \ -_ \ {2we *`.e >l`Vz \ f-b  G\Dpppp / -> e /: f$ x<zz#>I Oz%yz by Oz qJyJ p<@=u]6{@ v86E@ S9~-^ e!U 6^@ Logging in6p@,R:w%e!T qSw6^@ (  Idle [%1]q6p@,R6{@ a;6E@ e;-6f@'l:v %:{ %:i%e!S K=:v :{ :i6^@ .  Protected [%1]RK6:{ %6p@, RI6p@,R6{@ C;6E@ Z;-{!-` e!R 6^@ Muted6p@%R6{@ g;6E@ _<_%e!Q 6^@ Dead6p@%Re!P 6^@{-F6p@,R6{@ O:6E@ b:6p@,Rj@-6f@z ,wz @I*z @I-Bz @I8ge6^@6p@6{@6E@jz -6f@;j@ y@@`}<H&A~pH&,pG&,% [< "Account types"N=W a ^zW \ foreverQ|W &M\ forW &matches|W &U\ untilkW &\ forever\  A=iL#Dx-]JiG@W ]-F&W %-\syib i%-\b W  }b  n$b  n=i&1iZ-'x-'-\Z-'x-zW -]qiZ--]qix-'N  b N Gi% i] fi%D-\] W  bc] % q] i& qE%vE,I E ezI EH -'lEH -i%0-]#I ~I ,EH -] I ~I ,E G=iNStnO  U Dzt%Dt, t] &t!IwkZ Xget Engine.GameInfo GamePasswordb Xget Engine.GameInfo AdminPasswordJAPAllowed to play on server.JBPVIP slot access.JCPAdmin slot access.JDPPassword immune.JEPCan be idle.JFPGame supervisor.JGP Moderate.JKPSetup matches.JHPBan operator.JIPManage accounts.JJPServer administrator.JLPBan registered players.JMPHidden admin.'yUP R{P  (P iP t{it%zWWStWppW,St#O C O O ~O /&nO gVt%-Z t, {tF (tFaP u|ga-Z'i=t|t-G:`:|`$-G(-v}{-C(b H=c7LkB(r*a/!ha P.-I%' I=[l'i,[$*:[, j[[ L=kgMq P=`w& "  J Kz``s][%[%oW%W%T%T%m %m %[, [, W,W,T,T,m ,m ,[WT%[,s`I J%_I&_H,_,_|[xM vZ  Xub  XsK=[n=Wr=TX=m -i -lb`JIH|xvset Engine.GameInfo GamePassworduset Engine.GameInfo AdminPasswords[WTm $-lset IpServer.UdpServerUplink DoUplink TrueXset IpServer.UdpServerUplink DoUplink False%$ <$$New settings have been saved.1%1 has modified the basic server settings. @S=mj e.:mx:mStm Z=A& \+  A&B&C&D&E&  E=Lq;N?L%0&GL& q n%Y=V .8-!  Client ;client .8,!  Home O$ client d M$ .87!  Private message i client .8)!  Game ;gamew  f* .8<!  Info  f game  G .8/!  Moderator [$ game  Fw  e* .8E!  Match control  e game  K .81!  Match setup X$ game .8-!  Server ;serverw  g* .8>!  Info  g serverF  H .83!  Ban control K$ server  I .80!  Accounts _$ server  J .85!  Account types G$ server .8?!  Settings ;serversettingsserver .8<!  Basic ^$ server,serversettings .8L!  Nexgen Inexgensettingsserver,serversettings .84! Z$ server,serversettings,nexgensettings .84! T$ server,serversettings,nexgensettings .84! Q$ server,serversettings,nexgensettings .84! \$ server,serversettings,nexgensettings .8C!  Boot control L$ server,serversettings .8$!  About F$ O=zDLNQ2z.)Uwz*^ NzE^%z-l'zm=^zX g=^z-l(zm$zX n$z.)zUx \=@&"Hc-@&&%  hR=P3mRU]0:P &~MJ[:P ,~J @U=X= OXC?rf*f.> lwf*A' _=%QuSQ-M b, G|bU %-M'bU Nb [=~%XNX<~% %red &blue ),green 7,yellow   ]=1 "Write administrator actions to the server log"e=|%^ }|%  b=, "Write private messages to the server log"~<fpNi?Db>DODa6^fnk%n]nr6{f*n@iAHnH@@inA@@in l=YZXcUY a-U d, WzdU -U'dU Y^d h 0.60`= " -,."f=) "Write chat messages to the server log"b@n=d=qfZ$P%5P,@PeP@VUP%}P, @PFPOP%P,@PMP@O @Vg@Vf@:i@y@Ve@ g=+ "Write system messages to the server log"i= "Time format"j= "Log file name"k= "Log extension"v= "Log path"p=w%U? LC-c b, 9|bU w%-c'@b-c a= "\\n"q=[tC:[x[ $sI [M Z {:K:n:r"-i %,_ $%7, = %l, q>%, is%, W $%,a%H,c%w,L O%,b~%,} $%,b%S,l%%,hZ%,T%, }$"-t"-sIHWli Y$"-f"-G:|:`"-i"-] "-y"-]"-[%V,_( $:Ap:}:~:X "-j "-j "-v"-Z:j :]"-l "-p "-q :Rt"-u"-_"-B"-F $%,v $%,] $ "-G"-t"-J"-s"-P"-Y"-t ]d ^ ]   ^s= 10r= "S"J> "P"t= ","w= "?restart"Nt%| ?`S ?`*S u%Uv%gaS hcs cU@?,T.P ghS UT t%aS \T G>) "Write Nexgen events to the server log"U  "open"g}=y=n* ' @{=@H>|=T u=jmlknjp@kOajO.P lnpO l=A>B>C>@~=qd]hkLqjMkaK.P hLjkK s%Kk&hMj@kMqk.P hLjk D>P> "Write log messages to file"X>F>la hL> 2M> 1V> 0K>E>r%)#$I%IZtklsw@kFamr%{mz~m\nz%[mm[mzmmz}\n{[{o[I{%B[[2B[{&[[{&m.P tlwFm Bm kq%mp%lF[sl R>* "Write log messages to console (stdout)"N>S> "Log settings"e> ""Q>T>@Y> 6U>]>W>g k> 8@Z>@[>`>\><  O>|o&@,}|PzP&l~ -,.|P&%?P??P-P&PP c>^>Lq Pn%a>3eO _>o%9C/o%a g> "Replacement HUD class"d>}nYg3N~y%}ka }{R {a x%z.P }a R ~z zk&}{a }R @k{R @k}a ~.P }a R ~R , s> "Original HUD class"o>hN  }l> 9m> 8n> 7p> 6r>i>|B'  |> 5f>@y=8@m%l%Is Qcs Ts ?,Uc@IQTUQ l]0-a  t> "HUD replacement classes."v> "Ignore alternate fire"iz> "Ignore primary fire"Y }>w>fO r *(1 M q(Y %Y , Y  (] %,Y  (S t=Y (Y 8' q>j%4q;q.wq*wq-p ww*-pwwww-wwK-p.N-j% > "Weapon class"^=hq>U-6fhN ?l?,I@@@x?N CT?Db>Me%Tw6{h*AoJoN hJAN h?DoA@g$gl"gMN g+a6{hJJAAw6Eh*g$gMN g+a6EhJJAAMTJN hob@g$g6ph-6fhgb(g333?6g=D<6gN x6g=D<6gN x6g=D<6gN xgMN gOga6^h( A? 4F?] `] %M] ,Cw] I*] Ia] I*] ::$yw * a *w!*!a!*wN*NaN*w * a * y>i%MK?|i%true G?' "Weapons ignored by spawn protector."L? 3C?@D?@E?@@P?bTtr-w.*.8TbXpo,woI*oITbX- H?& "Auto disable match mode time (min)"I?% "Team kill push protect time (sec)"N?' "Team kill damage protect time (sec)"c "Reconnect"ZJ?A?!WZAk)h%A&$9A)g%A%$cf%,@ W? 2NK?d;N RNdiPrdc :i,Reconnect4 O? "Spawn protect time (sec)"Q?( "Max idle time in control panel (sec)"Y?d%d^p.;.8spluginclientsettingsrp*p.;.8<! Settings Ipluginclientsettingsclientp !d% R? "Max idle time (sec)"S? "Auto reconnect time (sec)"T? "Game start delay (sec)"U? "Game wait time (sec)"\?& "Register server in Nexgen database"ZjA ?"KZA k)b%A &$9A )a%A %$9A gnA `%Bj_%,@g,  e? 1NV?lD Nlnrlj :n,4c  g a.h> l{c wa*a hc Z?c%L Jq.;.8spluginsettingsrq*q.;.8D! Plugins Ipluginsettingsserver,serversettingsq !c% [?mj8K]Rjm(dNamem' ]?_&|;ppget  S .NexgenConfigExt bInstalledT'I|;ppget  S .NexgenConfigSys bInstalledT'MNexgenResidentConfigDialog ^?" "Name change allowed by default"k?^%z`a/!_-o.-.-(`Rx j:^%a/!_.--o _?# "Team balance allowed by default"`?" "Team switch allowed by default"a?! "Enable the Nexgen message HUD"b?2 "Broadcast administrator actions to all players"c?$ "Let Nexgen handle the game start"g?' "Don't decrease score on team switch"Z`|?"Z|k)\%|&$9|)[%|%$9|`d|Z%@Brd|Y%@B L@ 0@d?u#< M#zu` X%7` ur V% h? "Broadcast team kill attempts"l?, "Automatically remove expired ban entries"j?@@s?W%I# IW%]%-I ' m?$ "Automatically update ban entries"n? "General Nexgen settings"v? "Copy"w "Reconnect"Zo?B?!WZBk)U%B&$9B)T%B%$wS%,@ Np?y;R RNy{Pryw :{,Reconnect4 t?q?h--{!-`  G u?]?)-^ )-Fn< }gR }?H<*|2w.*.8<HFCzY,wYI*YI<HFC2 w? "Show message"x?& "Allow players to change their name"@@ "Mute all players"I "Reconnect"Zy?a ? bZa k)R%a &$9a )Q%a %$9a IP%,@ N Nz?J;O RNJRPrJI :R,Reconnect4 ~?|Uj|$:|, ;:|~$:| (] :|w# t|:|O":|~@(.8 3|r%r,wrI*rI 3|||$h:|,^:|}%!d:|}:|}%L|!d| N@OLyv!dL.8 LOsQ,wQI*QI LO5:O}L O%A@ "Ban"C@ "Kick"Z?c ?OZc k)O%c &$9c )N%c %$9c  E@ "Set name"{>R H$j3R -R -|,x%:R :R ,|:R |,| F@ "(Un)mute"G@ "IP address"Q@ "Client ID"W "OverrideClass"H@ "Botpack.CHSpectator"I@ "Reconnect"ZJ@f ?$zbZf k)L%f &$9f )K%f %$9f WJ%,@ I% ","NK@X># NXYrXW :Y,(b"OverrideClassBotpack.CHSpectator'Reconnect4 d@y3aѭKIU:yw:ywU:yO"M%:y~@' |T TS@ ""T@ "Send password"U@ "Stop match"V@ "Start match"W@ "Separate now"]@! "Automatically separate by tag"Z "Password"X@ "OverrideClass"Y@ "Reconnect"Z@ "Botpack.CHSpectator"Z[@J ?)]ZJ k)H%J &$9J )G%J %$9J DnJ F%BCD%,@ZE%,@D, _@ "Separate players by tag"@\@B%#I LD$ i 2_PasswordC-MB% h@5 "Automatically pause the game when a player leaves"a@@b@@@N^@E[ 'NEDrEZ :D,i ]_i $E_PasswordD i bReconnect4%rEC :D,C-(b"OverrideClassBotpack.CHSpectator'Reconnect4 y@C%>A{_-I T ,UwT I*|T IlC%-I'\T w-IT Iy* fiF1RIFF> WAVEfmt "VDdata #y[ 9 MJ \4][Y "*/.)!ɖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖζL̲ǨQNi .28<+:2-'wǖ)ۋͻS@݌& z'./1.(xJ] TO .@p ]@l.]U^  (  ^1LLISTBINFOICRD 1997-04-25IENG J@KER !ISFTSound Forge 4.0o@Fd)JB:Fa/!{ w*t.FF HTi@: "Automatically lock teams at the beginning of each game"j@7 "Switch back to last map when the server has crashed"k@# "Mute spectators during the game"l@9 "Allow spectators to enter the game without a password"q@ "Current game"B 60Zm@j?$"Zjk)@%j&$9jadj$B%Bdj~$B&Bj,Bj,Bj,Bjb}$,@ c2::$a  @n@|$#J za {$@ p|$\n@{@  o,X~@ \nE@ X@ @ X}\no%~,<~ZEoE~E%|EE|EEEEEoB  ||zE o,%XoxX,XBXK r@ "Number of games"u@ "Match settings"t@@o@z@ "Players"Z/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenUtil * $VERSION 1.15 (2-8-2008 15:14) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Utility class. Contains some usefull general purpose functions. * **************************************************************************************************/ class NexgenUtil extends Object; var float version; // Nexgen version number. var int versionCode; // Internal version number. var int internalVersion; // Internal version number. var string packageName; // Name of the Nexgen package. var string countryFlagsPkg; // Package containing the flag textures. const keyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const keyFormat = "5-5-5-5-5-5-4"; //const dateFormat = "mm/dd/yyyy"; const nexgenCommand = "NSC"; const separator = ","; const assignment = "="; const escapeToken = "\\"; const illegalFileNameChars = "\\/*?:<>\"|"; /*************************************************************************************************** * * $DESCRIPTION Generates a new unique key. * $RETURN A string containing a new unique key. * **************************************************************************************************/ static function string makeKey() { local string key; local int index; local string cs; local int size; local int count; for (index = 0; index < len(keyFormat); index++) { cs = mid(keyFormat, index, 1); if ("0" <= cs && cs <= "9") { size = int(cs); for (count = 0; count < size; count++) { key = key $ mid(keyChars, rand(len(keyChars)), 1); } } else { key = key $ cs; } } return key; } /*************************************************************************************************** * * $DESCRIPTION Replaces a specified substring in a string with another substring. * $PARAM source The original string, which is to be filtered. * $PARAM oldStr Substring in the original string that is to be replaced. * $PARAM newStr Replacement for the substring to be replaced. * $RETURN The specified string where all occurrences of oldStr are replaced with newStr. * **************************************************************************************************/ static function string replace(coerce string source, coerce string oldStr, coerce string newStr) { local bool bDone; local int subStrIndex; local string result; local string strLeft; strLeft = source; // Replace each occurrence of oldStr with newStr. while (!bDone) { // Find index of oldStr in the part not examined yet. subStrIndex = instr(strLeft, oldStr); // Update examined and unexamined parts. if (subStrIndex < 0) { bDone = true; result = result $ strLeft; } else { result = result $ left(strLeft, subStrIndex) $ newStr; strLeft = mid(strLeft, subStrIndex + len(oldStr)); } } // Return the filtered string. return result; } /*************************************************************************************************** * * $DESCRIPTION Formats the given string by inserting the specified strings into the proper * positions. The positions are indicated by the "%n" tags, where n is number of the * string to insert. * $PARAM source The string that is to be formatted. * $PARAM str1 String number 1 to insert. * $PARAM str2 String number 2 to insert. * $PARAM str3 String number 3 to insert. * $PARAM str4 String number 4 to insert. * $RETURN The formatted string. * **************************************************************************************************/ static function string format(string source, optional coerce string str1, optional coerce string str2, optional coerce string str3, optional coerce string str4) { local string formattedStr; formattedStr = replace(source, "%1", str1); formattedStr = replace(formattedStr, "%2", str2); formattedStr = replace(formattedStr, "%3", str3); formattedStr = replace(formattedStr, "%4", str4); return formattedStr; } /*************************************************************************************************** * * $DESCRIPTION Removes leading and trailing spaces from the given string. * $PARAM source The string for which the leading and trailing spaces are to be removed. * $RETURN The original string with all spaces removed from the front and back of the string. * $ENSURE len(result) > 0 ? left(result, 1) != " " && right(result, 1) != " " : true * **************************************************************************************************/ static function string trim(string source) { local int index; local string result; // Remove leading spaces. result = source; while (index < len(result) && mid(result, index, 1) == " ") { index++; } result = mid(result, index); // Remove trailing spaces. index = len(result) - 1; while (index >= 0 && mid(result, index, 1) == " ") { index--; } result = left(result, index + 1); // Return new string. return result; } /*************************************************************************************************** * * $DESCRIPTION Fills the given string from the left until it has a minimum length. * $PARAM source The original string. * $PARAM minLength Minimum length of the string. * $PARAM fillStr String used to fill up the original string. * $PARAM maxLength Maximum length of the string. * $REQUIRE minLength >= 0 && len(fillStr) > 0 * $RETURN The original string filled to a minimum length. * $ENSURE minLength <= len(result) && (maxLength >= minLength? len(result) <= maxLength : true) * **************************************************************************************************/ static function string lfill(coerce string source, int minLength, string fillStr, optional int maxLength) { local string result; // Add leading string until minLength is reached. result = source; while (len(result) < minLength) { result = fillStr $ result; } // Cut off. if (maxLength >= minLength && len(result) > maxLength) { result = right(result, maxLength); } // Return string. return result; } /*************************************************************************************************** * * $DESCRIPTION Fills the given string from the left until it has a minimum length. * $PARAM source The original string. * $PARAM minLength Minimum length of the string. * $PARAM fillStr String used to fill up the original string. * $PARAM maxLength Maximum length of the string. * $REQUIRE minLength >= 0 && len(fillStr) > 0 * $RETURN The original string filled to a minimum length. * $ENSURE minLength <= len(result) && (maxLength >= minLength? len(result) <= maxLength : true) * **************************************************************************************************/ static function string rfill(coerce string source, int minLength, string fillStr, optional int maxLength) { local string result; // Add trailing string until minLength is reached. result = source; while (len(result) < minLength) { result = result $ fillStr; } // Cut off. if (maxLength >= minLength && len(result) > maxLength) { result = left(result, maxLength); } // Return string. return result; } /*************************************************************************************************** * * $DESCRIPTION Parses the given command string. * $PARAM cmdStr The string containing the command and parameters. * $PARAM cmdName Name of the parsed command. * $PARAM args Arguments of the parsed command. * $RETURN True if the command is a Nexgen command, false if not. * **************************************************************************************************/ static function bool parseCommandStr(string cmdStr, out string cmdName, out string args[10]) { local string cmd; local int index; local int argCount; local string cc; local string lc; local int recStart; local int recEnd; local bool bRecStr; // Check command string. cmd = trim(cmdStr); index = instr(cmd, " "); if (index < 0 || caps(left(cmd, index)) != nexgenCommand) { // Not a valid command. return false; } // Get command name. cmd = trim(mid(cmd, index)); index = instr(cmd, " "); if (index < 0) { // No arguments, but it still is a commmand. cmdName = cmd; return true; } // Get command arguments. cmdName = left(cmd, index); cmd = mid(cmd, index) $ " "; // The extra space is to make sure the last argument gets stored. index = 0; recStart = -1; recEnd = -1; while (index < len(cmd) && argCount < arrayCount(args)) { // Fetch current character. cc = mid(cmd, index, 1); // Handle current character. if (cc == " ") { if (recStart >= 0 && !bRecStr) { // End of current argument. recEnd = index; } } else if (cc == "\"") { if (recStart < 0) { // Start recording a new argument. recStart = index + 1; bRecStr = true; } else if (lc != "\\") { // End of current argument. recEnd = index; bRecStr = false; } } else { if (recStart < 0) { // Start recording a new argument. recStart = index; } } // Store recorded strings. if (recEnd >= 0) { args[argCount++] = replace(mid(cmd, recStart, recEnd - recStart), "\\\"", "\""); recStart = -1; recEnd = -1; } // Continue with next character. lc = cc; index++; } // Yeah, it's a command. return true; } /*************************************************************************************************** * * $DESCRIPTION Adds a property to the given properties string. * $PARAM propStr The string where the property is to be added to. * $PARAM propName Name of the property to add. * $PARAM propValue The value of the property that is to be added. * $REQUIRE propName != "" * $ENSURE new.getProperty(propStr, propName) == propValue * **************************************************************************************************/ static function addProperty(out string propStr, string propName, coerce string propValue) { local string formattedValue; // Format value (in case it includes the separator tokens). formattedValue = replace(propValue, separator, escapeToken $ separator); // Update property string. if (propStr == "") { propStr = propName $ assignment $ formattedValue; } else { propStr = propStr $ separator $ propName $ assignment $ formattedValue; } } /*************************************************************************************************** * * $DESCRIPTION Reads a property from the given properties string. * $PARAM propStr The string where the property is to be read from. * $PARAM propName Name of the property to read. * $PARAM propDefaultValue The default value of the property to return if the property * string doesn't contain the specified property. * $REQUIRE propName != "" * $RETURN The value of the specified property in the given property string. * **************************************************************************************************/ static function string getProperty(string propStr, string propName, optional string propDefaultValue) { local int index; local bool bFound; local string remaining; local string saved; // Search until property is found or there are no properties left. remaining = propStr $ separator; index = instr(remaining, separator); while (!bFound && index >= 0) { // Check if the separator is preceeded by a escape token, i.e. a formatted value. if (index - len(escapeToken) >= 0 && mid(remaining, index - len(escapeToken), len(escapeToken)) == escapeToken) { // Escape token found, continue with next separator token. saved = saved $ left(remaining, index - len(escapeToken)) $ separator; remaining = mid(remaining, index + len(separator)); index = instr(remaining, separator); } else { // Property separator found, check name. saved = saved $ left(remaining, index); if (left(saved, len(propName) + len(assignment)) ~= (propName $ assignment)) { // Property found. bFound = true; } else { // Property name does not match continue search. saved = ""; remaining = mid(remaining, index + len(separator)); index = instr(remaining, separator); } } } // Return result. if (bFound) { return mid(saved, len(propName) + len(assignment)); } else { return propDefaultValue; } } /*************************************************************************************************** * * $DESCRIPTION Computes the date 'daysToCount' days after the specified date. * $PARAM daysToCount Number of days after the specified date. * $PARAM year Year of the starting date. * $PARAM month Month of the starting date. * $PARAM day Day of the starting date. * **************************************************************************************************/ static function computeDate(int daysToCount, out int year, out int month, out int day) { local int daysRemaining; daysRemaining = daysToCount; // Add years. while (daysRemaining > daysInYear(year)) { daysRemaining -= daysInYear(year); year++; } // Add months. while (daysRemaining > daysInMonth(year, month)) { daysRemaining -= daysInMonth(year, month); if (++month > 12) { month = 1; year++; } } // Add days. if (daysRemaining > daysInMonth(year, month) - day) { day = daysRemaining - daysInMonth(year, month) + day; if (++month > 12) { month = 1; year++; } } else { day += daysRemaining; } } /*************************************************************************************************** * * $DESCRIPTION Gives the number of days in the specified year. * $PARAM Year The year for which the number of days has to be returned. * $RETURN The number of days in the specified year. * $ENSURE result == isLeapYear(year) ? 366 : 365 * **************************************************************************************************/ static function int daysInYear(int year) { if (isLeapYear(year)) { return 366; } else { return 365; } } /*************************************************************************************************** * * $DESCRIPTION Gives the number of days in the specified year and month. * $PARAM Year The year for which the number of days has to be returned. * $PARAM Month The month for which the number of days has to be returned. * $RETURN The number of days in the specified year and month. * $ENSURE 28 <= result && result <= 31 * **************************************************************************************************/ static function int daysInMonth(int year, int month) { switch (month) { case 1: return 31; // January case 2: if (isLeapYear(year)) return 29; else return 28; // February case 3: return 31; // March case 4: return 30; // April case 5: return 31; // May case 6: return 30; // June case 7: return 31; // July case 8: return 31; // August case 9: return 30; // September case 10: return 31; // October case 11: return 30; // November case 12: return 31; // December }; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given year is a leap year. * $PARAM year The year for which has to be checked if it is a leap year or not. * $RETUN True if the specified year is a leap year, false if not. * **************************************************************************************************/ static function bool isLeapYear(int year) { return (year % 400 == 0) || (year % 4 == 0) && (year % 100 != 0); } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given IP address is valid. * $PARAM ipAddress The IP address that is to be checked. * $RETURN True if the specified IP address is valid, false if not. * **************************************************************************************************/ static function bool isValidIPAddress(string ipAddress) { local bool bValid; local string remaining; local int index; local int currentSection; local string section; local int char; // Check each section. bValid = true; currentSection = 1; remaining = ipAddress; while (bValid && currentSection <= 4) { // Get section split point. index = instr(remaining, "."); // Split head section from tail. if (currentSection == 4 && index > 0) { // Already at byte 4, but still some sections remaining. bValid = false; } else if (index < 0 && currentSection != 4) { // Premature end of address. bValid = false; } else if (index > 0) { section = left(remaining, index); remaining = mid(remaining, index + 1); } else { section = remaining; remaining = ""; } // Check section. if (bValid) { // Check section length. bValid = section != "" && len(section) <= 3; // Check section characters. index = 0; while (bValid && index < len(section)) { char = asc(caps(mid(section, index, 1))); bValid = asc("0") <= char && char <= asc("9"); index++; } // Check section value. bValid = bValid && int(section) < 256; // Section check completed. currentSection++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given client ID is valid. * $PARAM clientID The client ID that is to be checked. * $RETURN True if the specified client ID is valid, false if not. * **************************************************************************************************/ static function bool isValidClientID(string clientID) { local bool bValid; local int index; local int char; // Check length. bValid = len(clientID) == 32; // Check characters. while (bValid && index < 32) { char = asc(caps(mid(clientID, index, 1))); if (!(asc("0") <= char && char <= asc("9")) && !(asc("A") <= char && char <= asc("F"))) { bValid = false; } else { index++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified key is valid. * $PARAM key The key that is to be checked. * $RETURN True if the specified key is valid, false if not. * **************************************************************************************************/ static function bool isValidKey(string key) { local bool bValid; local int formatIndex; local int keyIndex; local int size; local int count; local string cs; bValid = true; // Check format. while (bValid && formatIndex < len(keyFormat)) { cs = mid(keyFormat, formatIndex, 1); // Check format token. if ("0" <= cs && cs <= "9") { // Check key characters. size = int(cs); count = 0; while (bValid && count < size && keyIndex < len(key)) { if (instr(keyChars, mid(key, keyIndex, 1)) < 0) { bValid = false; } else { count++; keyIndex++; } } } else { // Check literal token. if (mid(key, keyIndex, 1) != cs) { bValid = false; } else { keyIndex++; } } // Continue with next format token. formatIndex++; } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Serializes the specified date to a compact date description string. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE (1 <= month && moth <= 12) && (1 <= day && day <= 31) && * (0 <= hour && hour <= 23) && (0 <= minute && minute <= 59) * $RETURN A description string of the serialized date. * **************************************************************************************************/ static function string serializeDate(int year, int month, int day, int hour, int minute) { return lfill(year, 4, "0") $ "_" $ lfill(month, 2, "0") $ "_" $ lfill(day, 2, "0") $ "_" $ lfill(hour, 2, "0") $ "_" $ lfill(minute, 2, "0"); } /*************************************************************************************************** * * $DESCRIPTION Parses the given date string. * $PARAM dateStr The date string to parse. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $RETURN True if the specified date string was valid, false if not. When false is returned * the outcome (the date) should be ignored. * **************************************************************************************************/ static function bool readDate(string dateStr, out int year, out int month, out int day, out int hour, out int minute) { local bool bValid; local string remaining; local int index; bValid = true; remaining = class'NexgenUtil'.static.trim(dateStr); // Parse year. index = instr(remaining, "_"); if (index >= 0) { year = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } // Parse month. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { month = int(left(remaining, index)); remaining = class'NexgenUtil'.static.trim(mid(remaining, index + 1)); } else { bValid = false; } } // Parse day. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { day = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse hour. if (bValid) { index = instr(remaining, "_"); if (index >= 0) { hour = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse minute. if (bValid) { minute = int(remaining); } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Splits the head element for the tail in given string list. Elements are separated * by the separator token. * $PARAM list The list that is to be split. * $PARAM head The first element in the list. * $PARAM tail The remaining elements in the list. * **************************************************************************************************/ static function split(string list, out string head, out string tail) { local int index; index = instr(list, separator); if (index < 0) { head = list; tail = ""; } else { head = left(list, index); tail = mid(list, index + len(separator)); } } /*************************************************************************************************** * * $DESCRIPTION Splits the head element for the tail in given string list. Elements are separated * by the separator token. * $PARAM list The list that is to be split. * $PARAM head The first element in the list. * $PARAM tail The remaining elements in the list. * $PARAM separator The separator token to split on. * **************************************************************************************************/ static function split2(string list, out string head, out string tail, string separator) { local int index; index = instr(list, separator); if (index < 0) { head = list; tail = ""; } else { head = left(list, index); tail = mid(list, index + len(separator)); } } /*************************************************************************************************** * * $DESCRIPTION Gives the formatted GUID string of the raw GUID. * $PARAM rawGUIDStr The raw GUID string. * $REQUIRE len(rawGUIDStr) == 32 * $RETURN The formatted GUID string. * $ENSURE len(result) == 36 * **************************************************************************************************/ static function string formatGUID(string rawGUIDStr) { return left(rawGUIDStr, 8) $ "-" $ mid(rawGUIDStr, 8, 4) $ "-" $ mid(rawGUIDStr, 12, 4) $ "-" $ mid(rawGUIDStr, 16, 4) $ "-" $ mid(rawGUIDStr, 20, 12); } /*************************************************************************************************** * * $DESCRIPTION Removes the leading color tag from the specified message. * $PARAM msg The message for which the color tag has to be removed. * $RETURN The original message without a leading color tag. * **************************************************************************************************/ static function string removeMessageColorTag(string msg) { if (left(msg, 2) ~= "") { return mid(msg, 5); } else { return msg; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the base color of the message. * $PARAM msg The message for which the base color is be determined. * $RETURN The base color of the message based on the leading color tag, or -1 if none is * present. * **************************************************************************************************/ static function int getMessageColor(string msg) { if (left(msg, 2) ~= "") { return int(mid(msg, 2, 2)); } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a string. * $PARAM str The string that is to be hashed. * $RETURN The hash value of the given string. * **************************************************************************************************/ static function int stringHash(coerce string str) { local int hash; local int index; for (index = 0; index < len(str); index++) { hash = 31 * hash + asc(mid(str, index, 1)); } return hash; } /*************************************************************************************************** * * $DESCRIPTION Auto formats the specified string by inserting the values into the placeholders. * $PARAM control The Nexgen controller. * $PARAM msg The message that is to be auto formatted. * $REQUIRE control != none * $RETURN The auto formatted string. * **************************************************************************************************/ static function string autoFormat(NexgenController control, string msg) { local string output; output = msg; if (instr(output, "%port%") >= 0) { output = replace(output, "%port%", control.level.game.getServerPort()); } if (instr(output, "%name%") >= 0 && control.sConf != none) { output = replace(output, "%name%", control.sConf.serverName); } if (instr(output, "%admin%") >= 0 && control.sConf != none) { output = replace(output, "%admin%", control.sConf.adminName); } if (instr(output, "%serverid%") >= 0 && control.sConf != none) { output = replace(output, "%serverid%", control.sConf.serverID); } output = control.lng.getCurrentDate(output); return output; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the file name of the specified level. * $RETURN The filename of the specified level. * **************************************************************************************************/ static function string getLevelFileName(LevelInfo lvl) { local string levelFile; levelFile = string(lvl); levelFile = left(levelFile, instr(levelFile, ".")) $ ".unr"; return levelFile; } /*************************************************************************************************** * * $DESCRIPTION Validates the specified file name by replacing the illegal characters with a * valid character. * $PARAM fileName name of the file that is to be validated. * $RETURN A valid file name. * $ENSURE foreach(char in illegalFileNameChars) instr(result, char) < 0 * **************************************************************************************************/ static function string validateFileName(string fileName) { local int index; local string output; output = fileName; for (index = 0; index < len(illegalFileNameChars); index++) { output = replace(output, mid(illegalFileNameChars, index, 1), "_"); } return output; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified level is a valid game map. Use this function to * filter the Unreal Tournament system and tutorial maps from a list. * $PARAM levelFile File name of the level which is to be checked. In case no extension is * specified, the file name will be appended with ".unr" * $RETURN True if the specified level is valid for play, false if not. * **************************************************************************************************/ static function bool isValidLevel(string levelFile) { local string mapName; local bool bInvalidMap; // Get map name. mapName = levelFile; if (instr(mapName, ".") < 0) { mapName = mapName $ ".unr"; } // Check if the map is invalid. bInvalidMap = mapName ~= "CityIntro.unr" || mapName ~= "AutoPlay.unr" || mapName ~= "Entry.unr" || mapName ~= "UTCredits.unr" || mapName ~= "UT-Logo-Map.unr" || instr(caps(mapName), separator) >= 0 || instr(caps(mapName), "EOL_") >= 0 || instr(caps(mapName), "TUTORIAL") >= 0; // Return result. return !bInvalidMap; } /*************************************************************************************************** * * $DESCRIPTION Encodes the specified string as if it is an URL. * $PARAM url The string that is to be encoded as an URL. * $PARAM maxLength The maximum length of the output. A value below 1 means the length of * the output won't be limited. * $RETURN Returns a string in which all non-alphanumeric characters except -_. have been * replaced with a percent (%) sign followed by two hex digits and spaces encoded as * plus (+) signs. * **************************************************************************************************/ static function string urlEncode(string url, optional int maxLength) { local int index; local string cs; local string ns; local string output; local bool bMaxOutputLengthReached; // Encode each character in the url string. while (!bMaxOutputLengthReached && index < len(url)) { // Retrieve character to encode. cs = mid(url, index, 1); // Encode character. if (("0" <= cs && cs <= "9") || ("a" <= cs && cs <= "z") || ("A" <= cs && cs <= "Z") || (cs == "-") || (cs == "_") || (cs == ".")) { ns = cs; } else if (cs == " ") { ns = "+"; } else { ns = "%" $ class'MD5Hash'.static.decToHex(asc(cs), 1); } // Add character if it doesn't exceed the maximum length of the output string. if ((maxLength > 0) && (len(output) + len(ns) > maxLength)) { bMaxOutputLengthReached = true; } else { output = output $ ns; index++; } } // Return URL encoded string return output; } /*************************************************************************************************** * * $DESCRIPTION Splits the given string in two parts: the first line and the rest. * $PARAM str The string that should be splitted. * $RETURN The first line in the given string. * **************************************************************************************************/ static function string getNextLine(out string str) { local string line; local int indexCRLF; local int indexCR; local int indexLF; // Get location of newline token. indexCRLF = instr(str, chr(13) $ chr(10)); indexCR = instr(str, chr(13)); indexLF = instr(str, chr(10)); // Split data. if (indexCRLF >= 0) { line = left(str, indexCRLF); str = mid(str, indexCRLF + 2); } else if (indexCR >= 0) { line = left(str, indexCR); str = mid(str, indexCR + 1); } else if (indexLF >= 0) { line = left(str, indexLF); str = mid(str, indexLF + 1); } else { line = str; str = ""; } // Return result. return line; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ ]1/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenTextFile * $VERSION 1.01 (17-6-2008 13:53) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION General purpose class to support writing to text files. * **************************************************************************************************/ class NexgenTextFile extends StatLogFile; var bool bIsOpen; // Whether the text file is open. /*************************************************************************************************** * * $DESCRIPTION Called when this object is spawned. * $OVERRIDE * **************************************************************************************************/ function beginPlay() { // Do nothing, just prevent setTimer() from being called. } /*************************************************************************************************** * * $DESCRIPTION Creates a new text file. Initially the file will be named 'tempFileName'. Once the * file is closed it will be renamed to 'fileName'. * $RETRUN True if the text file was successfully opened, false if it failed. * $ENSURE imply(result == true, bIsOpen) * **************************************************************************************************/ function bool openFile(string tempFileName, string fileName) { if (tempFileName == "" || fileName == "" || bIsOpen) { return false; } statLogFile = tempFileName; statLogFinal = fileName; openLog(); bIsOpen = true; return true; } /*************************************************************************************************** * * $DESCRIPTION Closes the file. Before the file is closed any buffered text will be flushed. * **************************************************************************************************/ function closeFile() { if (bIsOpen) { fileFlush(); closeLog(); bIsOpen = false; } } /*************************************************************************************************** * * $DESCRIPTION Writes a new line to the text file. * $PARAM text The text that is to be written to the file * $PARAM bNoFlush Whether the text should be buffered, instead of written immediately. * **************************************************************************************************/ function println(string text, optional bool bBuffer) { if (bIsOpen) { logEventString(text); if (!bBuffer) fileFlush(); } } /*************************************************************************************************** * * $DESCRIPTION Flushes the buffer. Any text remaining in the buffer will be written to the file. * **************************************************************************************************/ function flush() { if (bIsOpen) { fileFlush(); } } ~/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenTeamBalancer * $VERSION 1.03 (28-12-2007 21:16) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Team balancing support class. The sole purpose of this class is to provide team * balancing support. This support has been put in a separate class so plugins can * change the default team balancing routine to a custom routine. * **************************************************************************************************/ class NexgenTeamBalancer extends Info; var NexgenController control; // The Nexgen controller. const maxTeamCount = 4; // Maximum number of teams supported. /*************************************************************************************************** * * $DESCRIPTION Initializes the team balancer. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { // Check owner. if (owner == none || !owner.isA('NexgenController')) { destroy(); return; } // Set controller. control = NexgenController(owner); } /*************************************************************************************************** * * $DESCRIPTION Attempts to balance the current teams. * $RETURN True if the teams have been balanced, false if they are already balanced. * **************************************************************************************************/ function bool balanceTeams() { local NexgenClient client; local int teamSize[4]; local int totalPlayers; local int numTeams; local int minPlayersPerTeam; local int index; local int targetTeam; local NexgenClient preferredSwitchers[32]; // Assume the game has at most 32 players. local int prefSwitchTeamOffsets[4]; local int switchedCount[4]; // Get number of teams. if (level.game.isA('TeamGamePlus')) { numTeams = TeamGamePlus(level.game).maxTeams; } else { // Not a team game, so we're unable to balance the teams. return false; } // Get current team sizes. for (client = control.clientList; client != none; client = client.nextClient) { if (!client.bSpectator && 0 <= client.team && client.team < maxTeamCount) { teamSize[client.team]++; totalPlayers++; } } // Check if teams are already balanced. if (teamSize[getLargestTeam(teamSize, numTeams)] - teamSize[getSmallestTeam(teamSize, numTeams)] < 2) { return false; } // Calculate minimum players per team. minPlayersPerTeam = totalPlayers / numTeams; // Get player switch desirability rankings. initPreferredSwitchers(preferredSwitchers, prefSwitchTeamOffsets); // Determine rebalance actions. for (index = 0; index < numTeams; index++) { while(teamSize[index] < minPlayersPerTeam || teamSize[index] > minPlayersPerTeam + 1) { // Switch a player. if (teamSize[index] < minPlayersPerTeam) { // Too few players, steal one from the largest team. targetTeam = getLargestTeam(teamSize, numTeams); teamSize[index]++; teamSize[targetTeam]--; switchPreferredPlayer(targetTeam, index, preferredSwitchers, prefSwitchTeamOffsets, switchedCount); } else { // Too many players, dump one at the smallest team. targetTeam = getSmallestTeam(teamSize, numTeams); teamSize[index]--; teamSize[targetTeam]++; switchPreferredPlayer(index, targetTeam, preferredSwitchers, prefSwitchTeamOffsets, switchedCount); } } } // Team balance complete. return true; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the index of the team with the most players. * $PARAM teamSize The current team sizes. * $PARAM numTeams The number of teams available in the current game. * $REQUIRE 0 < numTeams && numTeams <= maxTeamCount * $RETURN The team number of the biggest team. * $ENSURE 0 <= result && result < numTeams * **************************************************************************************************/ function int getLargestTeam(int teamSize[4], int numTeams) { local int largest; local int index; // Find largest team. for (index = 1; index < numTeams; index++) { if (teamSize[index] > teamSize[largest]) { largest = index; } } // Return result. return largest; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the index of the team with the least players. * $PARAM teamSize The current team sizes. * $PARAM numTeams The number of teams available in the current game. * $REQUIRE 0 < numTeams && numTeams <= maxTeamCount * $RETURN The team number of the smallest team. * $ENSURE 0 <= result && result < numTeams * **************************************************************************************************/ function int getSmallestTeam(int teamSize[4], int numTeams) { local int smallest; local int index; // Find smallest team. for (index = 1; index < numTeams; index++) { if (teamSize[index] < teamSize[smallest]) { smallest = index; } } // Return result. return smallest; } /*************************************************************************************************** * * $DESCRIPTION Initializes the list containing the players with the highest switch desirability. * $PARAM preferredSwitchers List containing the most preferred players for team * switching per team. Sorted in descending order. * $PARAM prefSwitchTeamOffsets Starting offsets in the preferredSwitchers array for each team. * **************************************************************************************************/ function initPreferredSwitchers(out NexgenClient preferredSwitchers[32], out int prefSwitchTeamOffsets[4]) { local int team; local int nextOffset; local NexgenClient client; local NexgenClient tempClient; local bool bSorted; local int index; // Add preferred players for each team. for (team = 0; team < maxTeamCount; team++) { prefSwitchTeamOffsets[team] = nextOffset; // Add each player belonging to this team. for (client = control.clientList; client != none; client = client.nextClient) { if (client.team == team && nextOffset < arrayCount(preferredSwitchers)) { // Add to end of list. preferredSwitchers[nextOffset] = client; // Sort list. bSorted = false; index = nextOffset - 1; while (!bSorted && index >= prefSwitchTeamOffsets[team]) { // Player is correctly positioned in the list? if (compareSwitchDesirability(preferredSwitchers[index], preferredSwitchers[index + 1]) >= 0) { // Yes, list sort done. bSorted = true; } else { // No, switch players. tempClient = preferredSwitchers[index]; preferredSwitchers[index] = preferredSwitchers[index + 1]; preferredSwitchers[index + 1] = tempClient; index--; } } // Update next player offset. nextOffset++; } } } } /*************************************************************************************************** * * $DESCRIPTION Switches the next preferred player from team oldTeam to newTeam. * $PARAM oldTeam The team from which a player is to be switched. * $PARAM newTeam Target team for the player. * $PARAM preferredSwitchers List containing the most preferred players for team * switching per team. Sorted in descending order. * $PARAM prefSwitchTeamOffsets Starting offsets in the preferredSwitchers array for each team. * $PARAM switchedCount Number of players that already have been switched for each team. * $REQUIRE 0 <= oldTeam && oldTeam <= maxTeamCount && * 0 <= newTeam && newTeam <= maxTeamCount && * oldTeam != newTeam * $ENSURE old.switchedCount[oldTeam] = old.switchedCount[oldTeam] + 1 * **************************************************************************************************/ function switchPreferredPlayer(int oldTeam, int newTeam, NexgenClient preferredSwitchers[32], int prefSwitchTeamOffsets[4], out int switchedCount[4]) { local NexgenClient preferred; // Get preferred player. preferred = preferredSwitchers[prefSwitchTeamOffsets[oldTeam] + switchedCount[oldTeam]]; // Switch the player. switchedCount[oldTeam]++; preferred.setTeam(newTeam); } /*************************************************************************************************** * * $DESCRIPTION Compares the switch desirability of two players. * $PARAM client1 The client of the first player. * $PARAM client2 The client of the second player. * $REQUIRE client1 != none && client2 != none && client1.team == client2.team * $RETURN -1 if the switch desirability of the first player is lower then the switch * desirability of the second player, 1 if it is higher and 0 if they are equal. * $ENSURE result == -1 || result == 0 || result == 1 * **************************************************************************************************/ function int compareSwitchDesirability(NexgenClient client1, NexgenClient client2) { if (client1.lastSwitchTime < client2.lastSwitchTime && client2.player.playerReplicationInfo.hasFlag == none || client1.player.playerReplicationInfo.hasFlag != none) { return -1; } else { return 1; } } {@z$S-?-C H,5rHI*-C'<H-CM az$M  M RM |A%HIM M  }@ "Author"DAy$K y$ Np@dSNdgQrdb :g,he ij4 ~@ "Title"@ "File"@A "Level"CA "Mutators"Zv$F?qDZFk)v$F&$9F)u$F%$ F/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimplePlayerListBox * $VERSION 1.00 (13-10-2007 18:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple player listbox GUI component. * **************************************************************************************************/ class NexgenSimplePlayerListBox extends NexgenPlayerListBox; /*************************************************************************************************** * * $DESCRIPTION Returns the text displayed for a list item. * $PARAM item The item for which its display text has to be determined. * $REQUIRE item != none * $RETURN The text that should be displayed for the specified item in the listbox. * $OVERRIDE * **************************************************************************************************/ function string getDisplayText(NexgenPlayerList item) { return item.pName; } FA "Name change allowed"EAt$db"t$w$-x$ WAjEe-:j&b"OverrideClass'kk:j,(b"OverrideClassBotpack.CHSpectator'Reconnect GA "Teams are locked"HA "Team balancing enabled"JA "Team switch enabled"p$@KA "Game speed"RA "Team score limit"i "OverrideClass"LA "Reconnect"MA "Botpack.CHSpectator"ZNAe ?)FZe k)s$e &$9e )r$e %$9e ne xo$,@ip$,@ u/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimpleListItem * $VERSION 1.00 (14-10-2007 22:36) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple list item description class. Stores only two properties a display name and * an item identifier. * **************************************************************************************************/ class NexgenSimpleListItem extends UWindowListBoxItem; var string displayText; var int itemID; /*************************************************************************************************** * * $DESCRIPTION Compares two UWindowList items. * $PARAM a First item to compare. * $PARAM b Second item to compare. * $REQUIRE a != none && b != none * $RETURNS -1 If the first item is 'smaller' then the second item, otherwise 1 is returned. * $OVERRIDE * **************************************************************************************************/ function int compare(UWindowList a, UWindowList b) { if (NexgenSimpleListItem(a).displayText < NexgenSimpleListItem(b).displayText) { return -1; } else { return 1; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ @OAn$#E5 4n)  q$n$m$l$ SA "Score limit"TA "Time limit"XA "Date"@NQAtVtNtsPrti :s,Reconnect4rtx :s,x-(b"OverrideClassBotpack.CHSpectator'Reconnect4 vAreHw.*.L..~o-'oZ{r.8 mr YA "FPH"ZA "Top players"\A "Total caps"x4/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenSimpleListBox * $VERSION 1.01 (31-10-2007 13:44) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Simple listbox GUI component. * **************************************************************************************************/ class NexgenSimpleListBox extends UWindowListBox; /*************************************************************************************************** * * $DESCRIPTION Renders the specified listbox item. * $PARAM c The canvas object on which the rendering will be performed. * $PARAM item Item to render. * $PARAM x Horizontal offset on the canvas. * $PARAM y Vertical offset on the canvas. * $PARAM w Width of the item that is to be rendered. * $PARAM h Height of the item that is to be rendered. * $REQUIRE c != none && item != none * $OVERRIDE * **************************************************************************************************/ function drawItem(Canvas c, UWindowList item, float x, float y, float w, float h) { if(NexgenSimpleListItem(item).bSelected) { c.drawColor.r = 0; c.drawColor.g = 0; c.drawColor.b = 128; drawStretchedTexture(c, x, y, w, h - 1, Texture'WhiteTexture'); c.drawColor.r = 255; c.drawColor.g = 255; c.drawColor.b = 255; } else { c.drawColor.r = 0; c.drawColor.g = 0; c.drawColor.b = 0; } c.font = root.fonts[F_Normal]; clipText(c, x + 2, y, NexgenSimpleListItem(item).displayText); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the item with the specified id number. * $PARAM itemID The id number of the item to return. * $RETURN The item that has the specified id number, or none if there is no item with the * specified id number. * **************************************************************************************************/ function NexgenSimpleListItem getItemByID(int itemID) { local NexgenSimpleListItem item; // Search for item. for (item = NexgenSimpleListItem(items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID == itemID) { return item; } } // Item not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Called when an item was double clicked on. * $PARAM item The item which was double clicked. * $REQUIRE item != none * $OVERRIDE * **************************************************************************************************/ function doubleClickItem(UWindowListBoxItem item) { if (notifyWindow != none) { notifyWindow.notify(self, DE_DoubleClick); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ ]A "Total deaths"^A "Total frags"mA "Games hosted"r 4.0_A 4.0`A 4.0aA 64.0bA 16.0cA 48.0dA 12.0eA 16.0fA "say"gAp?-6mBhApm@h@r.P pmhr h$ppm@@q.P pmhq g$hAmp@@B?,@p@@B@h@@.P pmh@m@,@z 'm@Bh@Ap@h@p.P pmhp f$p$ CI/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenServerFullDialog * $VERSION 1.01 (23-12-2006 14:15) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenServerFullDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var UWindowSmallButton spectatorButton; // Spectator button component. var UMenuLabelControl slotLabel; // Slot label component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string slotMessage; // Message describing the amount of slots. var localized string passwordText; // Label to display before the password field. var localized string reconnectText; // Text to display on the reconnect button. var localized string spectatorText; // Text to display on the spectator button. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE reconnectButton != none && spectatorButton != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); slotLabel = addLabel(cy); spectatorButton = addButton(spectatorText, 64.0); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM playerSlots Number of slots available for regular players. * $PARAM vipSlots Number of slots available for VIPs. * $PARAM adminSlots Number of slots available for administrators. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string playerSlots, optional string vipSlots, optional string adminSlots, optional string str4) { slotLabel.setText(class'NexgenUtil'.static.format(slotMessage, playerSlots, vipSlots, adminSlots)); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { getplayerowner().consoleCommand(reconnectCommand); close(); } // Spectator button. if (control == spectatorButton && eventType == DE_Click && !spectatorButton.bDisabled) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ CiAVAD 2 jA yiCnŔ> Y7XÔ> Y> Y᱘ 3B V]"!Failed to login: server is full.m:xThe server you have tried to enter has no more player slots available. You can try again in a few minutes or reconnect immediately as a spectator. Note that the server may appear to have some unused slots available. These are reserved slots for VIPs or administrators and are not accessible for regular players.J]MKThis server has %1 regular players slots, %2 VIP slots and %3 admin slots.]] Password:i] Reconnectd] Spectatorn/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenScrollPanelContainer * $VERSION 1.01 (11-3-2008 21:32) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page container scroll version. * **************************************************************************************************/ class NexgenScrollPanelContainer extends NexgenPanelContainer; var UWindowPageWindow clientArea; // Scrollable window. var UWindowVScrollBar scrollBar; // Vertical scrollbar control. var NexgenPanel panels[32]; // Panels displayed on this panel. var int numPanels; // Number of panels displayed. var float clientAreaDesiredHeight; // Desired height of the client area. var float nextPanelOffset; // Next vertical offset on the client area. const panelDistance = 4.0; // Distance between panels. const borderDistance = 6.0; // Disatnce between panels and the border of the client area. const defaultPanelHeight = 288.0; // Default height of panels displayed on the client area. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super(NexgenPanel).created(); clientArea = UWindowPageWindow(createWindow(class'UWindowPageWindow', 0, 0, winWidth - 16, winHeight - 6, ownerWindow)); scrollBar = UWindowVScrollbar(createWindow(class'UWindowVScrollbar', winWidth - 16, 0, 12, winHeight - 6)); scrollBar.bAlwaysOnTop = true; clientAreaDesiredHeight = 2 * borderDistance; nextPanelOffset = borderDistance; } /*************************************************************************************************** * * $DESCRIPTION Prepares the window for the paint call. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function beforePaint(Canvas c, float x, float y) { local float clientWidth, clientHeight; local bool bNeedScrollBar; clientWidth = winWidth - 12; clientHeight = clientAreaDesiredHeight; if (clientHeight <= winHeight) { clientHeight = winHeight; } else { bNeedScrollBar = true; } clientArea.setSize(clientWidth, clientHeight); if (bNeedScrollBar) { scrollBar.setRange(0, clientHeight, scrollBar.winHeight, 10); } else { scrollBar.setRange(0, 0, 0, 0); scrollBar.pos = 0; } clientArea.winTop = -scrollBar.pos; super.beforePaint(c, x, y); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the desired window dimensions. * $PARAM w The desired width. * $PARAM h The desired height. * $OVERRIDE * **************************************************************************************************/ function getDesiredDimensions(out float w, out float h){ super(UWindowWindow).getDesiredDimensions(w, h); } /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { // Ignore. } /*************************************************************************************************** * * $DESCRIPTION Retrieves the panel with the specified name. * $PARAM panelName Name of the panel that is to be returned. * $REQUIRE panelName != "" * $RETURN The panel that was requested or none if the panel wasn't found. * $OVERRIDE * **************************************************************************************************/ function NexgenPanel getPanel(string panelName) { local NexgenPanel panel; local int index; // Search for panel. while (panel == none && index < numPanels) { if (panels[index].panelIdentifier ~= panelName) { panel = panels[index]; } else if (panels[index].isA('NexgenPanelContainer')) { panel = NexgenPanelContainer(panels[index]).getPanel(panelName); } index++; } // Return result. return panel; } /*************************************************************************************************** * * $DESCRIPTION Adds a new NexgenPanel to the container or one of its subcontainers. To specify * a specific parent use the parent parameter to indicate the path, e.g. * "plugin,settings". If an invalid path is specified the panel won't be created. * $PARAM title Text to display in the tab header. * $PARAM panelClass Type of NexgenPanel to add/create. * $PARAM identifier Identifier to assign to the new panel. * $PARAM parent Path where to the parent of the new panel. * $REQUIRE panelClass != none * $RETURN The panel that was created and added to the container, or none if an invalid path * to the parent container was specified. * $OVERRIDE * **************************************************************************************************/ function NexgenPanel addPanel(string title, class panelClass, optional string identifier, optional string parent) { local NexgenPanel newPanel; local string parentPanel; local string subPanels; local NexgenPanelContainer container; local bool bFound; local int index; local float desiredPanelHeight; // Add to subpanel? if (parent != "") { // Get local parent name. class'NexgenUtil'.static.split(parent, parentPanel, subPanels); // Locate local parent. while (!bFound && index < numPanels) { if (panels[index].isA('NexgenPanelContainer') && NexgenPanelContainer(panels[index]).panelIdentifier ~= parentPanel ) { bFound = true; newPanel = NexgenPanelContainer(panels[index]).addPanel(title, panelClass, identifier, subPanels); } else { index++; } } } else { // Nope, add it to the scroll panel. if (numPanels < arrayCount(panels)) { // Determine panel height. desiredPanelHeight = panelClass.default.panelHeight; if (desiredPanelHeight <= 0) { desiredPanelHeight = defaultPanelHeight; } // Create control. newPanel = NexgenPanel(clientArea.createWindow(panelClass, borderDistance, nextPanelOffset, clientArea.winWidth - 2 * borderDistance, desiredPanelHeight)); panels[numPanels] = newPanel; if (identifier != "") { newPanel.panelIdentifier = identifier; } newPanel.client = self.client; newPanel.setContent(); // Update client area metrics. clientAreaDesiredHeight += desiredPanelHeight; if (numPanels > 0) { clientAreaDesiredHeight += panelDistance; } nextPanelOffset += desiredPanelHeight + panelDistance; numPanels++; } } // Return created panel. return newPanel; } /*************************************************************************************************** * * $DESCRIPTION Selects the panel with the specified name. * $PARAM panelName The name of the panel that is to be selected. * $RETURN True if the panel was selected, false if it wasn't found. * $OVERRIDE * **************************************************************************************************/ function bool selectPanel(string panelName) { local int index; local bool bFound; while (!bFound && index < numPanels) { // Check panel. if (panels[index].panelIdentifier ~= panelName) { // Panel found, select it. bFound = true; } else if (panels[index].isA('NexgenPanelContainer')) { // No, but page is a NexgenPanelContainer, so it may include the panel. bFound = NexgenPanelContainer(panels[index]).selectPanel(panelName); } // Panel found? if (bFound) { // Panel found, select it. scrollBar.pos = panels[index].winTop; } else { // No, continue with next page. index++; } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the sub panels on this panel that the server configuration has been * updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { local int index; for (index = 0; index < numPanels; index++) { panels[index].configChanged(configType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { local int index; for (index = 0; index < numPanels; index++) { panels[index].gameInfoChanged(infoType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * $OVERRIDE * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { local int index; for (index = 0; index < numPanels; index++) { panels[index].playerEvent(playerNum, eventType, args); } } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { local int index; for (index = 0; index < numPanels; index++) { panels[index].notifyEvent(type, arguments); } } e/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenResidentConfigDialog * $VERSION 1.00 (16-12-2007 15:32) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog displayed if a resident Nexgen configuration is detected. * **************************************************************************************************/ class NexgenResidentConfigDialog extends NexgenPopupDialog; var localized string caption; // Caption to display on the dialog. var localized string message; // Message to dispay on the dialog. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ oA "Stats"hAvy\<vc$b$vQ pA "Server ID"qA "Message of the day"yA "Contact address"nAJm~JKHrJr :K,. 4rJq :K,@W:$ rJ@ :K,Q ClAAAF 2sAGCn V]&%Local Nexgen configuration detected!mRPA Nexgen configuration has been detected on your client. This may prevent your client from initializing correctly. If you experience problems delete Nexgen.ini or the Nexgen configuration stored UnrealTournament.ini. You can find these files in the system folder of your Unreal Tournament installation.\n \nSorry for the inconvenience.e"Ny/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPUserAccounts * $VERSION 1.03 (9-8-2008 19:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen user account control panel page. * **************************************************************************************************/ class NexgenRCPUserAccounts extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowCheckbox rightEnableInp[18]; var NexgenPlayerACListBox playerList; var NexgenSimpleListBox accountList; var UWindowComboControl accountTypeList; var NexgenEditControl accountNameInp; var NexgenEditControl accountTitleInp; var UWindowSmallButton addButton; var UWindowSmallButton updateButton; var UWindowSmallButton deleteButton; const invalidAccountType = -1; // Invalid or no account type selected. const customAccountType = 0; // Custom account type selected. const defaultAccountType = 1; // Default account type selected. /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); divideRegionH(2, defaultComponentDist); // User info panel. p = addContentPanel(); p.splitRegionH(64); p.splitRegionV(96); p.splitRegionH(16, , , true); p.divideRegionH(3); p.divideRegionH(3); p.divideRegionV(2, defaultComponentDist); p.splitRegionV(192); p.addLabel(client.lng.userNameTxt, true); p.addLabel(client.lng.accountTypeTxt, true); p.addLabel(client.lng.userTitleTxt, true); accountNameInp = p.addEditBox(); accountTypeList = p.addListCombo(); accountTitleInp = p.addEditBox(); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionV(3, defaultComponentDist); p.skipRegion(); for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index] = p.addCheckBox(TA_Left, client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); } else { rightEnableInp[index] = p.addCheckBox(TA_Left, mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); } } updateButton = p.addButton(client.lng.updateTxt); addButton = p.addButton(client.lng.addTxt); deleteButton = p.addButton(client.lng.deleteTxt); // Account / player lists. splitRegionH(16); splitRegionH(16); addLabel(client.lng.onlineTxt, true, TA_Center); playerList = NexgenPlayerACListBox(addListBox(class'NexgenPlayerACListBox')); addLabel(client.lng.offlineTxt, true, TA_Center); accountList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Configure components. accountNameInp.setMaxLength(32); accountTitleInp.setMaxLength(24); accountTypeList.setEditable(false); playerList.register(self); accountList.register(self); updateButton.register(self); deleteButton.register(self); accountTypeList.register(self); loadUserAccounts(); loadAccountTypes(); accountSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the account types. * **************************************************************************************************/ function loadAccountTypes() { local int index; accountTypeList.clear(); accountTypeList.addItem(client.lng.customAccountTxt, string(-1)); while(index < arrayCount(client.sConf.atTypeName) && client.sConf.atTypeName[index] != "") { accountTypeList.addItem(client.sConf.atTypeName[index], string(index)); index++; } } /*************************************************************************************************** * * $DESCRIPTION Load the user account list. * **************************************************************************************************/ function loadUserAccounts() { local int index; local NexgenPlayerACListItem playerItem; local NexgenSimpleListItem accountItem; // Clear account list. accountList.items.clear(); accountList.selectedItem = none; // Load each user account. while (index < arrayCount(client.sConf.paPlayerID) && client.sConf.paPlayerID[index] != "") { playerItem = NexgenPlayerACListItem(playerList.getPlayerByID(client.sConf.paPlayerID[index])); // Client is online? if (playerItem == none) { // Nope, add to account list. accountItem = NexgenSimpleListItem(accountList.items.append(class'NexgenSimpleListItem')); accountItem.itemID = index; accountItem.displayText = "[" $ client.sConf.getUserAccountTitle(index) $ "] " $ client.sConf.paPlayerName[index]; } // Continue with next user account. index++; } // Sort list. accountList.items.sort(); // Load account info for online players. updatePlayerList(); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { local NexgenPlayerACListItem playerItem; local NexgenSimpleListItem accountItem; local bool bFound; local int accountNum; // Player has joined the game? if (eventType == client.PE_PlayerJoined) { // Add player. playerItem = NexgenPlayerACListItem(addPlayerToList(playerList, playerNum, args)); playerList.items.sort(); // Check if player has an account. accountNum = client.sConf.getUserAccountIndex(playerItem.pClientID); if (accountNum >= 0) { // Player has an account. // Update attributes. playerItem.bHasAccount = true; playerItem.accountNum = accountNum; // Move account item to player list. accountItem = accountList.getItemByID(accountNum); if (accountList.selectedItem == accountItem) { playerItem.bSelected = true; playerList.selectedItem = playerItem; accountList.selectedItem = none; } accountItem.remove(); } } // Player has left the game? if (eventType == client.PE_PlayerLeft) { // Check if player has an account. playerItem = NexgenPlayerACListItem(playerList.getPlayer(playerNum)); if (playerItem != none && playerItem.bHasAccount) { accountItem = NexgenSimpleListItem(accountList.items.append(class'NexgenSimpleListItem')); accountItem.itemID = playerItem.accountNum; accountItem.displayText = "[" $ client.sConf.getUserAccountTitle(playerItem.accountNum) $ "] " $ client.sConf.paPlayerName[playerItem.accountNum]; accountList.items.sort(); if (playerList.selectedItem == playerItem) { accountItem.bSelected = true; accountList.selectedItem = accountItem; playerList.selectedItem = none; } } // Delete from player list. playerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local string clientID; local string accountName; local int accountType; local string rights; local string title; super.notify(control, eventType); setRPCI(); // Online player selected. if (control == playerList && eventType == DE_Click) { if (accountList.selectedItem != none) { accountList.selectedItem.bSelected = false; accountList.selectedItem = none; } accountSelected(); } // Offline player selected. if (control == accountList && eventType == DE_Click) { if (playerList.selectedItem != none) { playerList.selectedItem.bSelected = false; playerList.selectedItem = none; } accountSelected(); } // Account type selected. if (control == accountTypeList && eventType == DE_Change) { accountTypeSelected(); } // Delete button clicked. if (control == deleteButton && eventType == DE_Click && !deleteButton.bDisabled && rpci != none) { rpci.deleteAccount(getSelectedAccountNum()); } // Update button clicked. if (control == updateButton && eventType == DE_Click && !updateButton.bDisabled && rpci != none) { accountName = class'NexgenUtil'.static.trim(accountNameInp.getValue()); accountType = accountTypeList.getSelectedIndex() - 1; if (accountType < 0) { rights = getCurrentRights(); title = class'NexgenUtil'.static.trim(accountTitleInp.getValue()); } else { rights = ""; title = ""; } rpci.updateAccount(getSelectedAccountNum(), accountName, accountType, rights, title); } // Add button clicked. if (control == addButton && eventType == DE_Click && !addButton.bDisabled && rpci != none) { clientID = NexgenPlayerACListItem(playerList.selectedItem).pClientID; accountName = class'NexgenUtil'.static.trim(accountNameInp.getValue()); accountType = accountTypeList.getSelectedIndex() - 1; if (accountType < 0) { rights = getCurrentRights(); title = class'NexgenUtil'.static.trim(accountTitleInp.getValue()); } else { rights = ""; title = ""; } rpci.addAccount(clientID, accountName, accountType, rights, title); } } /*************************************************************************************************** * * $DESCRIPTION Returns a string containing the currently selected rights. * $RETURN A string containing the rights currently selected. * **************************************************************************************************/ function string getCurrentRights() { local string rights; local string rightDef; local int index; // Check for each right if it is selected. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightEnableInp[index].bChecked && rightDef != "") { if (rights == "") { rights = left(rightDef, instr(rightDef, separator)); } else { rights = rights $ separator $ left(rightDef, instr(rightDef, separator)); } } } // Return result. return rights; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account number of the account currently selected from the online or * offline account lists. * $RETURN The account number of the currently selected account, or -1 if no account was * selected. * **************************************************************************************************/ function int getSelectedAccountNum() { local int accountNum; // Get account number of selected online / offline account. if (playerList.selectedItem != none) { // Online account selected. accountNum = NexgenPlayerACListItem(playerList.selectedItem).accountNum; } else if (accountList.selectedItem != none) { // Offline account selected. accountNum = NexgenSimpleListItem(accountList.selectedItem).itemID; } else { // No account selected. accountNum = -1; } // Return account number. return accountNum; } /*************************************************************************************************** * * $DESCRIPTION Checks if the currently selected item has an user account. * $RETURN True if the currently selected item has an user account, false if not. * **************************************************************************************************/ function bool selectionHasAccount() { local bool bHasAccount; // Check if selected item has an account. if (playerList.selectedItem != none) { // Online account selected. bHasAccount = NexgenPlayerACListItem(playerList.selectedItem).bHasAccount; } else if (accountList.selectedItem != none) { // Offline account selected. bHasAccount = true; } else { // No account selected. bHasAccount = false; } // Return result. return bHasAccount; } /*************************************************************************************************** * * $DESCRIPTION Called when an user account was selected from the list. * **************************************************************************************************/ function accountSelected() { local bool bItemSelected; local bool bHasAccount; local int index; local int accountNum; // Get account number of selected online / offline account. accountNum = getSelectedAccountNum(); bHasAccount = selectionHasAccount(); // Account selected? bItemSelected = accountNum >= 0; accountNameInp.setDisabled(!bItemSelected); updateButton.bDisabled = !bItemSelected || !bHasAccount || !canEditAccount(accountNum); deleteButton.bDisabled = !bItemSelected || !bHasAccount || !canEditAccount(accountNum); addButton.bDisabled = !bItemSelected || bHasAccount; // $TODO Implement UWindowComboControl class that can be disabled. if (bHasAccount && bItemSelected) { accountNameInp.setValue(client.sConf.paPlayerName[accountNum]); accountTypeList.setSelectedIndex(client.sConf.get_paAccountType(accountNum) + 1); } else if (!bHasAccount && bItemSelected) { accountNameInp.setValue(NexgenPlayerACListItem(playerList.selectedItem).pName); accountTypeList.setSelectedIndex(defaultAccountType); } else { accountNameInp.setValue(""); accountTypeList.setSelectedIndex(invalidAccountType); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client can delete the specified account. * $PARAM accountNum The number of the account for which has to be checked if it can be * deleted by this client. * $REQUIRE 0 <= accountNum && accountNum < arrayCount(client.sConf.paPlayerID) && * paPlayerID[accountNum] != none * $RETURN True if account can be deleted by this client, false if not. * **************************************************************************************************/ function bool canEditAccount(int accountNum) { local string rights; // Get rights of target account. if (client.sConf.get_paAccountType(accountNum) < 0) { rights = client.sConf.paCustomRights[accountNum]; } else { rights = client.sConf.atRights[client.sConf.get_paAccountType(accountNum)]; } // Return result. return client.hasRight(client.R_ServerAdmin) || !hasRight(rights, client.R_ServerAdmin); } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified right is included in the given right string. * $PARAM rights The rights specifier string. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the right is included, false if not. * **************************************************************************************************/ function bool hasRight(string rights, string rightID) { return instr(rights $ separator, rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Called when an account type was selected from the list. * **************************************************************************************************/ function accountTypeSelected() { local int accountTypeNum; local int accountNum; local int index; local string rightDef; local string rightStr; local bool bItemSelected; local bool bHasAccount; local string title; local bool bClientIsServerAdmin; bClientIsServerAdmin = client.hasRight(client.R_ServerAdmin); // Get currently selected account type. accountTypeNum = accountTypeList.getSelectedIndex(); // Get account number of selected online / offline account. accountNum = getSelectedAccountNum(); bItemSelected = accountNum >= 0; bHasAccount = selectionHasAccount(); // Retieve title. if (accountTypeNum == invalidAccountType) { title = ""; } else if (accountTypeNum == customAccountType) { if (bHasAccount) { title = client.sConf.paCustomTitle[accountNum]; } else { title = client.sConf.getAccountTypeTitle(0); } } else { title = client.sConf.getAccountTypeTitle(accountTypeNum - 1); } // Update GUI. if (accountTypeNum == invalidAccountType) { addButton.bDisabled = true; updateButton.bDisabled = true; } else if (bHasAccount) { addButton.bDisabled = true; updateButton.bDisabled = !canEditAccount(accountNum) || !bClientIsServerAdmin && !canUseAccountType(accountTypeNum); } else { addButton.bDisabled = !bClientIsServerAdmin && !canUseAccountType(accountTypeNum); updateButton.bDisabled = true; } // Update title. accountTitleInp.setValue(title); accountTitleInp.setDisabled(accountTypeNum != customAccountType); // Retrieve rights. if (accountTypeNum == invalidAccountType) { rightStr = ""; } else if (accountTypeNum == customAccountType) { if (bHasAccount) { rightStr = client.sConf.paCustomRights[accountNum]; } else { rightStr = client.sConf.atRights[0]; } } else { rightStr = client.sConf.atRights[accountTypeNum - 1]; } // Update rights. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index].bDisabled = true; } else { rightEnableInp[index].bDisabled = accountTypeNum != customAccountType || !bClientIsServerAdmin && !client.hasRight(left(rightDef, instr(rightDef, separator))); rightEnableInp[index].bChecked = hasRight(rightStr, left(rightDef, instr(rightDef, separator))); } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client can grant the specified account type. * $PARAM accountType The account type that is to be checked. * $REQUIRE 0 <= accountType && accountType <= arrayCount(client.atTypeName) * $RETURN True if the client can grant the specified account type, false if not. * **************************************************************************************************/ function bool canUseAccountType(int accountType) { return (accountType == customAccountType) || (client.hasRights(client.sConf.atRights[accountType - 1])); } /*************************************************************************************************** * * $DESCRIPTION Updates the player list. Iterates over all players in the player list and resets * their account attributes. * **************************************************************************************************/ function updatePlayerList() { local NexgenPlayerACListItem item; local int index; // For each player... for (item = NexgenPlayerACListItem(playerList.items); item != none; item = NexgenPlayerACListItem(item.next)) { // Check account... index = client.sConf.getUserAccountIndex(item.pClientID); // Set account info... if (index >= 0) { item.bHasAccount = true; item.accountNum = index; item.pTitle = client.sConf.getUserAccountTitle(index); } else { item.bHasAccount = false; item.accountNum = 0; item.pTitle = client.sConf.getAccountTypeTitle(0); } } // Sort list. playerList.items.sort(); } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_AccountTypes) { loadUserAccounts(); loadAccountTypes(); accountSelected(); } if (configType == client.sConf.CT_UserAccounts) { loadUserAccounts(); accountSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ rAnQQn@ O{n@ sayn PBd$Mڕw}Md$e$i$j$k$}L}}~o-'oZ zy<za$`$zQ A 5.0zA "Administrator"{A "Reboot"}A& "Pre switch server console commands"_/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerSettings * $VERSION 1.03 (7-10-2007 13:21) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen server settings control panel page. * **************************************************************************************************/ class NexgenRCPServerSettings extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; var UWindowEditControl serverNameInp; var UWindowEditControl shortServerNameInp; var UWindowEditControl MOTDInp[4]; var UWindowEditControl adminNameInp; var UWindowEditControl adminEmailInp; var UWindowEditControl serverPasswordInp; var UWindowEditControl adminPasswordInp; var UWindowEditControl playerSlotsInp; var UWindowEditControl vipSlotsInp; var UWindowEditControl adminSlotsInp; var UWindowEditControl specSlotsInp; var UWindowCheckbox doUplinkInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionH(20, defaultComponentDist, , true); p = addContentPanel(); splitRegionV(196, , , true); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); p.splitRegionH(55, , true); p.splitRegionV(100); p.splitRegionV(30, defaultComponentDist, true); p.divideRegionH(6); p.divideRegionH(6); p.splitRegionV(100); p.splitRegionV(100); p.addLabel(client.lng.serverNameTxt, true); p.addLabel(client.lng.shortServerNameTxt, true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 1), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 2), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 3), true); p.addLabel(client.lng.format(client.lng.MOTDLineTxt, 4), true); serverNameInp = p.addEditBox(); shortServerNameInp = p.addEditBox(); MOTDInp[0] = p.addEditBox(); MOTDInp[1] = p.addEditBox(); MOTDInp[2] = p.addEditBox(); MOTDInp[3] = p.addEditBox(); p.divideRegionH(5); p.divideRegionH(5); p.divideRegionH(5); p.divideRegionH(5); p.addLabel(client.lng.playerSlotsTxt, true); p.addLabel(client.lng.vipSlotsTxt, true); p.addLabel(client.lng.adminSlotsTxt, true); p.addLabel(client.lng.specSlotsTxt, true); p.addLabel(client.lng.advertiseTxt, true); playerSlotsInp = p.addEditBox( , 48, AL_Left); vipSlotsInp = p.addEditBox( , 48, AL_Left); adminSlotsInp = p.addEditBox( , 48, AL_Left); specSlotsInp = p.addEditBox( , 48, AL_Left); doUplinkInp = p.addCheckBox(TA_Right); p.addLabel(client.lng.adminNameTxt, true); p.addLabel(client.lng.adminEmailTxt, true); p.addLabel(client.lng.serverPasswordTxt, true); p.addLabel(client.lng.adminPasswordTxt, true); p.skipRegion(); adminNameInp = p.addEditBox(); adminEmailInp = p.addEditBox(); serverPasswordInp = p.addEditBox(); adminPasswordInp = p.addEditBox(); // Configure components. serverNameInp.setMaxLength(128); shortServerNameInp.setMaxLength(32); MOTDInp[0].setMaxLength(192); MOTDInp[1].setMaxLength(192); MOTDInp[2].setMaxLength(192); MOTDInp[3].setMaxLength(192); adminNameInp.setMaxLength(64); adminEmailInp.setMaxLength(64); serverPasswordInp.setMaxLength(30); adminPasswordInp.setMaxLength(30); playerSlotsInp.setNumericOnly(true); vipSlotsInp.setNumericOnly(true); adminSlotsInp.setNumericOnly(true); specSlotsInp.setNumericOnly(true); playerSlotsInp.setMaxLength(3); vipSlotsInp.setMaxLength(3); adminSlotsInp.setMaxLength(3); specSlotsInp.setMaxLength(3); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current server settings. * **************************************************************************************************/ function setValues() { serverNameInp.setValue(client.sConf.serverName); shortServerNameInp.setValue(client.sConf.shortName); adminNameInp.setValue(client.sConf.adminName); adminEmailInp.setValue(client.sConf.adminEmail); serverPasswordInp.setValue(client.sConf.decode(client.sConf.globalServerPassword)); adminPasswordInp.setValue(client.sConf.decode(client.sConf.globalAdminPassword)); MOTDInp[0].setValue(client.sConf.MOTDLine[0]); MOTDInp[1].setValue(client.sConf.MOTDLine[1]); MOTDInp[2].setValue(client.sConf.MOTDLine[2]); MOTDInp[3].setValue(client.sConf.MOTDLine[3]); playerSlotsInp.setValue(string(client.sConf.playerSlots)); vipSlotsInp.setValue(string(client.sConf.vipSlots)); adminSlotsInp.setValue(string(client.sConf.adminSlots)); specSlotsInp.setValue(string(client.sConf.spectatorSlots)); doUplinkInp.bChecked = client.sConf.enableUplink; } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_GlobalServerSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: setValues(); break; case saveButton: saveSettings(); break; } } } /*************************************************************************************************** * * $DESCRIPTION Saves the server settings. * **************************************************************************************************/ function saveSettings() { local string serverName; local string shortServerName; local string MOTDLine[4]; local string adminName; local string adminEmail; local string serverPassword; local string adminPassword; local int playerSlots; local int vipSlots; local int adminSlots; local int specSlots; local bool bEnableUplink; // Make sure the RPC interface is available. if (!setRPCI()) return; // Collect data from the GUI. serverName = serverNameInp.getValue(); shortServerName = shortServerNameInp.getValue(); MOTDLine[0] = MOTDInp[0].getValue(); MOTDLine[1] = MOTDInp[1].getValue(); MOTDLine[2] = MOTDInp[2].getValue(); MOTDLine[3] = MOTDInp[3].getValue(); adminName = adminNameInp.getValue(); adminEmail = adminEmailInp.getValue(); serverPassword = serverPasswordInp.getValue(); adminPassword = adminPasswordInp.getValue(); playerSlots = int(playerSlotsInp.getValue()); vipSlots = int(vipSlotsInp.getValue()); adminSlots = int(adminSlotsInp.getValue()); specSlots = int(specSlotsInp.getValue()); bEnableUplink = doUplinkInp.bChecked; // Save settings. rpci.setServerSettings(serverName, shortServerName, MOTDLine[0], MOTDLine[1], MOTDLine[2], MOTDLine[3], adminName, adminEmail, serverPassword, adminPassword, playerSlots, vipSlots, adminSlots, specSlots, bEnableUplink); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ A# "Additional command line options"J?/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPServerInfo * $VERSION 1.01 (10-11-2007 19:10) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen server info control panel page. * **************************************************************************************************/ class NexgenRCPServerInfo extends NexgenPanel; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local GameReplicationInfo GRI; local TournamentGameReplicationInfo TGRI; local NexgenContentPanel p; GRI = client.player.gameReplicationInfo; TGRI = TournamentGameReplicationInfo(GRI); // Create layout & add components. createWindowRootRegion(); splitRegionH(32, defaultComponentDist); // Title. p = addContentPanel(); p.addLabel(GRI.serverName, true, TA_Center); // Contact info. splitRegionH(128, defaultComponentDist); p = addContentPanel(); p.splitRegionV(128); p.divideRegionH(7); p.divideRegionH(7); p.addLabel(client.lng.administratorTxt, true); p.addLabel(client.lng.contactAddrTxt, true); p.addLabel(client.lng.msgOfTheDayTxt, true); p.skipRegion(); p.skipRegion(); p.skipRegion(); p.addLabel(client.lng.serverIDTxt, true); p.addLabel(fixStr(GRI.adminName)); p.addLabel(fixStr(GRI.adminEmail)); p.addLabel(fixStr(GRI.MOTDLine1)); p.addLabel(fixStr(GRI.MOTDLine2)); p.addLabel(fixStr(GRI.MOTDLine3)); p.addLabel(fixStr(GRI.MOTDLine4)); p.addLabel(class'NexgenUtil'.static.formatGUID(client.serverID)); // Server game stats. if (TGRI != none) { splitRegionV(160, defaultComponentDist); // Server stats. p = addContentPanel(); p.splitRegionH(20, , true); p.addLabel(client.lng.statisticsTxt, true, TA_Center); p.splitRegionV(100); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.totalGamesTxt, true); p.addLabel(client.lng.totalFragsTxt, true); p.addLabel(client.lng.totalDeathsTxt, true); p.addLabel(client.lng.totalFlagsTxt, true); p.addLabel(TGRI.totalGames); p.addLabel(TGRI.totalFrags); p.addLabel(TGRI.totalDeaths); p.addLabel(TGRI.totalFlags); // Top players. p = addContentPanel(); p.splitRegionH(20, , true); p.addLabel(client.lng.bestPlayersTxt, true, TA_Center); p.splitRegionV(160, , , true); p.divideRegionH(4); p.splitRegionV(48); p.addLabel(client.lng.playerNameTxt, true); p.addLabel(fixStr(TGRI.bestPlayers[0])); p.addLabel(fixStr(TGRI.bestPlayers[1])); p.addLabel(fixStr(TGRI.bestPlayers[2])); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.FPHTxt, true); p.addLabel(TGRI.bestFPHs[0]); p.addLabel(TGRI.bestFPHs[1]); p.addLabel(TGRI.bestFPHs[2]); p.addLabel(client.lng.recordSetTxt, true); p.addLabel(fixStr(TGRI.bestRecordDate[0])); p.addLabel(fixStr(TGRI.bestRecordDate[1])); p.addLabel(fixStr(TGRI.bestRecordDate[2])); } } /*************************************************************************************************** * * $DESCRIPTION Makes sure a non empty string is returned. * **************************************************************************************************/ static function string fixStr(coerce string str) { if (class'NexgenUtil'.static.trim(str) == "") { return "-"; } else { return str; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ AB "Map prefix"zr/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPReplacementHUDSettings * $VERSION 1.03 (20-6-2008 14:14) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen about control panel page. * **************************************************************************************************/ class NexgenRCPReplacementHUDSettings extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox hudReplacementList; var UWindowSmallButton hudReplSaveButton; var UWindowSmallButton hudReplRemButton; var NexgenEditControl originalHUDClassInp; var NexgenEditControl replacementHUDClassInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { // Create layout & add components. createPanelRootRegion(); splitRegionV(256, defaultComponentDist); hudReplacementList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); divideRegionH(4); addLabel(client.lng.hudReplaceClassesTxt, true, TA_Center); splitRegionV(128); splitRegionV(128); splitRegionV(192); addLabel(client.lng.originalHUDClassTxt); originalHUDClassInp = addEditBox(); addLabel(client.lng.replacementHUDClassTxt); replacementHUDClassInp = addEditBox(); divideRegionV(2, defaultComponentDist); skipRegion(); hudReplSaveButton = addButton(client.lng.saveTxt); hudReplRemButton = addButton(client.lng.removeTxt); // Configure components. originalHUDClassInp.setMaxLength(64); replacementHUDClassInp.setMaxLength(64); loadHUDReplacementList(); } /*************************************************************************************************** * * $DESCRIPTION Loads the HUD class replacement list. * **************************************************************************************************/ function loadHUDReplacementList() { local int index; local NexgenSimpleListItem item; // Clear list. hudReplacementList.selectedItem = none; hudReplacementList.items.clear(); // Add weapon classes. while (index < arrayCount(client.sConf.replacementClass) && client.sConf.replacementClass[index] != "") { item = NexgenSimpleListItem(hudReplacementList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.replacementClass[index]; item.itemID = index; index++; } // Add 'add new item' option if list isn't full. if (index < arrayCount(client.sConf.replacementClass)) { item = NexgenSimpleListItem(hudReplacementList.items.insert(class'NexgenSimpleListItem')); item.displayText = client.lng.addNewItemTxt; item.itemID = -1; } // Signal item select event. hudReplacementSelected(); } /*************************************************************************************************** * * $DESCRIPTION Called when an item from the hud replacement list was selected. * **************************************************************************************************/ function hudReplacementSelected() { local NexgenSimpleListItem item; local string str; // Get selected item. item = NexgenSimpleListItem(hudReplacementList.selectedItem); // Update GUI. hudReplSaveButton.bDisabled = (item == none); hudReplRemButton.bDisabled = (item == none) || (item.itemID < 0); originalHUDClassInp.setDisabled((item == none)); replacementHUDClassInp.setDisabled((item == none)); if (item == none) { originalHUDClassInp.setValue(""); replacementHUDClassInp.setValue(""); } else if (item.itemID < 0) { originalHUDClassInp.setValue(""); replacementHUDClassInp.setValue(""); } else { str = client.sConf.replacementClass[item.itemID]; originalHUDClassInp.setValue(left(str, instr(str, "="))); replacementHUDClassInp.setValue(mid(str, instr(str, "=") + 1)); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // HUD replacement selected? if (control == hudReplacementList && eventType == DE_Click) { hudReplacementSelected(); } // Remove HUD replacement selected? if (control == hudReplRemButton && eventType == DE_Click && !hudReplRemButton.bDisabled && setRPCI()) { rpci.delHUDReplacementClass(NexgenSimpleListItem(hudReplacementList.selectedItem).itemID); } // Save HUD replacement selected? if (control == hudReplSaveButton && eventType == DE_Click && !hudReplSaveButton.bDisabled && setRPCI()) { rpci.saveHUDReplacementClass(NexgenSimpleListItem(hudReplacementList.selectedItem).itemID, class'NexgenUtil'.static.trim(originalHUDClassInp.getValue()), class'NexgenUtil'.static.trim(replacementHUDClassInp.getValue())); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_HUDReplacementList) { loadHUDReplacementList(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ IB "Game type"B/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPPrivateMsg * $VERSION 1.03 (21-10-2007 14:38) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen private message system control panel page. * **************************************************************************************************/ class NexgenRCPPrivateMsg extends NexgenPanel; var NexgenClientCore rpci; var UWindowDynamicTextArea history; var NexgenSimplePlayerListBox playerList; var NexgenSimplePlayerListBox blockedPlayerList; var UWindowSmallButton blockToggleButton; var UWindowSmallButton sendNormalButton; var UWindowSmallButton sendWindowedButton; var UWindowEditControl msgInp; var UWindowCheckbox blockAllInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); // Player / blocked player list. p = addContentPanel(); p.divideRegionH(2, defaultComponentDist); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.playerListTxt, true, TA_Center); playerList = NexgenSimplePlayerListBox(p.addListBox(class'NexgenSimplePlayerListBox')); p.addLabel(client.lng.blockedListTxt, true, TA_Center); p.splitRegionH(32, defaultComponentDist, , true); blockedPlayerList = NexgenSimplePlayerListBox(p.addListBox(class'NexgenSimplePlayerListBox')); p.divideRegionH(2); blockToggleButton = p.addButton(client.lng.blockToggleTxt); blockAllInp = p.addCheckBox(TA_Left, client.lng.blockAllPMsTxt); // 'Say' panel. splitRegionH(64, defaultComponentDist); p = addContentPanel(); p.divideRegionH(3, defaultComponentDist); p.addLabel(client.lng.messageTxt, true, TA_Center); msgInp = p.addEditbox(); p.splitRegionV(256); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); sendNormalButton = p.addButton(client.lng.sendNormalPMTxt); sendWindowedButton = p.addButton(client.lng.sendWindowedPMTxt); // History panel. p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.historyTxt, true, TA_Center); history = p.addDynamicTextArea(); // Configure components. playerList.register(self); blockedPlayerList.register(self); blockToggleButton.register(self); blockAllInp.register(self); sendNormalButton.register(self); sendWindowedButton.register(self); sendWindowedButton.bDisabled = !client.hasRight(client.R_Moderate); msgInp.register(self); msgInp.setMaxLength(255); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // Make sure the RPC interface is available. setRPCI(); // Player has joined the game? if (eventType == client.PE_PlayerJoined) { if (rpci != none && rpci.isBlocked(class'NexgenUtil'.static.getProperty(args, client.PA_ClientID))) { addPlayerToList(blockedPlayerList, playerNum, args); } else { addPlayerToList(playerList, playerNum, args); } } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); blockedPlayerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); updatePlayerInfo(blockedPlayerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local NexgenPlayerList selected; setRPCI(); // Block all clicked. if (control == blockAllInp && eventType == DE_Click && rpci != none) { rpci.bBlockAll = blockAllInp.bChecked; } // Send normal clicked. if (control == sendNormalButton && eventType == DE_Click) { sendMessage(false); } // Enter button pressed on message edit box. if (control == msgInp && eventType == DE_EnterPressed) { sendMessage(false); } // Send windowed clicked. if (control == sendWindowedButton && eventType == DE_Click && !sendWindowedButton.bDisabled) { sendMessage(true); } // Block / Unblock clicked. if (control == blockToggleButton && eventType == DE_Click && rpci != none) { if (playerList.selectedItem != none) { selected = NexgenPlayerList(playerList.selectedItem); rpci.blockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.blockMsg, selected.pName)); playerList.moveSelectedPlayerTo(blockedPlayerList); } else if (blockedPlayerList.selectedItem != none) { selected = NexgenPlayerList(blockedPlayerList.selectedItem); rpci.unblockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.unblockMsg, selected.pName)); blockedPlayerList.moveSelectedPlayerTo(playerList); } } // Unblocked player selected. if (control == playerList && eventType == DE_Click) { if (blockedPlayerList.selectedItem != none) { blockedPlayerList.selectedItem.bSelected = false; blockedPlayerList.selectedItem = none; } } // Blocked player selected. if (control == blockedPlayerList && eventType == DE_Click) { if (playerList.selectedItem != none) { playerList.selectedItem.bSelected = false; playerList.selectedItem = none; } } // Double click on unblocked player -> block that player. if (control == playerList && eventType == DE_DoubleClick && playerList.selectedItem != none && rpci != none) { selected = NexgenPlayerList(playerList.selectedItem); rpci.blockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.blockMsg, selected.pName)); playerList.moveSelectedPlayerTo(blockedPlayerList); } // Double click on blocked player -> unblock that player. if (control == blockedPlayerList && eventType == DE_DoubleClick && blockedPlayerList.selectedItem != none && rpci != none) { selected = NexgenPlayerList(blockedPlayerList.selectedItem); rpci.unblockPlayer(selected.pClientID); addHistoryMsg(client.lng.format(client.lng.unblockMsg, selected.pName)); blockedPlayerList.moveSelectedPlayerTo(playerList); } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to the to message history box. * $PARAM msg The message to add. * **************************************************************************************************/ function addHistoryMsg(string msg) { local string timeStamp; timeStamp = "[" $ right("0" $ client.level.hour, 2) $ ":" $ right("0" $ client.level.minute, 2) $ "]"; history.addText(timeStamp @ msg); } /*************************************************************************************************** * * $DESCRIPTION Send the currently enterted message to the selected player. * $PARAM bWindowed Whether the message to send should popup in a window. * **************************************************************************************************/ function sendMessage(optional bool bWindowed) { local NexgenPlayerList selectedPlayer; // Make sure the RPC interface is available. if (rpci == none) return; // Get selected player. if (playerList.selectedItem != none) { selectedPlayer = NexgenPlayerList(playerList.selectedItem); } else if (blockedPlayerList.selectedItem != none) { selectedPlayer = NexgenPlayerList(blockedPlayerList.selectedItem); } // Send message. if (selectedPlayer != none && msgInp.getValue() != "" && !client.isMuted() && !( // Spectators muted during matches? client.sConf.matchModeActivated && client.sConf.muteSpectatorsDuringMatch && client.gInf.gameState == client.gInf.GS_Playing && client.bSpectator && !selectedPlayer.isSpectator() && !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate) ) ) { addHistoryMsg(client.lng.format(client.lng.sendMsgTxt, selectedPlayer.pName, msgInp.getValue())); rpci.sendPM(selectedPlayer.pNum, msgInp.getValue(), bWindowed); msgInp.setValue(""); } // $TODO Play sound on error. } /*************************************************************************************************** * * $DESCRIPTION Called when a new private message was received. Adds the received message to the * chat history. * $PARAM msg The message that was received. * $PARAM pri Player replication info actor of the player that has send the message. * $REQUIRE pri != none * **************************************************************************************************/ function receiveMessage(string msg, PlayerReplicationInfo pri) { addHistoryMsg(client.lng.format(client.lng.receivedMsgTxt, pri.playerName, msg)); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ O/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPModerate * $VERSION 1.01 (1-8-2008 20:50) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen moderator control panel page. * **************************************************************************************************/ class NexgenRCPModerate extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenPlayerListBox playerList; var UMenuLabelControl ipAddressLabel; var UMenuLabelControl clientIDLabel; var UWindowSmallButton copyIPAddressButton; var UWindowSmallButton copyClientIDButton; var UWindowSmallButton muteToggleButton; var UWindowSmallButton setNameButton; var UWindowSmallButton kickButton; var UWindowSmallButton banButton; var UWindowSmallButton showMsgButton; var UWindowEditControl playerNameInp; var UWindowEditControl banReasonInp; var NexgenEditControl numMatchesInp; var NexgenEditControl numDaysInp; var UWindowEditControl messageInp; var UWindowCheckbox banForeverInp; var UWindowCheckbox banMatchesInp; var UWindowCheckbox banDaysInp; var UWindowCheckbox muteAllInp; var UWindowCheckbox allowNameChangeInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); // Player info. splitRegionH(49, defaultComponentDist); p = addContentPanel(); p.divideRegionH(2); p.splitRegionV(64); p.splitRegionV(64); p.addLabel(client.lng.ipAddressTxt, true); p.splitRegionV(48, , , true); p.addLabel(client.lng.clientIDTxt, true); p.splitRegionV(48, , , true); ipAddressLabel = p.addLabel(); copyIPAddressButton = p.addButton(client.lng.copyTxt); clientIDLabel = p.addLabel(); copyClientIDButton = p.addButton(client.lng.copyTxt); // Player controller. splitRegionH(51, defaultComponentDist); p = addContentPanel(); p.divideRegionH(2); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); muteToggleButton = p.addButton(client.lng.muteToggleTxt); p.skipRegion(); setNameButton = p.addButton(client.lng.setPlayerNameTxt); playerNameInp = p.addEditBox(); // Ban controller. splitRegionH(107, defaultComponentDist); p = addContentPanel(); p.divideRegionH(5); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.splitRegionV(96, defaultComponentDist); p.addLabel(client.lng.banReasonTxt); banReasonInp = p.addEditBox(); kickButton = p.addButton(client.lng.kickPlayerTxt); p.skipRegion(); banButton = p.addButton(client.lng.banPlayerTxt); p.splitRegionV(96, defaultComponentDist); p.skipRegion(); p.splitRegionV(96, defaultComponentDist); p.skipRegion(); p.splitRegionV(96, defaultComponentDist); banForeverInp = p.addCheckBox(TA_Left, client.lng.banForeverTxt); p.skipRegion(); banMatchesInp = p.addCheckBox(TA_Left, client.lng.banMatchesTxt); numMatchesInp = p.addEditBox(); banDaysInp = p.addCheckBox(TA_Left, client.lng.banDaysTxt); numDaysInp = p.addEditBox(); // Game controller. splitRegionH(65); p = addContentPanel(); p.divideRegionH(3); muteAllInp = p.addCheckBox(TA_Left, client.lng.muteAllTxt); allowNameChangeInp = p.addCheckBox(TA_Left, client.lng.allowNameChangeTxt); p.splitRegionV(96, defaultComponentDist); showMsgButton = p.addButton(client.lng.showAdminMessageTxt); messageInp = p.addEditBox(); // Configure components. playerNameInp.setMaxLength(32); banReasonInp.setMaxLength(250); numMatchesInp.setMaxLength(4); numMatchesInp.setNumericOnly(true); numDaysInp.setMaxLength(4); numDaysInp.setNumericOnly(true); messageInp.setMaxLength(250); playerList.register(self); muteToggleButton.register(self); setNameButton.register(self); kickButton.register(self); banButton.register(self); showMsgButton.register(self); banForeverInp.register(self); banMatchesInp.register(self); banDaysInp.register(self); muteAllInp.register(self); allowNameChangeInp.register(self); banMatchesInp.bChecked = true; numMatchesInp.setValue("3"); numDaysInp.setValue("7"); playerSelected(); banPeriodSelected(); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current game info settings. * **************************************************************************************************/ function setValues() { muteAllInp.bChecked = client.gInf.bMuteAll; allowNameChangeInp.bChecked = !client.gInf.bNoNameChange; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { local NexgenPlayerList item; item = NexgenPlayerList(playerList.selectedItem); muteToggleButton.bDisabled = (item == none); setNameButton.bDisabled = (item == none); kickButton.bDisabled = (item == none); banButton.bDisabled = (item == none || !client.hasRight(client.R_BanOperator)); copyIPAddressButton.bDisabled = (item == none); copyClientIDButton.bDisabled = (item == none); if (item == none) { playerNameInp.setValue(""); ipAddressLabel.setText(""); clientIDLabel.setText(""); } else { playerNameInp.setValue(item.pName); ipAddressLabel.setText(item.pIPAddress); clientIDLabel.setText(item.pClientID); } } /*************************************************************************************************** * * $DESCRIPTION Called when a ban period was selected from the list. * **************************************************************************************************/ function banPeriodSelected() { numMatchesInp.setDisabled(!banMatchesInp.bChecked); numDaysInp.setDisabled(!banDaysInp.bChecked); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Bans the currently selected player. * **************************************************************************************************/ function banPlayer() { local byte banPeriodType; local int banPeriodArgs; if (banMatchesInp.bChecked) { banPeriodType = client.sConf.BP_Matches; banPeriodArgs = int(class'NexgenUtil'.static.trim(numMatchesInp.getValue())); } else if (banDaysInp.bChecked) { banPeriodType = client.sConf.BP_UntilDate; banPeriodArgs = int(class'NexgenUtil'.static.trim(numDaysInp.getValue())); } else { banPeriodType = client.sConf.BP_Forever; } rpci.banPlayer(NexgenPlayerList(playerList.selectedItem).pNum, banPeriodType, banPeriodArgs, class'NexgenUtil'.static.trim(banReasonInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local NexgenPlayerList item; super.notify(control, eventType); setRPCI(); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled && rpci != none) { switch (control) { case muteToggleButton: rpci.togglePlayerMute(NexgenPlayerList(playerList.selectedItem).pNum); break; case setNameButton: rpci.setPlayerName(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(playerNameInp.getValue())); break; case kickButton: rpci.kickPlayer(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(banReasonInp.getValue())); break; case banButton: banPlayer(); break; case showMsgButton: rpci.showAdminMessage(class'NexgenUtil'.static.trim(messageInp.getValue())); break; case copyIPAddressButton: item = NexgenPlayerList(playerList.selectedItem); if (item != none) { getPlayerOwner().copyToClipboard(item.pIPAddress); } break; case copyClientIDButton: item = NexgenPlayerList(playerList.selectedItem); if (item != none) { getPlayerOwner().copyToClipboard(item.pClientID); } break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } // Ban period selected? if (control == banForeverInp && eventType == DE_Click) { banForeverInp.bChecked = true; banMatchesInp.bChecked = false; banDaysInp.bChecked = false; banPeriodSelected(); } else if (control == banMatchesInp && eventType == DE_Click) { banForeverInp.bChecked = false; banMatchesInp.bChecked = true; banDaysInp.bChecked = false; banPeriodSelected(); } else if (control == banDaysInp && eventType == DE_Click) { banForeverInp.bChecked = false; banMatchesInp.bChecked = false; banDaysInp.bChecked = true; banPeriodSelected(); } // Toggle mute all clicked? if (control == muteAllInp && eventType == DE_Click && rpci != none) { rpci.toggleGlobalMute(); } // Toggle allow name change clicked? if (control == allowNameChangeInp && eventType == DE_Click && rpci != none) { rpci.toggleGlobalNameChange(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); playerSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.gInf.IT_GlobalRights) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ e 5DBm&RA}A ,i .pp T#.A  Hi *-L' EBGBT: -Lmi  FBNUJ NeeNQQNXXNw w NEENA A NqqNi i N-L-L MB "Server boot command line"HBKBT^ :q, W/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPMiscNexgenSettings * $VERSION 1.06 (4-8-2008 16:19) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extra server settings control panel page. * **************************************************************************************************/ class NexgenRCPMiscNexgenSettings extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; var UWindowCheckbox autoUpdateBansInp; var UWindowCheckbox autoDelExpiredBansInp; var UWindowCheckbox broadcastAdminActionsInp; var UWindowCheckbox announceTeamKillsInp; var UWindowCheckbox enableNexgenMessageHUDInp; var UWindowCheckbox enableNexgenStartControlInp; var UWindowCheckbox restoreScoreOnTeamSwitchInp; var UWindowCheckbox defaultAllowTeamSwitchInp; var UWindowCheckbox defaultAllowTeamBalanceInp; var UWindowCheckbox defaultAllowNameChangeInp; var UWindowCheckbox autoRegisterServerInp; var UWindowEditControl gameWaitTimeInp; var UWindowEditControl gameStartDelayInp; var UWindowEditControl autoReconnectTimeInp; var UWindowEditControl maxIdleTimeInp; var UWindowEditControl maxIdleTimeCPInp; var UWindowEditControl spawnProtectTimeInp; var UWindowEditControl teamKillDmgProtectInp; var UWindowEditControl teamKillPushProtectInp; var UWindowEditControl autoDisableMatchTimeInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { // Create layout & add components. createPanelRootRegion(); splitRegionH(12, defaultComponentDist); addLabel(client.lng.nexgenMiscSettingsPanelTitle, true, TA_Center); splitRegionH(1, defaultComponentDist); addComponent(class'NexgenDummyComponent'); divideRegionV(2, 2 * defaultComponentDist); divideRegionH(11); divideRegionH(11); autoUpdateBansInp = addCheckBox(TA_Left, client.lng.autoUpdateBansTxt, true); autoDelExpiredBansInp = addCheckBox(TA_Left, client.lng.autoDelExpiredBansTxt, true); broadcastAdminActionsInp = addCheckBox(TA_Left, client.lng.broadcastAdminActionsTxt, true); announceTeamKillsInp = addCheckBox(TA_Left, client.lng.announceTeamKillsTxt, true); enableNexgenMessageHUDInp = addCheckBox(TA_Left, client.lng.enableNexgenMessageHUDTxt, true); enableNexgenStartControlInp = addCheckBox(TA_Left, client.lng.enableNexgenStartControlTxt, true); restoreScoreOnTeamSwitchInp = addCheckBox(TA_Left, client.lng.restoreScoreOnTeamSwitchTxt, true); defaultAllowTeamSwitchInp = addCheckBox(TA_Left, client.lng.defaultAllowTeamSwitchTxt, true); defaultAllowTeamBalanceInp = addCheckBox(TA_Left, client.lng.defaultAllowTeamBalanceTxt, true); defaultAllowNameChangeInp = addCheckBox(TA_Left, client.lng.defaultAllowNameChangeTxt, true); autoRegisterServerInp = addCheckBox(TA_Left, client.lng.autoRegisterServerTxt, true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); splitRegionV(64, , , true); skipRegion(); splitRegionV(196, , , true); addLabel(client.lng.gameWaitTimeTxt, true); gameWaitTimeInp = addEditBox(); addLabel(client.lng.gameStartDelayTxt, true); gameStartDelayInp = addEditBox(); addLabel(client.lng.autoReconnectTimeTxt, true); autoReconnectTimeInp = addEditBox(); addLabel(client.lng.maxIdleTimeTxt, true); maxIdleTimeInp = addEditBox(); addLabel(client.lng.maxIdleTimeCPTxt, true); maxIdleTimeCPInp = addEditBox(); addLabel(client.lng.spawnProtectTimeTxt, true); spawnProtectTimeInp = addEditBox(); addLabel(client.lng.teamKillDmgProtectTxt, true); teamKillDmgProtectInp = addEditBox(); addLabel(client.lng.teamKillPushProtectTxt, true); teamKillPushProtectInp = addEditBox(); addLabel(client.lng.autoDisableMatchTimeTxt, true); autoDisableMatchTimeInp = addEditBox(); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); /* splitRegionH(20, defaultComponentDist, , true); p = addContentPanel(); splitRegionV(196, , , true); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); p.divideRegionV(2, 2 * defaultComponentDist); p.divideRegionH(10); p.splitRegionV(64, , , true); autoUpdateBansInp = p.addCheckBox(TA_Left, client.lng.autoUpdateBansTxt, true); autoDelExpiredBansInp = p.addCheckBox(TA_Left, client.lng.autoDelExpiredBansTxt, true); broadcastAdminActionsInp = p.addCheckBox(TA_Left, client.lng.broadcastAdminActionsTxt, true); announceTeamKillsInp = p.addCheckBox(TA_Left, client.lng.announceTeamKillsTxt, true); enableNexgenMessageHUDInp = p.addCheckBox(TA_Left, client.lng.enableNexgenMessageHUDTxt, true); enableNexgenStartControlInp = p.addCheckBox(TA_Left, client.lng.enableNexgenStartControlTxt, true); restoreScoreOnTeamSwitchInp = p.addCheckBox(TA_Left, client.lng.restoreScoreOnTeamSwitchTxt, true); defaultAllowTeamSwitchInp = p.addCheckBox(TA_Left, client.lng.defaultAllowTeamSwitchTxt, true); defaultAllowTeamBalanceInp = p.addCheckBox(TA_Left, client.lng.defaultAllowTeamBalanceTxt, true); defaultAllowNameChangeInp = p.addCheckBox(TA_Left, client.lng.defaultAllowNameChangeTxt, true); p.divideRegionH(10); p.divideRegionH(10); p.addLabel(client.lng.gameWaitTimeTxt, true); p.addLabel(client.lng.gameStartDelayTxt, true); p.addLabel(client.lng.autoReconnectTimeTxt, true); p.addLabel(client.lng.maxIdleTimeTxt, true); p.addLabel(client.lng.maxIdleTimeCPTxt, true); p.addLabel(client.lng.spawnProtectTimeTxt, true); p.addLabel(client.lng.teamKillDmgProtectTxt, true); p.addLabel(client.lng.teamKillPushProtectTxt, true); p.addLabel(client.lng.autoDisableMatchTimeTxt, true); p.skipRegion(); gameWaitTimeInp = p.addEditBox(); gameStartDelayInp = p.addEditBox(); autoReconnectTimeInp = p.addEditBox(); maxIdleTimeInp = p.addEditBox(); maxIdleTimeCPInp = p.addEditBox(); spawnProtectTimeInp = p.addEditBox(); teamKillDmgProtectInp = p.addEditBox(); teamKillPushProtectInp = p.addEditBox(); autoDisableMatchTimeInp = p.addEditBox(); */ // Configure components. gameWaitTimeInp.setMaxLength(2); gameStartDelayInp.setMaxLength(2); autoReconnectTimeInp.setMaxLength(2); maxIdleTimeInp.setMaxLength(3); maxIdleTimeCPInp.setMaxLength(3); spawnProtectTimeInp.setMaxLength(2); teamKillDmgProtectInp.setMaxLength(2); teamKillPushProtectInp.setMaxLength(2); autoDisableMatchTimeInp.setMaxLength(2); gameWaitTimeInp.setNumericOnly(true); gameStartDelayInp.setNumericOnly(true); autoReconnectTimeInp.setNumericOnly(true); maxIdleTimeInp.setNumericOnly(true); maxIdleTimeCPInp.setNumericOnly(true); spawnProtectTimeInp.setNumericOnly(true); teamKillDmgProtectInp.setNumericOnly(true); teamKillPushProtectInp.setNumericOnly(true); autoDisableMatchTimeInp.setNumericOnly(true); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current server settings. * **************************************************************************************************/ function setValues() { autoUpdateBansInp.bChecked = client.sConf.autoUpdateBans; autoDelExpiredBansInp.bChecked = client.sConf.removeExpiredBans; broadcastAdminActionsInp.bChecked = client.sConf.broadcastAdminActions; announceTeamKillsInp.bChecked = client.sConf.broadcastTeamKillAttempts; enableNexgenMessageHUDInp.bChecked = client.sConf.useNexgenMessageHUD; enableNexgenStartControlInp.bChecked = client.sConf.enableNexgenStartControl; restoreScoreOnTeamSwitchInp.bChecked = client.sConf.restoreScoreOnTeamSwitch; defaultAllowTeamSwitchInp.bChecked = client.sConf.allowTeamSwitch; defaultAllowTeamBalanceInp.bChecked = client.sConf.allowTeamBalance; defaultAllowNameChangeInp.bChecked = client.sConf.allowNameChange; autoRegisterServerInp.bChecked = client.sConf.autoRegisterServer; gameWaitTimeInp.setValue(string(client.sConf.waitTime)); gameStartDelayInp.setValue(string(client.sConf.startTime)); autoReconnectTimeInp.setValue(string(client.sConf.autoReconnectTime)); maxIdleTimeInp.setValue(string(client.sConf.maxIdleTime)); maxIdleTimeCPInp.setValue(string(client.sConf.maxIdleTimeCP)); spawnProtectTimeInp.setValue(string(client.sConf.spawnProtectionTime)); teamKillDmgProtectInp.setValue(string(client.sConf.teamKillDamageProtectionTime)); teamKillPushProtectInp.setValue(string(client.sConf.teamKillPushProtectionTime)); autoDisableMatchTimeInp.setValue(string(client.sConf.autoDisableMatchTime)); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: setValues(); break; case saveButton: saveSettings(); break; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_ExtraServerSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Saves the server settings. * **************************************************************************************************/ function saveSettings() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Save settings. rpci.setServerSettingsExt1(autoUpdateBansInp.bChecked, autoDelExpiredBansInp.bChecked, broadcastAdminActionsInp.bChecked, announceTeamKillsInp.bChecked, enableNexgenMessageHUDInp.bChecked, enableNexgenStartControlInp.bChecked, restoreScoreOnTeamSwitchInp.bChecked, defaultAllowTeamSwitchInp.bChecked, defaultAllowTeamBalanceInp.bChecked, defaultAllowNameChangeInp.bChecked, autoRegisterServerInp.bChecked); rpci.setServerSettingsExt2(int(gameWaitTimeInp.getValue()), int(gameStartDelayInp.getValue()), int(autoReconnectTimeInp.getValue()), int(maxIdleTimeInp.getValue()), int(maxIdleTimeCPInp.getValue()), int(spawnProtectTimeInp.getValue()), int(teamKillDmgProtectInp.getValue()), int(teamKillPushProtectInp.getValue()), int(autoDisableMatchTimeInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ UB "Mutators not used"l{J!a.){X`.)RX\.){Q[.)RQsa`za`s\[z\[%&& xA 20.0RBhN/ro*iwo*KowK*pKa/!iK4KKBh *g+i *f+|oh@Joi@Ko *|Jhi..*K.k .8g %g ,wg I*g IVg  aSBbJgAro*ib Ze+c Za+^ob@`oc@Yo Z^`bc}.ZY}C}B2}Av }@F}4 aBiNio.Co-on gro*o| * aWB "Mutators used"W/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPMatchSet * $VERSION 1.04 (10-8-2008 11:39) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen match setup control panel page. * **************************************************************************************************/ class NexgenRCPMatchSet extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowSmallButton saveButton; var UWindowSmallButton startStopButton; var UWindowSmallButton resetButton; var NexgenPlayerListBox playerList; var UWindowEditControl tagInp[4]; var UWindowEditControl numGamesInp; var UWindowEditControl currGameInp; var UWindowEditControl passwordInp; var UWindowCheckbox specNeedNoPWInp; var UWindowCheckbox muteSpecsInp; var UWindowCheckbox enableBootControlInp; var UWindowCheckbox autoLockInp; var UWindowCheckbox autoPauseInp; var UWindowCheckbox autoSeperateInp; var UWindowSmallButton separateButton; var UWindowSmallButton sendPasswordButton; const numTeams = 4; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenPlayerList playerItem; local NexgenContentPanel p; local int region; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist, , true); p = addContentPanel(); // Match settings. p.splitRegionH(144, defaultComponentDist); p.splitRegionH(16); region = p.currRegion; p.skipRegion(); p.addLabel(client.lng.matchSettingsTxt, true, TA_Center); p.divideRegionH(7); p.divideRegionV(2); p.splitRegionV(96); specNeedNoPWInp = p.addCheckBox(, client.lng.matchSpecNoPassTxt); muteSpecsInp = p.addCheckBox(, client.lng.matchMuteSpecsTxt); enableBootControlInp = p.addCheckBox(, client.lng.matchBootControlTxt); autoLockInp = p.addCheckBox(, client.lng.matchAutoLockTeamsTxt); autoPauseInp = p.addCheckBox(, client.lng.matchAutoPauseTxt); //p.addCheckBox(, "Automatically take a screenshot at the end of each game"); // Should be a client option! p.splitRegionV(96); p.splitRegionV(96); p.addLabel(client.lng.passwordTxt); passwordInp = p.addEditBox(); p.addLabel(client.lng.matchNumOfGamesTxt); numGamesInp = p.addEditBox(, 48, AL_Left); p.addLabel(client.lng.matchCurrGameNumTxt); currGameInp = p.addEditBox(, 48, AL_Left); // Separate by tag settings. p.selectRegion(region); p.selectRegion(p.splitRegionH(72, defaultComponentDist)); p.splitRegionH(1); region = p.currRegion; p.skipRegion(); p.addComponent(class'NexgenDummyComponent'); p.splitRegionH(16); p.addLabel(client.lng.matchSeparateByTagTxt, true, TA_Center); p.splitRegionH(16, defaultComponentDist, , true); p.divideRegionH(2); p.splitRegionV(20); p.divideRegionV(numTeams); p.divideRegionV(numTeams); autoSeperateInp = p.addCheckBox(TA_Right); p.splitRegionV(192); for (index = 0; index < numTeams; index++) { p.addLabel(client.lng.getTeamName(index), , TA_Center); } for (index = 0; index < numTeams; index++) { tagInp[index] = p.addEditBox(, 64, AL_Center); tagInp[index].setMaxLength(16); } p.addLabel(client.lng.matchAutoTagSeparateTxt); p.splitRegionV(80, , , true); p.skipRegion(); separateButton = p.addButton(client.lng.matchDoSeparateTxt); // Match setup control buttons. p.selectRegion(region); p.selectRegion(p.splitRegionH(1)); p.addComponent(class'NexgenDummyComponent'); p.splitRegionH(16, , , true); p.skipRegion(); p.divideRegionV(3, defaultComponentDist); saveButton = p.addButton(client.lng.saveTxt); startStopButton = p.addButton(); resetButton = p.addButton(client.lng.resetTxt); // Send password to. splitRegionH(16, defaultComponentDist, , true); playerList = NexgenPlayerListBox(addListBox(class'NexgenSimplePlayerListBox')); sendPasswordButton = addButton(client.lng.sendPasswordTxt); // Configure components. numGamesInp.setNumericOnly(true); numGamesInp.setMaxLength(2); currGameInp.setNumericOnly(true); currGameInp.setMaxLength(2); passwordInp.setMaxLength(32); playerItem = playerList.addPlayer(); playerItem.pNum = -1; playerItem.pName = client.lng.allPlayersTxt; playerItem.pTeam = 4; separateButton.bDisabled = !client.player.gameReplicationInfo.bTeamGame; saveButton.register(self); startStopButton.register(self); resetButton.register(self); separateButton.register(self); playerList.register(self); sendPasswordButton.register(self); numGamesInp.register(self); currGameInp.register(self); passwordInp.register(self); specNeedNoPWInp.register(self); muteSpecsInp.register(self); enableBootControlInp.register(self); autoLockInp.register(self); autoPauseInp.register(self); autoSeperateInp.register(self); for (index = 0; index < numTeams; index++) { tagInp[index].register(self); } loadMatchSettings(); playerSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the match settings. * **************************************************************************************************/ function loadMatchSettings() { local int index; numGamesInp.setValue(string(client.sConf.matchesToPlay)); currGameInp.setValue(string(client.sConf.currentMatch)); passwordInp.setValue(client.sConf.decode(client.sConf.serverPassword)); specNeedNoPWInp.bChecked = !client.sConf.spectatorsNeedPassword; muteSpecsInp.bChecked = client.sConf.muteSpectatorsDuringMatch; enableBootControlInp.bChecked = client.sConf.enableMatchBootControl; autoLockInp.bChecked = client.sConf.matchAutoLockTeams; autoPauseInp.bChecked = client.sConf.matchAutoPause; autoSeperateInp.bChecked = client.sConf.matchAutoSeparate; for (index = 0; index < numTeams; index++) { tagInp[index].setValue(client.sConf.tagsToSeparate[index]); } if (client.sConf.matchModeActivated) { startStopButton.setText(client.lng.stopMatchTxt); } else { startStopButton.setText(client.lng.startMatchTxt); } startStopButton.bDisabled = false; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { sendPasswordButton.bDisabled = playerList.selectedItem == none; } /*************************************************************************************************** * * $DESCRIPTION Separates the players by the currently entered tags. * **************************************************************************************************/ function separatePlayers() { local string teamTags[4]; local int index; // Make sure the RPC interface is available. if (!setRPCI()) return; // Get tags. for (index = 0; index < numTeams; index++) { teamTags[index] = class'NexgenUtil'.static.trim(tagInp[index].getValue()); } // Separate players. rpci.separatePlayers(teamTags); } /*************************************************************************************************** * * $DESCRIPTION Sends the password to the currently selected player. * **************************************************************************************************/ function sendPassword() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Send password. rpci.sendPassword(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(passwordInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Sends the current match settings to the server. * **************************************************************************************************/ function saveSettings() { local string teamTags[4]; local int index; // Make sure the RPC interface is available. if (!setRPCI()) return; // Get tags. for (index = 0; index < numTeams; index++) { teamTags[index] = class'NexgenUtil'.static.trim(tagInp[index].getValue()); } // Update settings. rpci.updateMatchSettings(int(numGamesInp.getValue()), int(currGameInp.getValue()), passwordInp.getValue(), !specNeedNoPWInp.bChecked, muteSpecsInp.bChecked, enableBootControlInp.bChecked, autoLockInp.bChecked, autoPauseInp.bChecked, autoSeperateInp.bChecked, teamTags[0], teamTags[1], teamTags[2], teamTags[3]); } /*************************************************************************************************** * * $DESCRIPTION Starts / stops the match. * **************************************************************************************************/ function toggleMatchMode() { // Make sure the RPC interface is available. if (!setRPCI()) return; // Start / stop match. rpci.toggleMatchMode(); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: loadMatchSettings(); break; case saveButton: saveSettings(); break; case separateButton: separatePlayers(); break; case sendPasswordButton: sendPassword(); break; case startStopButton: toggleMatchMode(); break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } // Check if some settings were changed. if (eventType == DE_Change && !client.sConf.matchModeActivated && control != none && (control.isA('UWindowEditControl') || control.isA('UWindowCheckbox'))) { startStopButton.bDisabled = true; } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_MatchSettings) { loadMatchSettings(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ YB "Restart game on last map">W$J84s. W$>. V$>6& ZB "Enable Nexgen boot control"]B "Remove"@/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPMatchControl * $VERSION 1.04 (22-6-2008 17:59) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen match control panel page. * **************************************************************************************************/ class NexgenRCPMatchControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenPlayerListBox playerList; var UWindowSmallButton teamButtons[4]; var UWindowSmallButton pauseButton; var UWindowSmallButton endButton; var UWindowSmallButton restartButton; var UWindowSmallButton sendToURLButton; var UWindowSmallButton reconnectAsPlayerButton; var UWindowSmallButton reconnectAsSpecButton; var UWindowSmallButton disableTeamSwitchButton; var UWindowEditControl urlInp; var UWindowCheckbox allowTeamSwitchInp; var UWindowCheckbox allowTeamBalanceInp; var UWindowCheckbox lockTeamsInp; var UWindowCheckbox tournamentModeInp; var UWindowComboControl favouritesList; var color teamColor[4]; var color defaultButtonColor; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); splitRegionH(184, defaultComponentDist); // Player controls. p = addContentPanel(); p.splitRegionV(128, defaultComponentDist); p.divideRegionH(8); p.divideRegionH(8); for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index] = p.addButton(client.lng.format(client.lng.switchToTeamTxt, client.lng.getTeamName(index))); } sendToURLButton = p.addButton(client.lng.sendToURLTxt); reconnectAsPlayerButton = p.addButton(client.lng.reconnectAsPlayerTxt); reconnectAsSpecButton = p.addButton(client.lng.reconnectAsSpecTxt); disableTeamSwitchButton = p.addButton(client.lng.disableTeamSwitchTxt); p.skipRegion(); p.skipRegion(); p.skipRegion(); favouritesList = p.addListCombo(); urlInp = p.addEditBox(); // Global match controls. splitRegionV(140, defaultComponentDist); p = addContentPanel(); p.divideRegionH(3); pauseButton = p.addButton(client.lng.pauseGameTxt); endButton = p.addButton(client.lng.endGameTxt); restartButton = p.addButton(client.lng.restartGameTxt); p = addContentPanel(); p.divideRegionH(4); allowTeamSwitchInp = p.addCheckBox(TA_Left, client.lng.allowTeamSwitchTxt); allowTeamBalanceInp = p.addCheckBox(TA_Left, client.lng.allowTeamBalanceTxt); lockTeamsInp = p.addCheckBox(TA_Left, client.lng.lockTeamsTxt); tournamentModeInp = p.addCheckBox(TA_Left, client.lng.tournamentModeTxt); // Configure components. urlInp.setMaxLength(128); playerList.register(self); allowTeamSwitchInp.register(self); allowTeamBalanceInp.register(self); lockTeamsInp.register(self); tournamentModeInp.register(self); favouritesList.register(self); allowTeamSwitchInp.bDisabled = !client.player.gameReplicationInfo.bTeamGame; allowTeamBalanceInp.bDisabled = !client.player.gameReplicationInfo.bTeamGame; playerSelected(); setValues(); loadFavourites(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current game info settings. * **************************************************************************************************/ function setValues() { allowTeamSwitchInp.bChecked = !client.gInf.bNoTeamSwitch; allowTeamBalanceInp.bChecked = !client.gInf.bNoTeamBalance; lockTeamsInp.bChecked = client.gInf.bTeamsLocked; tournamentModeInp.bChecked = client.gInf.bTournamentMode; } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.gInf.IT_GlobalRights || infoType == client.gInf.IT_GameSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Called when a player was selected from the list. * **************************************************************************************************/ function playerSelected() { local NexgenPlayerList player; local int index; local bool bTeamGame; // Get selected player. player = NexgenPlayerList(playerList.selectedItem); // Determine which buttons can be used. bTeamGame = client.player.gameReplicationInfo.bTeamGame; if (player == none) { for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index].bDisabled = true; teamButtons[index].setTextColor(defaultButtonColor); } sendToURLButton.bDisabled = true; reconnectAsPlayerButton.bDisabled = true; reconnectAsSpecButton.bDisabled = true; disableTeamSwitchButton.bDisabled = true; } else { for (index = 0; index < arrayCount(teamButtons); index++) { teamButtons[index].bDisabled = !bTeamGame || player.isSpectator() || index == player.pTeam || index >= client.gInf.maxTeams; if (teamButtons[index].bDisabled) { teamButtons[index].setTextColor(defaultButtonColor); } else { teamButtons[index].setTextColor(teamColor[index]); } } sendToURLButton.bDisabled = false; reconnectAsPlayerButton.bDisabled = !player.isSpectator(); reconnectAsSpecButton.bDisabled = player.isSpectator(); disableTeamSwitchButton.bDisabled = player.isSpectator() || !bTeamGame; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); playerSelected(); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); playerSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); setRPCI(); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled && rpci != none) { switch (control) { case pauseButton: rpci.pauseGame(); break; case endButton: rpci.endGame(); break; case restartButton: rpci.restartGame(); break; case teamButtons[0]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 0); break; case teamButtons[1]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 1); break; case teamButtons[2]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 2); break; case teamButtons[3]: rpci.setPlayerTeam(NexgenPlayerList(playerList.selectedItem).pNum, 3); break; case disableTeamSwitchButton: rpci.toggleTeamSwitch(NexgenPlayerList(playerList.selectedItem).pNum); break; case reconnectAsPlayerButton: rpci.reconnectPlayer(NexgenPlayerList(playerList.selectedItem).pNum, false); break; case reconnectAsSpecButton: rpci.reconnectPlayer(NexgenPlayerList(playerList.selectedItem).pNum, true); break; case sendToURLButton: rpci.sendPlayerToURL(NexgenPlayerList(playerList.selectedItem).pNum, class'NexgenUtil'.static.trim(urlInp.getValue())); break; } } // Checkbox pressed? if (control != none && eventType == DE_Click && control.isA('UWindowCheckbox') && !UWindowCheckbox(control).bDisabled && rpci != none) { switch (control) { case allowTeamSwitchInp: rpci.toggleGlobalTeamSwitch(); break; case allowTeamBalanceInp: rpci.toggleGlobalTeamBalance(); break; case lockTeamsInp: rpci.toggleLockedTeams(); break; case tournamentModeInp: rpci.toggleGlobalTournamentMode(); break; } } // Player selected? if (control == playerList && eventType == DE_Click) { playerSelected(); } // Server selected? if (control == favouritesList && eventType == DE_Change) { serverSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Load the favourites from this client. * **************************************************************************************************/ function loadFavourites() { local int index; local int favsCount; local string favStr; local string serverName; local string serverIP; local string serverPort; // Get current favorites. favsCount = class'UBrowserFavoritesFact'.default.favoriteCount; for (index = 0; index < favsCount; index++) { favStr = class'UBrowserFavoritesFact'.default.favorites[index]; class'NexgenUtil'.static.split2(favStr, serverName, favStr, "\\"); class'NexgenUtil'.static.split2(favStr, serverIP, favStr, "\\"); class'NexgenUtil'.static.split2(favStr, serverPort, favStr, "\\"); serverName = class'NexgenUtil'.static.trim(serverName); if (serverName != "") { favouritesList.addItem(serverName, "unreal://" $ serverIP $ ":" $ (int(serverPort) - 1)); } } favouritesList.addItem("", ""); } /*************************************************************************************************** * * $DESCRIPTION Called when a server has been selected from the favourites list.. * **************************************************************************************************/ function serverSelected() { urlInp.setValue(favouritesList.getValue2()); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ _B "Remove ban"Mk/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPLogSettings * $VERSION 1.01 (18-6-2008 12:43) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen log settings control panel page. * **************************************************************************************************/ class NexgenRCPLogSettings extends NexgenPanel; var NexgenClientCore rpci; var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; var UWindowCheckbox logToConsoleInp; var UWindowCheckbox logEventsInp; var UWindowCheckbox logMessagesInp; var UWindowCheckbox logChatMessagesInp; var UWindowCheckbox logPrivateMessagesInp; var UWindowCheckbox logAdminActionsInp; var UWindowCheckbox logToFileInp; var UWindowEditControl logFilePathInp; var UWindowEditControl logFileExtensionInp; var UWindowEditControl logFileNameFormatInp; var UWindowEditControl logTimeStampFormatInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local int region; rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); // Create layout & add components. createPanelRootRegion(); splitRegionH(12, defaultComponentDist); addLabel(client.lng.logSettingsPanelTitle, true, TA_Center); splitRegionH(1, defaultComponentDist); addComponent(class'NexgenDummyComponent'); divideRegionV(2, 2 * defaultComponentDist); divideRegionH(6); divideRegionH(6); logToConsoleInp = addCheckBox(TA_Left, client.lng.logToConsoleTxt, true); logEventsInp = addCheckBox(TA_Left, client.lng.logEventsTxt, true); logMessagesInp = addCheckBox(TA_Left, client.lng.logMessagesTxt, true); logChatMessagesInp = addCheckBox(TA_Left, client.lng.logChatMessagesTxt, true); logPrivateMessagesInp = addCheckBox(TA_Left, client.lng.logPrivateMessagesTxt, true); logAdminActionsInp = addCheckBox(TA_Left, client.lng.logAdminActionsTxt, true); logToFileInp = addCheckBox(TA_Left, client.lng.logToFileTxt, true); splitRegionV(80); splitRegionV(80); splitRegionV(80); splitRegionV(80); splitRegionV(196, , , true); addLabel(client.lng.logFilePathTxt, true); logFilePathInp = addEditBox(); addLabel(client.lng.logFileExtensionTxt, true); logFileExtensionInp = addEditBox(); addLabel(client.lng.logFileNameFormatTxt, true); logFileNameFormatInp = addEditBox(); addLabel(client.lng.logTimeStampFormatTxt, true); logTimeStampFormatInp = addEditBox(); skipRegion(); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); // Configure components. logFilePathInp.setMaxLength(200); logFileExtensionInp.setMaxLength(10); logFileNameFormatInp.setMaxLength(100); logTimeStampFormatInp.setMaxLength(100); setValues(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of all input components to the current settings. * **************************************************************************************************/ function setValues() { logToConsoleInp.bChecked = client.sConf.logToConsole; logEventsInp.bChecked = client.sConf.logEvents; logMessagesInp.bChecked = client.sConf.logSystemMessages; logChatMessagesInp.bChecked = client.sConf.logChatMessages; logPrivateMessagesInp.bChecked = client.sConf.logPrivateMessages; logAdminActionsInp.bChecked = client.sConf.logAdminActions; logToFileInp.bChecked = client.sConf.logToFile; logFilePathInp.setValue(client.sConf.logPath); logFileExtensionInp.setValue(client.sConf.logFileExtension); logFileNameFormatInp.setValue(client.sConf.logFileNameFormat); logTimeStampFormatInp.setValue(client.sConf.logFileTimeStampFormat); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case resetButton: setValues(); break; case saveButton: saveSettings(); break; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_LogSettings) { setValues(); } } /*************************************************************************************************** * * $DESCRIPTION Saves the server settings. * **************************************************************************************************/ function saveSettings() { rpci.setLogSettings(logEventsInp.bChecked, logMessagesInp.bChecked, logChatMessagesInp.bChecked, logPrivateMessagesInp.bChecked, logAdminActionsInp.bChecked, logToConsoleInp.bChecked, logToFileInp.bChecked, logFilePathInp.getValue(), logFileExtensionInp.getValue(), logFileNameFormatInp.getValue(), logTimeStampFormatInp.getValue()); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ dB "Update ban"MeD5wR$YQYe}eI$fYO%oO,WpWv :Of&O9W  eBY$G8TfaY$-]']-] {a (ada-]d-] Y@gB "Create new ban"iBS$'&~pp,f ,pp,S$,% Jz/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPIgnoredWeaponsSettings * $VERSION 1.03 (20-6-2008 14:24) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen about control panel page. * **************************************************************************************************/ class NexgenRCPIgnoredWeaponsSettings extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox ignoredWeaponList; var UWindowSmallButton weapSaveButton; var UWindowSmallButton weapRemButton; var UWindowCheckbox ignorePrimaryFireInp; var UWindowCheckbox ignoreAltFireInp; var NexgenEditControl weaponClassInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { // Create layout & add components. createPanelRootRegion(); // Ignored weapons. splitRegionV(256, defaultComponentDist); ignoredWeaponList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); divideRegionH(4); addLabel(client.lng.ignoredWeaponsTxt, true, TA_Center); splitRegionV(96); divideRegionV(2, 2 * defaultComponentDist); splitRegionV(192); addLabel(client.lng.weaponClassTxt); weaponClassInp = addEditBox(); ignorePrimaryFireInp = addCheckBox(TA_Left, client.lng.ignorePrimaryFireTxt); ignoreAltFireInp = addCheckBox(TA_Left, client.lng.ignoreAltFireTxt); divideRegionV(2, defaultComponentDist); skipRegion(); weapSaveButton = addButton(client.lng.saveTxt); weapRemButton = addButton(client.lng.removeTxt); // Configure components. weaponClassInp.setMaxLength(64); loadIgnoredWeaponList(); } /*************************************************************************************************** * * $DESCRIPTION Loads the list of weapons ignored by the spawn protector. * **************************************************************************************************/ function loadIgnoredWeaponList() { local int index; local NexgenSimpleListItem item; local string weaponClass; local string remaining; // Clear list. ignoredWeaponList.selectedItem = none; ignoredWeaponList.items.clear(); // Add weapon classes. while (index < arrayCount(client.sConf.spawnProtectExcludeWeapons) && client.sConf.spawnProtectExcludeWeapons[index] != "") { class'NexgenUtil'.static.split(client.sConf.spawnProtectExcludeWeapons[index], weaponClass, remaining); item = NexgenSimpleListItem(ignoredWeaponList.items.append(class'NexgenSimpleListItem')); item.displayText = weaponClass; item.itemID = index; index++; } // Add 'add new item' option if list isn't full. if (index < arrayCount(client.sConf.spawnProtectExcludeWeapons)) { item = NexgenSimpleListItem(ignoredWeaponList.items.insert(class'NexgenSimpleListItem')); item.displayText = client.lng.addNewItemTxt; item.itemID = -1; } // Signal item select event. weaponSelected(); } /*************************************************************************************************** * * $DESCRIPTION Called when an item from the ignored weapon list was selected. * **************************************************************************************************/ function weaponSelected() { local NexgenSimpleListItem item; local string weaponClass; local string excludedModes; // Get selected item. item = NexgenSimpleListItem(ignoredWeaponList.selectedItem); // Update GUI. weapSaveButton.bDisabled = (item == none); weapRemButton.bDisabled = (item == none) || (item.itemID < 0); ignorePrimaryFireInp.bDisabled = (item == none); ignoreAltFireInp.bDisabled = (item == none); weaponClassInp.setDisabled((item == none)); if (item == none) { ignorePrimaryFireInp.bChecked = false; ignoreAltFireInp.bChecked = false; weaponClassInp.setValue(""); } else if (item.itemID < 0) { ignorePrimaryFireInp.bChecked = false; ignoreAltFireInp.bChecked = false; weaponClassInp.setValue(""); } else { class'NexgenUtil'.static.split(client.sConf.spawnProtectExcludeWeapons[item.itemID], weaponClass, excludedModes); ignorePrimaryFireInp.bChecked = (instr(excludedModes, client.sConf.IW_Fire) >= 0); ignoreAltFireInp.bChecked = (instr(excludedModes, client.sConf.IW_AltFire) >= 0); weaponClassInp.setValue(weaponClass); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Weapon selected? if (control == ignoredWeaponList && eventType == DE_Click) { weaponSelected(); } // Remove weapon selected? if (control == weapRemButton && eventType == DE_Click && !weapRemButton.bDisabled && setRPCI()) { rpci.delIgnoredWeapon(NexgenSimpleListItem(ignoredWeaponList.selectedItem).itemID); } // Save weapon selected? if (control == weapSaveButton && eventType == DE_Click && !weapSaveButton.bDisabled && setRPCI()) { rpci.saveIgnoredWeapon(NexgenSimpleListItem(ignoredWeaponList.selectedItem).itemID, class'NexgenUtil'.static.trim(weaponClassInp.getValue()), ignorePrimaryFireInp.bChecked, ignoreAltFireInp.bChecked); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_ExclWeaponList) { loadIgnoredWeaponList(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ hB "Client IDs"kB "IP addresses"qBj F|[j  N\j %T-F{R Spectatork{ n$f % qY-_ '{ g=j  Oj %f j  bf  Oj  qYMB-F{R SpectatorY{ n$ ~}/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPHome * $VERSION 1.04 (27-11-2007 23:11) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen client start/home control panel page. * **************************************************************************************************/ class NexgenRCPHome extends NexgenPanel; var UWindowSmallButton teamBalanceButton; var UWindowSmallButton teamButton[4]; var UWindowSmallButton playSpecButton; var UWindowSmallButton reconnectButton; var UWindowSmallButton disconnectButton; var UWindowSmallButton exitButton; var UWindowSmallButton mapVoteButton; var UWindowSmallButton startButton; var UWindowSmallButton loginButton; var UMenuLabelControl serverTitleLabel; var color teamColor[4]; var color rightGranted; var color rightDenied; var color rightNotDefined; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local NexgenContentPanel rightPanel; local UMenuLabelControl l; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(96, defaultComponentDist, , true); splitRegionH(32, defaultComponentDist); divideRegionH(14); // Title. p = addContentPanel(); serverTitleLabel = p.addLabel(client.sConf.serverName, true, TA_Center); // Rights overview. p = addContentPanel(); p.splitRegionH(32, defaultComponentDist); p.divideRegionH(2); p.divideRegionV(2, defaultComponentDist); p.addLabel(client.lng.format(client.lng.welcomeTxt, client.playerName, client.title), true); p.addLabel(client.lng.rightsOverviewTxt); p.divideRegionH(arraycount(client.sConf.rightsDef) / 2, defaultComponentDist); p.divideRegionH(arraycount(client.sConf.rightsDef) / 2, defaultComponentDist); for (index = 0; index < arraycount(client.sConf.rightsDef); index++) { rightPanel = p.addContentPanel(PBT_Transparent); l = rightPanel.addLabel("", true, TA_Center); rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { l.setText(client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); l.setTextColor(rightNotDefined); } else { l.setText(mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); if (client.hasRight(left(rightDef, instr(rightDef, client.sConf.separator)))) { l.setTextColor(rightGranted); rightPanel.panelBGType = PBT_Default; } else { l.setTextColor(rightDenied); } } } // Sidebar buttons. teamBalanceButton = addButton(client.lng.teamBalanceTxt); teamButton[0] = addButton(client.lng.redTeamTxt); teamButton[1] = addButton(client.lng.blueTeamTxt); teamButton[2] = addButton(client.lng.greenTeamTxt); teamButton[3] = addButton(client.lng.goldTeamTxt); skipRegion(); playSpecButton = addButton(); reconnectButton = addButton(client.lng.reconnectTxt); disconnectButton = addButton(client.lng.disconnectTxt); exitButton = addButton(client.lng.exitTxt); skipRegion(); mapVoteButton = addButton(client.lng.mapVoteTxt); startButton = addButton(client.lng.startTxt); loginButton = addButton(client.lng.loginTxt); // Configure components. if (client.bSpectator) { playSpecButton.setText(client.lng.playTxt); } else { playSpecButton.setText(client.lng.spectateTxt); } setupTeamButtons(); if (client.gInf.gameState > client.gInf.GS_Ready) { startButton.bDisabled = true; } } /*************************************************************************************************** * * $DESCRIPTION Sets the properties of the team control buttons. * **************************************************************************************************/ function setupTeamButtons() { local TournamentGameReplicationInfo gri; local int index; // Check which buttons should be disabled. if (client.bSpectator || !client.player.gameReplicationInfo.bTeamGame) { if (!client.player.gameReplicationInfo.bTeamGame) { teamBalanceButton.bDisabled = true; } for (index = 0; index < arrayCount(teamButton); index++) { teamButton[index].bDisabled = true; } } else { for (index = 0; index < arrayCount(teamButton); index++) { teamButton[index].bDisabled = index >= client.gInf.maxTeams; } } // Set button colors. for (index = 0; index < arrayCount(teamButton); index++) { if (!teamButton[index].bDisabled) { teamButton[index].setTextColor(teamColor[index]); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Button pressed? if (control != none && eventType == DE_Click && control.isA('UWindowSmallButton') && !UWindowSmallButton(control).bDisabled) { switch (control) { case teamBalanceButton: client.player.consoleCommand("mutate nsc balanceteams"); break; case teamButton[0]: client.player.consoleCommand("mutate nsc setteam 0"); break; case teamButton[1]: client.player.consoleCommand("mutate nsc setteam 1"); break; case teamButton[2]: client.player.consoleCommand("mutate nsc setteam 2"); break; case teamButton[3]: client.player.consoleCommand("mutate nsc setteam 3"); break; case playSpecButton: if (client.bSpectator) { client.player.consoleCommand("mutate nsc play"); } else { client.player.consoleCommand("mutate nsc spectate"); } UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case reconnectButton: client.player.consoleCommand("reconnect"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case disconnectButton: client.player.consoleCommand("disconnect"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case exitButton: client.player.consoleCommand("exit"); break; case mapVoteButton: client.player.consoleCommand("mutate nsc openvote"); UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); break; case startButton: client.player.consoleCommand("mutate nsc start"); break; case loginButton: client.showPopup("NexgenAdminLoginDialog"); break; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_GlobalServerSettings) { serverTitleLabel.setText(client.sConf.serverName); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ lB "Until 'date'"nB "'x' days"x/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPGameInfo * $VERSION 1.04 (6-11-2007 11:49) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen game information overview control panel page. * **************************************************************************************************/ class NexgenRCPGameInfo extends NexgenPanel; var NexgenPlayerListBox playerList; var NexgenSimpleListBox mutatorList; var UMenuLabelControl timeLimitLabel; var UMenuLabelControl fragLimitLabel; var UMenuLabelControl teamScoreLimitLabel; var UMenuLabelControl gameSpeedLabel; var UWindowCheckbox enableTeamSwitchInp; var UWindowCheckbox enableTeamBalanceInp; var UWindowCheckbox teamsLockedInp; var UWindowCheckbox allowNameChangeInp; var UMenuLabelControl fileLabel; var UMenuLabelControl titleLabel; var UMenuLabelControl authorLabel; var UMenuLabelControl playersLabel; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; // Create layout & add components. createWindowRootRegion(); divideRegionV(2, defaultComponentDist); splitRegionH(160, defaultComponentDist); splitRegionH(96, defaultComponentDist); splitRegionH(16); splitRegionH(16); splitRegionH(16); splitRegionH(16); // Game info. addLabel(client.player.gameReplicationInfo.gameName, true, TA_Center); p = addContentPanel(); p.splitRegionV(200/3, , true); p.divideRegionH(8); p.divideRegionH(8); p.addLabel(client.lng.timeLimitTxt, true); p.addLabel(client.lng.scoreLimitTxt, true); p.addLabel(client.lng.teamScoreLimitTxt, true); p.addLabel(client.lng.gameSpeedTxt, true); p.addLabel(client.lng.teamSwitchEnabledTxt, true); p.addLabel(client.lng.teamBalanceEnabledTxt, true); p.addLabel(client.lng.teamsLockedTxt, true); p.addLabel(client.lng.nameChangeAllowedTxt, true); timeLimitLabel = p.addLabel(); fragLimitLabel = p.addLabel(); teamScoreLimitLabel = p.addLabel(); gameSpeedLabel = p.addLabel(); enableTeamSwitchInp = p.addCheckBox(TA_right); enableTeamBalanceInp = p.addCheckBox(TA_right); teamsLockedInp = p.addCheckBox(TA_right); allowNameChangeInp = p.addCheckBox(TA_right); // Mutators. addLabel(client.lng.mutatorsTxt, true, TA_Center); mutatorList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Level info. addLabel(client.lng.levelTxt, true, TA_Center); p = addContentPanel(); p.splitRegionV(72, defaultComponentDist); if (client.player.level.screenshot == none) { p.addComponent(class'NexgenDummyComponent', 64, 64, AL_Center, AL_Center); } else { p.addImageBox(client.player.level.screenshot, true, 64, 64); } p.splitRegionV(48); p.divideRegionH(4); p.divideRegionH(4); p.addLabel(client.lng.fileTxt, true); p.addLabel(client.lng.titleTxt, true); p.addLabel(client.lng.authorTxt, true); p.addLabel(client.lng.idealPlayerCountTxt, true); fileLabel = p.addLabel(); titleLabel = p.addLabel(); authorLabel = p.addLabel(); playersLabel = p.addLabel(); // Player info. addLabel(client.lng.playerListTxt, true, TA_Center); playerList = NexgenPlayerListBox(addListBox(class'NexgenPlayerListBox')); // Configure components. enableTeamSwitchInp.bDisabled = true; enableTeamBalanceInp.bDisabled = true; teamsLockedInp.bDisabled = true; allowNameChangeInp.bDisabled = true; setGameInfo(); setLevelInfo(); loadMutatorList(); } /*************************************************************************************************** * * $DESCRIPTION Sets the values of the game info labels. * **************************************************************************************************/ function setGameInfo() { local GameReplicationInfo GRI; local TournamentGameReplicationInfo TGRI; GRI = client.player.gameReplicationInfo; TGRI = TournamentGameReplicationInfo(GRI); if (TGRI != none) { timeLimitLabel.setText(string(TGRI.timeLimit)); fragLimitLabel.setText(string(TGRI.fragLimit)); teamScoreLimitLabel.setText(string(TGRI.goalTeamScore)); } gameSpeedLabel.setText(int(100.0 * client.gInf.gameSpeed) $ "%"); enableTeamSwitchInp.bChecked = !client.gInf.bNoTeamSwitch; enableTeamBalanceInp.bChecked = !client.gInf.bNoTeamBalance; teamsLockedInp.bChecked = client.gInf.bTeamsLocked; allowNameChangeInp.bChecked = !client.gInf.bNoNameChange; } /*************************************************************************************************** * * $DESCRIPTION Sets the values of the level info labels. * **************************************************************************************************/ function setLevelInfo() { local string levelFile; levelFile = string(client); levelFile = left(levelFile, instr(levelFile, ".")) $ ".unr"; fileLabel.setText(levelFile); titleLabel.setText(client.player.level.summary.title); authorLabel.setText(client.player.level.summary.author); playersLabel.setText(client.player.level.summary.idealPlayerCount); } /*************************************************************************************************** * * $DESCRIPTION Loads the list of active mutators. * **************************************************************************************************/ function loadMutatorList() { local NexgenSimpleListItem item; local string remaining; local string mutatorIndex; local string mutatorInfo; local string mutatorClass; local string mutatorName; // For each mutator index.. remaining = client.sConf.activeMutatorIndices; while (remaining != "") { // Get index. class'NexgenUtil'.static.split(remaining, mutatorIndex, remaining); // Get mutator info. mutatorInfo = client.sConf.mutatorInfo[int(mutatorIndex)]; class'NexgenUtil'.static.split(mutatorInfo, mutatorClass, mutatorName); // Add mutator to the list. item = NexgenSimpleListItem(mutatorList.items.append(class'NexgenSimpleListItem')); item.displayText = mutatorName; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // Player has joined the game? if (eventType == client.PE_PlayerJoined) { addPlayerToList(playerList, playerNum, args); } // Player has left the game? if (eventType == client.PE_PlayerLeft) { playerList.removePlayer(playerNum); } // Attribute changed? if (eventType == client.PE_AttributeChanged) { updatePlayerInfo(playerList, playerNum, args); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { if (infoType == client.gInf.IT_GlobalRights) { setGameInfo(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ pB "'x' matches"O[/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPClientConfig * $VERSION 1.06 (8-3-2008 22:13) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen client settings control panel page. * **************************************************************************************************/ class NexgenRCPClientConfig extends NexgenPanel; var UWindowCheckbox enableNexgenHUDInp; var UWindowCheckbox useMsgFlashEffectInp; var UWindowCheckbox showPlayerLocationInp; var UWindowCheckbox playPMSoundInp; var UWindowCheckbox autoSSNormalGameInp; var UWindowCheckbox autoSSMatchInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; // Create layout & add components. setAcceptsFocus(); createPanelRootRegion(); //createWindowRootRegion(); splitRegionV(196, defaultComponentDist); // Keybindings. addSubPanel(class'NexgenCPKeyBind'); // User Interface settings. splitRegionH(96, defaultComponentDist); p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.UISettingsTxt, true, TA_Center); p.divideRegionH(4); enableNexgenHUDInp = p.addCheckBox(TA_Left, client.lng.enableMsgHUDTxt); useMsgFlashEffectInp = p.addCheckBox(TA_Left, client.lng.msgFlashEffectTxt); showPlayerLocationInp = p.addCheckBox(TA_Left, client.lng.showPlayerLocationTxt); playPMSoundInp = p.addCheckBox(TA_Left, client.lng.pmSoundTxt); // Other stuff. splitRegionH(64, defaultComponentDist); p = addContentPanel(); p.splitRegionH(16); p.addLabel(client.lng.miscSettingsTxt, true, TA_Center); p.divideRegionH(2); autoSSNormalGameInp = p.addCheckBox(TA_Left, client.lng.autoSSNormalGameTxt); autoSSMatchInp = p.addCheckBox(TA_Left, client.lng.autoSSMatchTxt); // Configure components. enableNexgenHUDInp.register(self); useMsgFlashEffectInp.register(self); showPlayerLocationInp.register(self); playPMSoundInp.register(self); autoSSNormalGameInp.register(self); autoSSMatchInp.register(self); enableNexgenHUDInp.bChecked = client.gc.get(client.SSTR_UseNexgenHUD, "true") ~= "true"; useMsgFlashEffectInp.bChecked = client.gc.get(client.SSTR_FlashMessages, "true") ~= "true"; showPlayerLocationInp.bChecked = client.gc.get(client.SSTR_ShowPlayerLocation, "true") ~= "true"; playPMSoundInp.bChecked = client.gc.get(client.SSTR_PlayPMSound, "true") ~= "true"; autoSSNormalGameInp.bChecked = client.gc.get(client.SSTR_AutoSSNormalGame, "false") ~= "true"; autoSSMatchInp.bChecked = client.gc.get(client.SSTR_AutoSSMatch, "true") ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Toggle Nexgen HUD on/off. if (control == enableNexgenHUDInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_UseNexgenHUD, string(enableNexgenHUDInp.bChecked)); client.gc.saveConfig(); // Change HUD on the fly! if (client.sConf.HUDReplacementClass != none) { if (enableNexgenHUDInp.bChecked) { client.setNexgenMessageHUD(true); } else { client.setNexgenMessageHUD(false); } } } // Toggle message flash effect on/off. if (control == useMsgFlashEffectInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_FlashMessages, string(useMsgFlashEffectInp.bChecked)); client.gc.saveConfig(); // Apply setting. client.nscHUD.bFlashMessages = useMsgFlashEffectInp.bChecked; } // Toggle show player location in teamsay messages on/off. if (control == showPlayerLocationInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_ShowPlayerLocation, string(showPlayerLocationInp.bChecked)); client.gc.saveConfig(); // Apply setting. client.nscHUD.bShowPlayerLocation = showPlayerLocationInp.bChecked; } // Toggle private message sound on/off. if (control == playPMSoundInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_PlayPMSound, string(playPMSoundInp.bChecked)); client.gc.saveConfig(); } // Toggle auto screenshot for normal games on/off. if (control == autoSSNormalGameInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_AutoSSNormalGame, string(autoSSNormalGameInp.bChecked)); client.gc.saveConfig(); } // Toggle auto screenshot for matches on/off. if (control == autoSSMatchInp && eventType == DE_Click) { // Save setting. client.gc.set(client.SSTR_AutoSSMatch, string(autoSSMatchInp.bChecked)); client.gc.saveConfig(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ tB "Forever"sBi$x::$T fiuw*ST!uw*-zSi!uST!u }/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPBootControl * $VERSION 1.05 (15-12-2007 15:38) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen boot control panel page. * **************************************************************************************************/ class NexgenRCPBootControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var UWindowComboControl gameTypeList; var NexgenSimpleListBox inclMutatorList; var NexgenSimpleListBox exclMutatorList; var UWindowSmallButton rebootButton; var UWindowSmallButton saveButton; var UWindowSmallButton resetButton; var NexgenEditControl mapPrefixInp; var NexgenEditControl extraOptionsInp; var NexgenEditControl commandsInp; var UWindowCheckbox enableBootControlInp; var UWindowCheckbox restartOnLastGameInp; var UWindowDynamicTextArea previewBox; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int region; // Create layout & add components. createWindowRootRegion(); splitRegionH(20, defaultComponentDist); divideRegionV(2); splitRegionH(20, defaultComponentDist, , true); splitRegionV(20, defaultComponentDist); splitRegionV(20, defaultComponentDist); p = addContentPanel(); splitRegionV(196, , , true); enableBootControlInp = addCheckBox(TA_Right); addLabel(client.lng.enableBootCtrlTxt, true); restartOnLastGameInp = addCheckBox(TA_Right); addLabel(client.lng.restartOnLastGameTxt, true); rebootButton = addButton(client.lng.rebootTxt, 96, AL_Left); divideRegionV(2, defaultComponentDist); saveButton = addButton(client.lng.saveTxt); resetButton = addButton(client.lng.resetTxt); // Mutator list. p.splitRegionV(65, 2 * defaultComponentDist, true); region = p.currRegion; p.skipRegion(); p.divideRegionH(2); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.inclMutatorsTxt, true); inclMutatorList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.addLabel(client.lng.exclMutatorsTxt, true); exclMutatorList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); // Server boot command line preview. p.selectRegion(region); p.selectRegion(p.splitRegionH(128, defaultComponentDist)); region = p.currRegion; p.skipRegion(); p.splitRegionH(16); p.addLabel(client.lng.bootCmdLineTxt, True); previewBox = p.addDynamicTextArea(); // Server boot options. p.selectRegion(region); p.selectRegion(p.divideRegionH(4, defaultComponentDist)); p.splitRegionV(64); p.splitRegionV(64); p.splitRegionH(16); p.splitRegionH(16); p.addLabel(client.lng.gameTypeTxt, true); gameTypeList = p.addListCombo(); p.addLabel(client.lng.mapPrefixTxt, true); mapPrefixInp = p.addEditBox(); p.addLabel(client.lng.extraCmdLineOptTxt, true); extraOptionsInp = p.addEditBox(); p.addLabel(client.lng.preSwitchCommandsTxt, true); commandsInp = p.addEditBox(); // Configure components. mapPrefixInp.setMaxLength(8); extraOptionsInp.setMaxLength(255); commandsInp.setMaxLength(255); resetButton.register(self); inclMutatorList.register(self); exclMutatorList.register(self); gameTypeList.register(self); mapPrefixInp.register(self); extraOptionsInp.register(self); rebootButton.register(self); loadGameTypeList(); loadMutatorList(); loadBootControlSettings(); previewBox.bTopCentric = true; inclMutatorList.bCanDrag = true; } /*************************************************************************************************** * * $DESCRIPTION Loads the game type list. * **************************************************************************************************/ function loadGameTypeList() { local int index; local string gameClass; local string mapPrefix; local string gameName; while (index < arrayCount(client.sConf.gameTypeInfo) && client.sConf.gameTypeInfo[index] != "") { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameClass, mapPrefix); class'NexgenUtil'.static.split(mapPrefix, mapPrefix, gameName); gameTypeList.addItem(gameName, string(index)); index++; } } /*************************************************************************************************** * * $DESCRIPTION Loads the mutator list. * **************************************************************************************************/ function loadMutatorList() { local int index; local NexgenSimpleListItem item; local string mutatorClass; local string mutatorName; while (index < arrayCount(client.sConf.mutatorInfo) && client.sConf.mutatorInfo[index] != "") { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[index], mutatorClass, mutatorName); item = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); item.displayText = mutatorName; item.itemID = index; index++; } exclMutatorList.items.sort(); } /*************************************************************************************************** * * $DESCRIPTION Loads the boot control settings. * **************************************************************************************************/ function loadBootControlSettings() { local NexgenSimpleListItem oldItem; local NexgenSimpleListItem newItem; local string remaining; local string mutatorIndex; local int index; // Select game type. index = client.sConf.getGameIndex(client.sConf.bootGameType); gameTypeList.setSelectedIndex(index); // Move all mutators to excluded list. for (oldItem = NexgenSimpleListItem(inclMutatorList.items); oldItem != none; oldItem = NexgenSimpleListItem(oldItem.next)) { if (oldItem.itemID >= 0) { newItem = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = oldItem.displayText; newItem.itemID = oldItem.itemID ; } } inclMutatorList.items.clear(); inclMutatorList.selectedItem = none; // Load included mutator list. if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; exclMutatorList.selectedItem = none; } remaining = client.sConf.bootMutatorIndices; while (remaining != "") { class'NexgenUtil'.static.split(remaining, mutatorIndex, remaining); index = int(mutatorIndex); oldItem = exclMutatorList.getItemByID(index); newItem = NexgenSimpleListItem(inclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = oldItem.displayText; newItem.itemID = oldItem.itemID; oldItem.remove(); } exclMutatorList.items.sort(); // Load other settings. mapPrefixInp.setValue(client.sConf.bootMapPrefix); extraOptionsInp.setValue(client.sConf.bootOptions); commandsInp.setValue(client.sConf.bootCommands); enableBootControlInp.bChecked = client.sConf.enableBootControl; restartOnLastGameInp.bChecked = client.sConf.restartOnLastGame; // Update preview. updatePreview(); } /*************************************************************************************************** * * $DESCRIPTION Updates the server boot command line preview. * **************************************************************************************************/ function updatePreview() { local string bootCmd; local string gameType; local string mutators; local string mutator; local string remaining; local int index; local NexgenSimpleListItem item; // Get game type. index = gameTypeList.getSelectedIndex(); if (index >= 0) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameType, remaining); } // Get mutators. for (item = NexgenSimpleListItem(inclMutatorList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID >= 0) { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[item.itemID], mutator, remaining); if (mutators == "") { mutators = mutator; } else { mutators = mutators $ separator $ " " $ mutator; } } } // Create boot command string. bootCmd = class'NexgenUtil'.static.trim(mapPrefixInp.getValue()) $ "-*.unr"; if (gameType != "") bootCmd = bootCmd $ " ?game=" $ gameType; if (mutators != "") bootCmd = bootCmd $ " ?mutator=" $ mutators; bootCmd = bootCmd $ " " $ class'NexgenUtil'.static.trim(extraOptionsInp.getValue()); // Set preview box contents. previewBox.clear(); previewBox.addText(bootCmd); } /*************************************************************************************************** * * $DESCRIPTION Automatically selects the map prefix for the selected game type. * **************************************************************************************************/ function updateMapPrefix() { local int index; local string gameClass; local string mapPrefix; local string remaining; // Get map prefix. index = gameTypeList.getSelectedIndex(); if (index >= 0) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[index], gameClass, remaining); class'NexgenUtil'.static.split(remaining, mapPrefix, remaining); } else { mapPrefix = ""; } // Set map prefix. mapPrefixInp.setValue(mapPrefix); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Sends the new boot control settings to the server. * $REQUIRE rpci != none * **************************************************************************************************/ function updateBootControl() { local string mutators; local NexgenSimpleListItem item; // Get mutators. for (item = NexgenSimpleListItem(inclMutatorList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.itemID >= 0) { if (mutators == "") { mutators = string(item.itemID); } else { mutators = mutators $ separator $ " " $ item.itemID; } } } // Send new settings to the server. rpci.updateBootControl(enableBootControlInp.bChecked, restartOnLastGameInp.bChecked, gameTypeList.getSelectedIndex(), mutators, class'NexgenUtil'.static.trim(mapPrefixInp.getValue()), class'NexgenUtil'.static.trim(extraOptionsInp.getValue()), class'NexgenUtil'.static.trim(commandsInp.getValue())); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local NexgenSimpleListItem newItem; super.notify(control, eventType); setRPCI(); // Reset button pressed? if (control == resetButton && eventType == DE_Click) { loadBootControlSettings(); } // Save button pressed? if (control == saveButton && eventType == DE_Click && rpci != none) { updateBootControl(); } // Reboot button pressed? if (control == rebootButton && eventType == DE_Click && rpci != none) { rpci.rebootServer(); } // Mutator selected? if (control == inclMutatorList && eventType == DE_Click) { if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; exclMutatorList.selectedItem = none; } } else if (control == exclMutatorList && eventType == DE_Click) { if (inclMutatorList.selectedItem != none) { inclMutatorList.selectedItem.bSelected = false; inclMutatorList.selectedItem = none; } } // Mutator double clicked? if (control == inclMutatorList && eventType == DE_DoubleClick && inclMutatorList.selectedItem != none) { newItem = NexgenSimpleListItem(exclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = NexgenSimpleListItem(inclMutatorList.selectedItem).displayText; newItem.itemID = NexgenSimpleListItem(inclMutatorList.selectedItem).itemID; if (exclMutatorList.selectedItem != none) { exclMutatorList.selectedItem.bSelected = false; } exclMutatorList.selectedItem = newItem; newItem.bSelected = true; inclMutatorList.selectedItem.remove(); inclMutatorList.selectedItem = none; exclMutatorList.sort(); updatePreview(); } else if (control == exclMutatorList && eventType == DE_DoubleClick && exclMutatorList.selectedItem != none) { newItem = NexgenSimpleListItem(inclMutatorList.items.append(class'NexgenSimpleListItem')); newItem.displayText = NexgenSimpleListItem(exclMutatorList.selectedItem).displayText; newItem.itemID = NexgenSimpleListItem(exclMutatorList.selectedItem).itemID; if (inclMutatorList.selectedItem != none) { inclMutatorList.selectedItem.bSelected = false; } inclMutatorList.selectedItem = newItem; newItem.bSelected = true; exclMutatorList.selectedItem.remove(); exclMutatorList.selectedItem = none; updatePreview(); } // Game type selected? if (control == gameTypeList && eventType == DE_Change) { updateMapPrefix(); //updatePreview(); } // Map prefix changed? if (control == mapPrefixInp && eventType == DE_Change) { updatePreview(); } // Extra options changed? if (control == extraOptionsInp && eventType == DE_Change) { updatePreview(); } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_BootControl) { loadBootControlSettings(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ |BhvRh vB "Ban period"g/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPBanControl * $VERSION 1.01 (2-11-2007 21:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen ban control panel page. * **************************************************************************************************/ class NexgenRCPBanControl extends NexgenPanel; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox banList; var NexgenSimpleListBox ipList; var NexgenSimpleListBox idList; var UWindowSmallButton addBanButton; var UWindowSmallButton updateBanButton; var UWindowSmallButton deleteBanButton; var UWindowEditControl playerNameInp; var UWindowEditControl banReasonInp; var UWindowCheckbox banPeriodInp[3]; var NexgenEditControl matchCountInp; var NexgenEditControl dateInp; var UWindowSmallButton addIPButton; var UWindowSmallButton delIPButton; var UWindowSmallButton addIDButton; var UWindowSmallButton delIDButton; var UWindowEditControl ipAddressInp; var UWindowEditControl clientIDInp; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int region; local int index; // Create layout & add components. createWindowRootRegion(); splitRegionV(160, defaultComponentDist); splitRegionH(72, defaultComponentDist, , true); // Ban entry editor. p = addContentPanel(); // Player name & ban reason. region = p.splitRegionH(40, defaultComponentDist) + 1; p.splitRegionV(96); p.skipRegion(); p.divideRegionH(2); p.divideRegionH(2); p.addLabel(client.lng.playerNameTxt, true); p.addLabel(client.lng.banReasonTxt, true); playerNameInp = p.addEditBox(); banReasonInp = p.addEditBox(); // Ban period. p.selectRegion(region); p.selectRegion(p.splitRegionH(64, defaultComponentDist)); region = p.currRegion + 1; p.splitRegionH(1); p.skipRegion(); p.addComponent(class'NexgenDummyComponent'); p.splitRegionV(96); p.divideRegionH(3); p.splitRegionV(96, defaultComponentDist); p.addLabel(client.lng.banPeriodTxt, true); p.skipRegion(); p.skipRegion(); p.divideRegionH(3); p.divideRegionH(3); banPeriodInp[0] = p.addCheckBox(TA_Left, client.lng.banForeverTxt); banPeriodInp[1] = p.addCheckBox(TA_Left, client.lng.banMatchesTxt); banPeriodInp[2] = p.addCheckBox(TA_Left, client.lng.banUntilDateTxt); p.skipRegion(); matchCountInp = p.addEditBox(); dateInp = p.addEditBox(); // Banned IP's and ID's. p.selectRegion(region); p.selectRegion(p.splitRegionH(1, defaultComponentDist)); p.addComponent(class'NexgenDummyComponent'); p.divideRegionH(2, defaultComponentDist); p.splitRegionV(224, defaultComponentDist, , true); p.splitRegionV(224, defaultComponentDist, , true); p.splitRegionH(56); ipList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.splitRegionH(56); idList = NexgenSimpleListBox(p.addListBox(class'NexgenSimpleListBox')); p.divideRegionH(3); p.skipRegion(); p.divideRegionH(3); p.skipRegion(); p.addLabel(client.lng.ipAddressesTxt, true); p.divideRegionV(2, defaultComponentDist); ipAddressInp = p.addEditBox(); p.addLabel(client.lng.clientIDsTxt, true); p.divideRegionV(2, defaultComponentDist); clientIDInp = p.addEditBox(); addIPButton = p.addButton(client.lng.addTxt); delIPButton = p.addButton(client.lng.removeTxt); addIDButton = p.addButton(client.lng.addTxt); delIDButton = p.addButton(client.lng.removeTxt); // Ban list. banList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Ban list editor. p = addContentPanel(); p.divideRegionH(3); addBanButton = p.addButton(client.lng.addBanTxt); updateBanButton = p.addButton(client.lng.updateBanTxt); deleteBanButton = p.addButton(client.lng.delBanTxt); // Configure components. ipAddressInp.setMaxLength(15); clientIDInp.setMaxLength(32); playerNameInp.setMaxLength(32); banReasonInp.setMaxLength(255); matchCountInp.setMaxLength(3); matchCountInp.setNumericOnly(true); dateInp.setMaxLength(24); banList.register(self); ipList.register(self); idList.register(self); addIPButton.register(self); delIPButton.register(self); addIDButton.register(self); delIDButton.register(self); addBanButton.register(self); updateBanButton.register(self); deleteBanButton.register(self); for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].register(self); } loadBanList(); banPeriodInp[0].bChecked = true; banPeriodTypeSelected(); delIPButton.bDisabled = true; delIDButton.bDisabled = true; dateInp.setValue(client.lng.dateFormatStr); } /*************************************************************************************************** * * $DESCRIPTION Load the banlist. * **************************************************************************************************/ function loadBanList() { local int index; local NexgenSimpleListItem item; local int numBans; // Clear list. banList.items.clear(); banList.selectedItem = none; // Add bans. while(index < arrayCount(client.sConf.bannedName) && client.sConf.bannedName[index] != "") { item = NexgenSimpleListItem(banList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.bannedName[index]; item.itemID = index; index++; } // Configure components. numBans = index; addBanButton.bDisabled = numBans >= arrayCount(client.sConf.bannedName); banSelected(); } /*************************************************************************************************** * * $DESCRIPTION Called when a ban entry has been selected. Loads the info for the selected ban * entry & configures the components. * **************************************************************************************************/ function banSelected() { // Item selected? if (banList.selectedItem == none) { // No. updateBanButton.bDisabled = true; deleteBanButton.bDisabled = true; } else { // Yes. updateBanButton.bDisabled = false; deleteBanButton.bDisabled = false; loadBanInfo(NexgenSimpleListItem(banList.selectedItem).itemID); } } /*************************************************************************************************** * * $DESCRIPTION Loads the information for the specified ban entry. * $PARAM entryNum The entry number in the ban list. * $REQUIRE 0 <= entryNum && entryNum < arrayCount(client.sConf.bannedName) && * client.sConf.bannedName[entryNum] != "" * **************************************************************************************************/ function loadBanInfo(int entryNum) { local string remaining; local string part; local NexgenSimpleListItem item; local byte banPeriodType; local string banArgs; local int index; // Player name & ban reason. playerNameInp.setValue(client.sConf.bannedName[entryNum]); banReasonInp.setValue(client.sConf.banReason[entryNum]); // Ban period. client.sConf.getBanPeriodType(client.sConf.banPeriod[entryNum], banPeriodType, banArgs); for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].bChecked = index == banPeriodType; } if (banPeriodType == client.sConf.BP_Matches) { matchCountInp.setDisabled(false); matchCountInp.setValue(banArgs); dateInp.setDisabled(true); dateInp.setValue(""); } else if (banPeriodType == client.sConf.BP_UntilDate) { matchCountInp.setDisabled(true); matchCountInp.setValue(""); dateInp.setDisabled(false); dateInp.setValue(client.lng.getLocalizedDateStr(banArgs)); } else { matchCountInp.setDisabled(true); matchCountInp.setValue(""); dateInp.setDisabled(true); dateInp.setValue(""); } // Load ip addresses. ipList.items.clear(); ipList.selectedItem = none; remaining = client.sConf.bannedIPs[entryNum]; while (remaining != "") { // Split head element from tail. index = instr(remaining, separator); if (index < 0) { part = remaining; remaining = ""; } else { part = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Add element to list. item = NexgenSimpleListItem(ipList.items.append(class'NexgenSimpleListItem')); item.displayText = part; } addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; delIPButton.bDisabled = true; // Load client id's. idList.items.clear(); idList.selectedItem = none; remaining = client.sConf.bannedIDs[entryNum]; while (remaining != "") { // Split head element from tail. index = instr(remaining, separator); if (index < 0) { part = remaining; remaining = ""; } else { part = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Add element to list. item = NexgenSimpleListItem(idList.items.append(class'NexgenSimpleListItem')); item.displayText = part; } addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; delIDButton.bDisabled = true; } /*************************************************************************************************** * * $DESCRIPTION Called when a ban period type was selected. * **************************************************************************************************/ function banPeriodTypeSelected() { matchCountInp.setDisabled(!banPeriodInp[client.sConf.BP_Matches].bChecked); dateInp.setDisabled(!banPeriodInp[client.sConf.BP_UntilDate].bChecked); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_BanList) { loadBanList(); } } /*************************************************************************************************** * * $DESCRIPTION Assembles the current ban period string. * $RETURN The ban period string for the currently selected settings. * **************************************************************************************************/ function string getCurrentBanPeriod() { if (banPeriodInp[client.sConf.BP_Matches].bChecked) { return "M" $ matchCountInp.getValue(); } else if (banPeriodInp[client.sConf.BP_UntilDate].bChecked) { return "U" $ client.lng.getDelocalizedDateStr(dateInp.getValue()); } else { return ""; } } /*************************************************************************************************** * * $DESCRIPTION Returns the ip addresses entered in the ip list. * $RETURN A string containing all ip addresses entered in the ip list. * **************************************************************************************************/ function string getIPList() { local NexgenSimpleListItem item; local string list; // Assemble list. for (item = NexgenSimpleListItem(ipList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.displayText != "") { if (list == "") { list = item.displayText; } else { list = list $ separator $ item.displayText; } } } // Return the list. return list; } /*************************************************************************************************** * * $DESCRIPTION Returns the client id's entered in the id list. * $RETURN A string containing all client id's entered in the id list. * **************************************************************************************************/ function string getIDList() { local NexgenSimpleListItem item; local string list; // Assemble list. for (item = NexgenSimpleListItem(idList.items); item != none; item = NexgenSimpleListItem(item.next)) { if (item.displayText != "") { if (list == "") { list = item.displayText; } else { list = list $ separator $ item.displayText; } } } // Return the list. return list; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local int index; local int selectedIndex; local string value; local NexgenSimpleListItem item; local string playerName; super.notify(control, eventType); setRPCI(); // Add ban entry button clicked? if (control == addBanButton && eventType == DE_Click && !addBanButton.bDisabled) { playerName = class'NexgenUtil'.static.trim(playerNameInp.getValue()); if (playerName != "") { rpci.addBan(playerName, getIPList(), getIDList(), class'NexgenUtil'.static.trim(banReasonInp.getValue()), getCurrentBanPeriod()); } } // Update ban entry button clicked? if (control == updateBanButton && eventType == DE_Click && !updateBanButton.bDisabled) { playerName = class'NexgenUtil'.static.trim(playerNameInp.getValue()); if (playerName != "") { rpci.updateBan(NexgenSimpleListItem(banList.selectedItem).itemID, playerName, getIPList(), getIDList(), class'NexgenUtil'.static.trim(banReasonInp.getValue()), getCurrentBanPeriod()); } } // Delete ban entry button clicked? if (control == deleteBanButton && eventType == DE_Click && !deleteBanButton.bDisabled) { rpci.deleteBan(NexgenSimpleListItem(banList.selectedItem).itemID); } // Ban entry selected? if (control == banList && eventType == DE_Click) { banSelected(); } // IP address selected? if (control == ipList && eventType == DE_Click) { delIPButton.bDisabled = ipList.selectedItem == none; if (ipList.selectedItem != none) { ipAddressInp.setValue(NexgenSimpleListItem(ipList.selectedItem).displayText); } } // Client ID selected? if (control == idList && eventType == DE_Click) { delIDButton.bDisabled = idList.selectedItem == none; if (idList.selectedItem != none) { clientIDInp.setValue(NexgenSimpleListItem(idList.selectedItem).displayText); } } // Add IP address pressed? if (control == addIPButton && eventType == DE_Click && !addIPButton.bDisabled) { value = class'NexgenUtil'.static.trim(ipAddressInp.getValue()); if (class'NexgenUtil'.static.isValidIPAddress(value)) { item = NexgenSimpleListItem(ipList.items.append(class'NexgenSimpleListItem')); item.displayText = value; addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; } } // Del IP address pressed? if (control == delIPButton && eventType == DE_Click && !delIPButton.bDisabled) { ipList.selectedItem.remove(); ipList.selectedItem = none; delIPButton.bDisabled = true; addIPButton.bDisabled = ipList.items.countShown() >= client.sConf.maxBanIPAddresses; } // Del client ID pressed? if (control == addIDButton && eventType == DE_Click && !addIDButton.bDisabled) { value = class'NexgenUtil'.static.trim(clientIDInp.getValue()); if (class'NexgenUtil'.static.isValidClientID(value)) { item = NexgenSimpleListItem(idList.items.append(class'NexgenSimpleListItem')); item.displayText = value; addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; } } // Del client ID pressed? if (control == delIDButton && eventType == DE_Click && !delIDButton.bDisabled) { idList.selectedItem.remove(); idList.selectedItem = none; delIDButton.bDisabled = true; addIDButton.bDisabled = idList.items.countShown() >= client.sConf.maxBanClientIDs; } // Ban period type selected? if (eventType == DE_Click && control.isA('UWindowCheckbox')) { // Find selected type. selectedIndex = -1; while (selectedIndex < 0 && index < arrayCount(banPeriodInp)) { if (control == banPeriodInp[index]) { selectedIndex = index; } else { index++; } } // Has a period been selected? if (selectedIndex >= 0) { // Yes, update components. for (index = 0; index < arrayCount(banPeriodInp); index++) { banPeriodInp[index].bChecked = index == selectedIndex; } banPeriodTypeSelected(); } } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ xB "Ban/kick reason"}/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPAccountTypes * $VERSION 1.01 (20-10-2007 10:50) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen account type manager control panel page. * **************************************************************************************************/ class NexgenRCPAccountTypes extends NexgenPanel; var int numAccountTypes; var NexgenClientCore rpci; // Remote Procedure Call interface. var NexgenSimpleListBox accountTypeList; var UWindowSmallButton addAccountTypeButton; var UWindowSmallButton deleteAccountTypeButton; var UWindowSmallButton moveUpButton; var UWindowSmallButton moveDownButton; var UWindowEditControl accountNameInp; var UWindowEditControl accountTitleInp; var UWindowEditControl accountPasswordInp; var UWindowCheckbox rightEnableInp[18]; var UWindowSmallButton resetButton; var UWindowSmallButton saveButton; /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local NexgenContentPanel p; local int index; local string rightDef; // Create layout & add components. createWindowRootRegion(); splitRegionV(192, defaultComponentDist); splitRegionH(96, defaultComponentDist, , true); // Account info panel. p = addContentPanel(); p.splitRegionH(64); p.splitRegionV(96); p.splitRegionH(16, , , true); p.divideRegionH(3); p.divideRegionH(3); p.divideRegionV(2, defaultComponentDist); p.splitRegionV(182); p.addLabel(client.lng.accountNameTxt, true); p.addLabel(client.lng.accountTitleTxt, true); p.addLabel(client.lng.passwordTxt, true); accountNameInp = p.addEditBox(); accountTitleInp = p.addEditBox(); accountPasswordInp = p.addEditBox(); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionH(arraycount(rightEnableInp) / 2); p.divideRegionV(2, defaultComponentDist); p.skipRegion(); for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { rightEnableInp[index] = p.addCheckBox(TA_Left, client.lng.format(client.lng.rightNotDefinedTxt, string(index + 1))); rightEnableInp[index].bDisabled = true; } else { rightEnableInp[index] = p.addCheckBox(TA_Left, mid(rightDef, instr(rightDef, client.sConf.separator) + 1)); } } saveButton = p.addButton(client.lng.saveTxt); resetButton = p.addButton(client.lng.resetTxt); // Account type list. accountTypeList = NexgenSimpleListBox(addListBox(class'NexgenSimpleListBox')); // Account type controls. p = addContentPanel(); p.divideRegionH(4); addAccountTypeButton = p.addButton(client.lng.addAccountTypeTxt); deleteAccountTypeButton = p.addButton(client.lng.delAccountTypeTxt); moveUpButton = p.addButton(client.lng.moveUpTxt); moveDownButton = p.addButton(client.lng.moveDownTxt); // Configure components. accountNameInp.setMaxLength(24); accountTitleInp.setMaxLength(24); accountPasswordInp.setMaxLength(32); accountTypeList.register(self); addAccountTypeButton.register(self); deleteAccountTypeButton.register(self); moveUpButton.register(self); moveDownButton.register(self); resetButton.register(self); saveButton.register(self); loadAccountTypes(); accountTypeSelected(); } /*************************************************************************************************** * * $DESCRIPTION Loads the account types. * **************************************************************************************************/ function loadAccountTypes() { local int index; local NexgenSimpleListItem item; accountTypeList.items.clear(); accountTypeList.selectedItem = none; while(index < arrayCount(client.sConf.atTypeName) && client.sConf.atTypeName[index] != "") { item = NexgenSimpleListItem(accountTypeList.items.append(class'NexgenSimpleListItem')); item.displayText = client.sConf.atTypeName[index]; item.itemID = index; index++; } numAccountTypes = index; } /*************************************************************************************************** * * $DESCRIPTION Loads the settings for the specified account type. * $PARAM accountTypeNum The ID number of the account type to load. * $REQUIRE 0 <= accountTypeNum && accountTypeNum <= arrayCount(client.sConf.atTypeName) * **************************************************************************************************/ function loadAccountTypeInfo(int accountTypeNum) { local int index; local string accountRights; local string rightDef; local string rightID; // Cancel on error. In theory this should not happen. if (client.sConf.atTypeName[accountTypeNum] == "") { return; } // Load general info. accountNameInp.setValue(client.sConf.atTypeName[accountTypeNum]); accountTitleInp.setValue(client.sConf.atTitle[accountTypeNum]); accountPasswordInp.setValue(client.sConf.decode(client.sConf.atPassword[accountTypeNum])); // Load right assignment. accountRights = client.sConf.atRights[accountTypeNum]; for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightDef == "") { // Right isn't defined. rightEnableInp[index].bChecked = false; } else { rightID = left(rightDef, instr(rightDef, separator)); rightEnableInp[index].bChecked = hasRight(accountRights, rightID); } } } /*************************************************************************************************** * * $DESCRIPTION Called when an account type was selected from the list. * **************************************************************************************************/ function accountTypeSelected() { local NexgenSimpleListItem selected; // Check which buttons should be enabled / disabled. if (accountTypeList.selectedItem == none) { // No item selected. deleteAccountTypeButton.bDisabled = true; moveUpButton.bDisabled = true; moveDownButton.bDisabled = true; resetButton.bDisabled = true; saveButton.bDisabled = true; } else { // Other account type selected. selected = NexgenSimpleListItem(accountTypeList.selectedItem); deleteAccountTypeButton.bDisabled = selected.itemID < 1; moveUpButton.bDisabled = selected.itemID < 2; moveDownButton.bDisabled = selected.itemID < 1 || selected.itemID + 1 == numAccountTypes; resetButton.bDisabled = false; saveButton.bDisabled = false; } // Load account info. if (accountTypeList.selectedItem != none) { loadAccountTypeInfo(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified right is included in the given right string. * $PARAM rights The rights specifier string. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the right is included, false if not. * **************************************************************************************************/ function bool hasRight(string rights, string rightID) { return instr(rights $ separator, rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Returns a string containing the currently selected rights. * $RETURN A string containing the rights currently selected. * **************************************************************************************************/ function string getCurrentRights() { local string rights; local string rightDef; local int index; // Check for each right if it is selected. for (index = 0; index < arraycount(rightEnableInp); index++) { rightDef = client.sConf.rightsDef[index]; if (rightEnableInp[index].bChecked && rightDef != "") { if (rights == "") { rights = left(rightDef, instr(rightDef, separator)); } else { rights = rights $ separator $ left(rightDef, instr(rightDef, separator)); } } } // Return result. return rights; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); setRPCI(); // Account type selected. if (control == accountTypeList && eventType == DE_Click) { accountTypeSelected(); } // Reset account type info. if (control == resetButton && !resetButton.bDisabled && eventType == DE_Click) { loadAccountTypeInfo(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } // Save account type info. if (control == saveButton && !saveButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.updateAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, accountNameInp.getValue(), getCurrentRights(), accountTitleInp.getValue(), accountPasswordInp.getValue()); } // Add account type. if (control == addAccountTypeButton && !addAccountTypeButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.addAccountType(accountNameInp.getValue(), getCurrentRights(), accountTitleInp.getValue(), accountPasswordInp.getValue()); } // Delete account type. if (control == deleteAccountTypeButton && !deleteAccountTypeButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.deleteAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID); } // Move account type up. if (control == moveUpButton && !moveUpButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.moveAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, false); } // Move account type down. if (control == moveDownButton && !moveDownButton.bDisabled && eventType == DE_Click && rpci != none) { rpci.moveAccountType(NexgenSimpleListItem(accountTypeList.selectedItem).itemID, true); } } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the RPC interface for this control panel. * $REQUIRE client != none * $RETURN True if the RPC interface has been set, false if not. * $ENSURE result == true ? rcpi != none : true * **************************************************************************************************/ function bool setRPCI() { // Check if RPC interface is already set. if (rpci == none) { // Attempt to get the RPC interface. rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); return rpci != none; } else { // It is. return true; } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { // Relevant settings for this panel? if (configType == client.sConf.CT_AccountTypes) { loadAccountTypes(); accountTypeSelected(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ yB "Player name"zB "Tournament mode"{B "Lock the game"~B "Allow team balancing"BJ$IWq-z(-J$0rf*frv*va Uvfva/!Z.a v|.fNwv*-z'vfa/!r .f*wf*f K"/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenRCPAbout * $VERSION 1.00 (10-3-2007 21:20) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen about control panel page. * **************************************************************************************************/ class NexgenRCPAbout extends NexgenPanel; #exec TEXTURE IMPORT NAME=logo FILE=Resources\logo.pcx GROUP="GFX" FLAGS=1 MIPS=Off /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { // Create layout & add components. createWindowRootRegion(); divideRegionV(2); addImageBox(Texture'logo'); divideRegionH(17); skipRegion(); addLabel("Nexgen Server Controller", true, TA_Center); addLabel("version" @ left(class'NexgenUtil'.default.version, 4) @ "build" @ class'NexgenUtil'.default.internalVersion, , TA_Center); addLabel("Copyright 2006-2008 Zeropoint productions", , TA_Center); addLabel("d.scheerens@gmail.com", , TA_Center); skipRegion(); addLabel("Development", true, TA_Center); addLabel("Daan \"Defrost\" Scheerens", , TA_Center); skipRegion(); addLabel("Credits and thanks to", true, TA_Center); addLabel("Mickal \"ATHoS\" DEHEZ", , TA_Center); addLabel("Matthew \"MSuLL\" Sullivan", , TA_Center); addLabel("David \"The_Dave\" Schwartzstein", , TA_Center); addLabel("Zohar \"SuB\" Zada", , TA_Center); addLabel("*TNT*CryptKeeper", , TA_Center); addLabel("[BOSS]Snipes", , TA_Center); skipRegion(); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ BC "Allow team switching"NCw `yalfNa :Z|22UseNexgenHUDtruetruew U*I'Na?@'w %w ,ww I*w Ihw wMJ22RunCount0&2ERunCountSM2b^M, $ ?Welcome to Nexgen, type !open to open the control panel.h HN/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPrivateMsgDialog * $VERSION 1.00 (25-12-2006 17:06) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player is banned from the server. * **************************************************************************************************/ class NexgenPrivateMsgDialog extends NexgenPopupDialog; var UMenuLabelControl msgLabel[5]; // Message content label components. var UMenuLabelControl senderLabel; // Message sender label component. var UWindowSmallButton replyButton; // Reply button component. var localized string caption; // Caption to display on the dialog. var localized string senderText; // Message sender label text. var localized string messageText; // Received message label text. var localized string replyButtonText; // Received message label text. const firstLineWrapLen = 60; // Wrap lenght at the first message line. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); senderLabel = addPropertyLabel(cy, senderText, 64.0); msgLabel[0] = addPropertyLabel(cy, messageText, 64.0); msgLabel[1] = addLabel(cy); msgLabel[2] = addLabel(cy); msgLabel[3] = addLabel(cy); msgLabel[4] = addLabel(cy); replyButton = addButton(replyButtonText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM message The message that was received. * $PARAM sender Name of the player that has send the message. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string message, optional string sender, optional string str3, optional string str4) { local int lineNum; local int index; local string remaining; local string lineStr; local int wrapLen; local int wrapPos; local string lineEntry; // Set sender label. senderLabel.setText(sender); // Set message labels. remaining = message $ newlineToken; while (remaining != "" && lineNum < arrayCount(msgLabel)) { // Split at new line tokens. index = instr(remaining, newlineToken); lineStr = left(remaining, index); remaining = mid(remaining, index + len(newlineToken)); // Split at wrap length. do { // Get wrap position. if (lineNum == 0) { wrapLen = firstLineWrapLen; } else { wrapLen = wrapLength; } wrapPos = getWrapPosition(lineStr, wrapLen); // Split line. if (wrapPos < 0) { lineEntry = lineStr; lineStr = ""; } else { lineEntry = left(lineStr, wrapPos); lineStr = mid(lineStr, wrapPos); } msgLabel[lineNum++].setText(class'NexgenUtil'.static.trim(lineEntry)); } until (lineStr == "" || lineNum >= arrayCount(msgLabel)); } // Clean empty lines. for (index = lineNum; index < arrayCount(msgLabel); index++) { msgLabel[index].setText(""); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reply button. if (control == replyButton && eventType == DE_Click) { client.showPanel(class'NexgenRCPPrivateMsg'.default.panelIdentifier); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ C@C|@[ 2 AC;Cn9ˉi'r᱘᱘ 3B᱘ V]*)You have received a new private message.M]From:L] Message:K]Replye"OEC "Restart game"y2/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPopupFrame * $VERSION 1.03 (8-3-2008 16:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen GUI popup window class. An instance of this class defines the frame for a * popup window. * **************************************************************************************************/ class NexgenPopupFrame extends UMenuFramedWindow; var float windowWidth; // Width of the popup window frame (in pixels). var float windowHeight; // Height of the popup window frame (in pixels). var NexgenClient client; // Nexgen client instance. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var string serverID; // Identification code of the server where has been connected to. /*************************************************************************************************** * * $DESCRIPTION Makes sure the popup frame will be properly setup. * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); windowTitle = "Nexgen Server Controller v" $ left(class'NexgenUtil'.default.version, 4); bLeaveOnScreen = true; bMoving = true; } /*************************************************************************************************** * * $DESCRIPTION Changes the contents of the popup window. * $PARAM popupClass The dialog class used for the contents of the popup frame. * $PARAM str1 Dialog specific content data. * $PARAM str2 Dialog specific content data. * $PARAM str3 Dialog specific content data. * $PARAM str4 Dialog specific content data. * $REQUIRE the specified popup class exists and is a subclass of NexgenPopupDialog * **************************************************************************************************/ function showPopup(string popupClass, optional string str1, optional string str2, optional string str3, optional string str4) { local NexgenPopupDialog dialog; local Class dialogClass; // Get the object class. if (instr(popupClass, ".") < 0) { popupClass = class'NexgenUtil'.default.packageName $ "." $ popupClass; } dialogClass = class(DynamicLoadObject(popupClass, class'Class')); // Set popup contents. dialog = NexgenPopupDialog(createWindow(dialogClass, 4, 16, winWidth - 4, winHeight - 16)); dialog.gc = gc; dialog.sc = sc; dialog.serverID = serverID; dialog.client = client; dialog.setContent(str1, str2, str3, str4); clientArea = dialog; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ W/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPopupDialog * $VERSION 1.06 (21-10-2007 15:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Parent class for all popup dialogs. * **************************************************************************************************/ class NexgenPopupDialog extends UWindowDialogClientWindow; var NexgenClient client; // Nexgen client instance. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var string serverID; // Identification code of the server. var bool hasCloseButton; // Automatically add a close button to the dialog? var UWindowSmallButton closeButton; // Default close button. var int wrapLength; // Insert a new line after this many characters. // NOTE: This value is only an estimation for word wrapping. const minWrapRetain = 0.60; // Minimum size of the text before wrapping inside words // will occur. const wrapChars = " -,."; // Preferred characters where wrapping should occur. var bool autoCloseControlPanel; // Automatically close the control panel once the dialog is // shown? // Component positioning. All values are measured in pixels. var float nextButtonPos; // Next horizontal position of a button that is to be added. var float borderSize; // Distance between objects on the dialog and its borders. var float labelHeight; // Height of label objects. var float editControlHeight; // Height of edit control objects. var float editControlLabelVOffset; // Vertical offset of labels relative to their edit control. var float buttonPanelBorderSize; // Distance between the dialog borders and the button panel. var float buttonPanelHeight; // The height of the button panel. var float buttonHeight; // Height of buttons on this dialog. var float buttonWidth; // Default width of a button on this dialog. var float buttonSpace; // Space between two buttons. const newLineToken = "\\n"; // Token used to detect new lines in texts. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE hasCloseButton ? closeButton != none : true * $OVERRIDE * **************************************************************************************************/ function created() { local UMenuLabelControl label; local UWindowRootWindow rootWin; local UWindowWindow win; super.created(); nextButtonPos = winWidth - buttonSpace - buttonPanelBorderSize; // Automatically add close button? if (hasCloseButton) { closeButton = addButton("Close"); } // Automatically close control panel? if (autoCloseControlPanel) { // Yes, iterate over each window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (win != none) { // Window is a control panel? if (win.isA('NexgenMainFrame')) { // Yes, close it. win.close(); } // Continue with next window. win = win.nextSiblingWindow; } } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * This function will only check if the close button has been clicked. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); if (control == closeButton && eventType == DE_Click) { close(); } } /*************************************************************************************************** * * $DESCRIPTION Adds a new button to the dialog. The button will be added to the button panel of * the dialog and will be automatically positioned. * $PARAM text Text to display on the button. * $PARAM width Width of the button in pixels. * $RETURN The button that has been added to the button panel of this dialog. * $ENSURE result != none * **************************************************************************************************/ function UWindowSmallButton addButton(string text, optional int width) { local float cx, cy, cw, ch; local UWindowSmallButton newButton; if (width > 0.0) { cw = width; } else { cw = buttonWidth; } ch = buttonHeight; cx = nextButtonPos - cw; cy = winHeight - buttonPanelHeight - buttonPanelBorderSize + (buttonPanelHeight - ch) / 2.0 - 3; newButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); newButton.setText(text); nextButtonPos -= cw + buttonSpace; return newButton; } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the dialog. * $PARAM yPos Vertical position on the dialog where the text will be added. * $REQUIRE yPos >= 0 * $RETURN The label that has been added to the dialog. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addLabel(out float yPos) { local float cx, cy, cw, ch; // Initialze position & dimensions. cx = borderSize; cy = yPos; cw = winWidth - 2.0 * borderSize; ch = labelHeight; yPos += ch; // Create label. return UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the dialog with a property description label in * front of the label. * $PARAM yPos Vertical position on the dialog where the text will be added. * $PARAM text Property name / description. * $PARAM labelWidth Width of the property name label (in pixels). * $REQUIRE yPos >= 0 && labelWidth > 0 * $RETURN The label that has been added to the dialog. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addPropertyLabel(out float yPos, string text, float labelWidth) { local float cx, cy, cw, ch; local UMenuLabelControl label; cx = borderSize; cy = yPos; cw = labelWidth; ch = labelHeight; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(text); label.setFont(F_Bold); cx += labelWidth; cw = winWidth - 2.0 * borderSize - labelWidth; yPos += ch; return UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); } /*************************************************************************************************** * * $DESCRIPTION Adds a text to the dialog. This function detects new line tokens and writes the * after the token on a new line. Note that it doesn't support word wrapping. The * fontType and align properties will be maintained for all lines written. * $PARAM text The text to write to the dialog. * $PARAM yPos Vertical position on the dialog where the text will be added. * $PARAM fontType Type of font used to display the text. * $PARAM align Horizontal alignment of the text to add. * $PARAM wrapLen Maximum characters on a line. For word wrapping. * $REQUIRE yPos >= 0 && (fontType == F_Normal || fontType == F_Bold) && * (align == TA_Left || align == TA_Center || align == TA_Right) * **************************************************************************************************/ function addText(string text, out float yPos, int fontType, TextAlign align, optional int wrapLen) { local float cx, cy, cw, ch; local UMenuLabelControl label; local string textStr; local string lineStr; local int newLinePos; local string wrapStr; local int wrapPos; // Set proper wrapping length. if (wrapLen <= 0) { wrapLen = wrapLength; } // Initialze position & dimensions. cx = borderSize; cy = yPos; cw = winWidth - 2.0 * borderSize; ch = labelHeight; // Create a label for each line in the text string. textStr = text; while (textStr != "") { // Get text for the current line. newLinePos = instr(textStr, newLineToken); if (newLinePos < 0) { lineStr = textStr; textStr = ""; } else { lineStr = left(textStr, newLinePos); textStr = mid(textStr, newLinePos + len(newLineToken)); } // Word wrapping for the current line. while (lineStr != "") { wrapPos = getWrapPosition(lineStr, wrapLen); // Wrap current line. if (wrapPos < 0) { wrapStr = lineStr; lineStr = ""; } else { wrapStr = left(lineStr, wrapPos + 1); lineStr = mid(lineStr, wrapPos + 1); } // Create label. label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(wrapStr); label.setFont(fontType); label.align = align; // Update vertical offset. cy += ch; } } // Update vertical offset. yPos = cy; } /*************************************************************************************************** * * $DESCRIPTION Gets the first position in the given string where a new line should be inserted * for word wrapping. * $PARAM text The string for which the first wrapping position is to be determined. * $PARAM maxChars Maximum characters allowed on a line. * $REQUIRE maxChars > 0 * $RETURN The first position in the specified string where word wrapping should occur, or * -1 if no wrapping is necessary. * $ENSURE result >= 0 && result < len(text) && result < maxChars || result == -1 * **************************************************************************************************/ function int getWrapPosition(string text, int maxChars) { local int index; // Find wrap position. if (len(text) < maxChars) { // No wrapping necessary. index = -1; } else { // Start at the back. index = maxChars - 1; while ((instr(wrapChars, mid(text, index, 1)) < 0) && (index >= minWrapRetain * maxChars)) { index--; } // Wrap at least after 1 character. index = max(1, index); } return index; } /*************************************************************************************************** * * $DESCRIPTION Adds an empty line in this dialog. No control object is created, only the value of * yPos will be increased. * $PARAM yPos Vertical position of the new line on this dialog. * $ENSURE new.yPos >= old.yPos * **************************************************************************************************/ function addNewLine(out float yPos) { yPos += labelHeight; } /*************************************************************************************************** * * $DESCRIPTION Adds a new edit control object to this dialog. * $PARAM yPos Vertical position of the edit control on this dialog. * $PARAM labelText Label to display before the edit control. * $PARAM labelWidth Width of the label to display * $REQUIRE yPos >= 0 && (labelText != "" ? labelWidth > 0 : true) * $RETURN The edit control object created for this dialog. * $ENSURE result != none * **************************************************************************************************/ function UWindowEditControl addEditControl(out float yPos, optional string labelText, optional float labelWidth) { local float cx, cy, cw, ch; local UMenuLabelControl label; local UWindowEditControl editControl; // Set control position & dimension. ch = editControlHeight; cx = borderSize; cy = yPos; if (labelText != "") { // Add a label before the edit control. cw = labelWidth; cy += editControlLabelVOffset; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(labelText); label.setFont(F_Bold); cx += labelWidth; cy = yPos; cw = winWidth - 2.0 * borderSize - labelWidth; } else { cw = winWidth - 2.0 * borderSize; } yPos = cy + ch; // Create & setup the edit control. editControl = UWindowEditControl(createControl(class'UWindowEditControl', cx, cy, cw, ch)); editControl.editBoxWidth = cw; editControl.setMaxLength(250); // Return the control. return editControl; } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ local float cx, cy, cw, ch; super.paint(c, x, y); // Draw the button panel. cx = buttonPanelBorderSize; cy = winHeight - buttonPanelHeight - buttonPanelBorderSize; cw = winWidth - buttonPanelBorderSize * 2; ch = buttonPanelHeight; drawUpBevel(c, cx, cy, cw, ch, getLookAndFeelTexture()); } /*************************************************************************************************** * * $DESCRIPTION Closes the dialog. * $PARAM bByParent The close call was issued by the parent of the dialog. * $OVERRIDE * **************************************************************************************************/ function close(optional bool bByParent) { local UWindowRootWindow rootWin; local UWindowWindow win; local bool bWindowVisible; // Check if there is another visible window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (!bWindowVisible && win != none) { // Current window visible? bWindowVisible = win != parentWindow && win.windowIsVisible() && win.bLeaveOnscreen; // Continue with next window. win = win.nextSiblingWindow; } } // Close the window. if (!bWindowVisible) { WindowConsole(getPlayerOwner().player.console).closeUWindow(); } super.close(bByParent); } /*************************************************************************************************** * * $DESCRIPTION Parses a boolean string value. * $PARAM str The string representation of a boolean value. * $RETURN True if the string equals "true" (case insensitive), false otherwise. * **************************************************************************************************/ function bool parseBool(string str) { return str ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM str1 Dialog specific content data. * $PARAM str2 Dialog specific content data. * $PARAM str3 Dialog specific content data. * $PARAM str4 Dialog specific content data. * $ABSTRACT * **************************************************************************************************/ function setContent(optional string str1, optional string str2, optional string str3, optional string str4); /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ HC "End game"A/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlugin * $VERSION 1.16 (10-8-2008 11:05) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen controller plugin. This is the base class used for nexgen plugins. The * plugin is being setup from here. An instance of this class also receives events * emitted by the Nexgen Server Controller. * **************************************************************************************************/ class NexgenPlugin extends Info abstract; var NexgenController control; // The Nexgen Server Controller instance. var String pluginName; // Name of the plugin. var String pluginAuthor; // Who developed the plugin. var String pluginVersion; // Plugin version description. /*************************************************************************************************** * * $DESCRIPTION Starts up the plugin. First the Nexgen controller is located. Once it has been * found initialize() is called. If the initialization was successfull the plugin * will be registered at the server controller. If any error occurs during the load * process the plugin will be destroyed. * **************************************************************************************************/ function preBeginPlay() { // Locate Nexgen Controller. foreach allActors(class'NexgenController', control) { if (control != none) { break; } } // Quit on error. if (control == none) { warn("CRITICAL EXCEPTION, Nexgen controller not detected."); destroy(); return; } // Don't load on special mode. if (control.bSpecialMode) { destroy(); return; } // Initialize plugin. control.nscLog(control.lng.format(control.lng.loadingPluginMsg, pluginName, pluginVersion)); if (initialize()) { // Register plugin. if (!control.registerPlugin(self)) { // Failed to register, destroy instance. control.nscLog(control.lng.format(control.lng.regFailedMsg, string(self))); destroy(); } } else { // Initialization failed, destroy instance. control.nscLog(control.lng.format(control.lng.initFailedMsg, string(self))); destroy(); } // Make sure the checksums are up to date. control.sConf.updateDynamicChecksums(); control.sConf.staticChecksum = control.sConf.calcStaticChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Initializes the plugin. Note that if this function returns false the plugin will * be destroyed and is not to be used anywhere. * $RETURN True if the initialization succeeded, false if it failed. * **************************************************************************************************/ function bool initialize() { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called when a new client has been created. Use this function to setup the new * client with your own extensions (in order to support the plugin). * $PARAM client The client that was just created. * $REQUIRE client != none * **************************************************************************************************/ function clientCreated(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a client is attempting to login. This allows to plugin to accept or * reject the login request. If the function returns false the login request will be * rejected (player will be disconnected). Please make sure the reason parameter * is set in that case, as it will be written to the log. * $PARAM client Client that is requesting to login to the server. * $PARAM rejectType Reject type identification code. * $PARAM reason Message describing why the login is rejected. * $PARAM popupWindowClass Class name of the popup window that is to be shown at the client. * $PARAM popupArgs Optional arguments for the popup window. Note you may have to * explicitly reset them if you change the popupWindowClass. * $REQUIRE client != none * $RETURN True if the login request is accepted, false if it should be rejected. * $ENSURE result == false ? new.reason != "" : true * **************************************************************************************************/ function bool checkLogin(NexgenClient client, out name rejectType, out string reason, out string popupWindowClass, out string popupArgs[4]) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called whenever the login request of a player has been rejected and allows the * plugin to modify the behaviour. * $PARAM client The client that was denied access to the server. * $PARAM rejectType Reject type identification code. * $PARAM reason Reason why the player was rejected from the server. * $PARAM popupWindowClass Class name of the popup window that is to be shown at the client. * $PARAM popupArgs Optional arguments for the popup window. Note you may have to * explicitly reset them if you change the popupWindowClass. * $REQUIRE client != none * **************************************************************************************************/ function modifyLoginReject(NexgenClient client, out name rejectType, out string reason, out string popupWindowClass, out string popupArgs[4]) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called whenever a player has joined the game (after its login has been accepted). * $PARAM client The player that has joined the game. * $REQUIRE client != none * **************************************************************************************************/ function playerJoined(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called whenever a client has finished its initialisation process. During this * process things such as the remote control window are created. So only after the * client is fully initialized all functions can be safely called. * $PARAM client The client that has finished initializing. * $REQUIRE client != none * **************************************************************************************************/ function clientInitialized(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM client The client of the player that was respawned. * $REQUIRE client != none * **************************************************************************************************/ function playerRespawned(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called if a player has left the server. * $PARAM client The player that has left the game. * $REQUIRE client != none * **************************************************************************************************/ function playerLeft(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client != none * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his or her name. * $PARAM client The client that has changed name. * $PARAM oldName The old name of the player. * $PARAM bWasForcedChanged Whether the name change was forced by the controller. * $REQUIRE client != none * **************************************************************************************************/ function playerNameChanged(NexgenClient client, string oldName, bool bWasForcedChanged) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the game has started. * **************************************************************************************************/ function gameStarted() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the game has ended. * **************************************************************************************************/ function gameEnded() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called to check if the specified message should be send to the given receiver. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM msg The message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $PARAM type Type of the message that is to be send. * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorBroadcastMessage(Actor sender, Pawn receiver, out coerce string msg, optional bool bBeep, out optional name type) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called to check if the specified team message should be send to the given * receiver. * is called if a message is send to player. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM pri Player replication info of the sending player. * $PARAM s The message that is to be send. * $PARAM type Type of the message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorTeamMessage(Actor sender, Pawn receiver, PlayerReplicationInfo pri, coerce string s, name type, optional bool bBeep) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called to check if the given localized message should be send to the specified * receiver. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM message The class of the localized message that is to be send. * $PARAM switch Optional message switch argument. * $PARAM relatedPRI_1 PlayerReplicationInfo of a player that is related to the message. * $PARAM relatedPRI_2 PlayerReplicationInfo of a player that is related to the message. * $PARAM optionalObject Optional object used to construct the message string. * $REQUIRE message != none * $RETURN True if the message should be send, false if it should be suppressed. * **************************************************************************************************/ function bool mutatorBroadcastLocalizedMessage(Actor sender, Pawn receiver, out class message, out optional int switch, out optional PlayerReplicationInfo relatedPRI_1, out optional PlayerReplicationInfo relatedPRI_2, out optional Object optionalObject) { // To implement in subclass. return true; } /*************************************************************************************************** * * $DESCRIPTION Called when a player has send a mutate call to the server. * $PARAM mutateString Mutator specific string (indicates the action to perform). * $PARAM sender Player that has send the message. * **************************************************************************************************/ function mutate(string mutateString, PlayerPawn sender) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player attempts to login to the server. Allows mutators to modify * some of the login parameters. * $PARAM spawnClass The PlayerPawn class to use for the player. * $PARAM portal Name of the portal where the player wishes to spawn. * $PARAM option Login option parameters. * **************************************************************************************************/ function modifyLogin(out class spawnClass, out string portal, out string options) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM other The pawn/player that has (re)spawned. * $REQUIRE other != none * **************************************************************************************************/ function modifyPlayer(Pawn other) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a pawn takes damage. * $PARAM actualDamage The amount of damage sustained by the pawn. * $PARAM victim Pawn that has become victim of the damage. * $PARAM instigatedBy The pawn that has instigated the damage to the victim. * $PARAM hitLocation Location where the damage was dealt. * $PARAM momentum Momentum of the damage that has been dealt. * $PARAM damageType Type of damage dealt to the victim. * $REQUIRE victim != none * **************************************************************************************************/ function mutatorTakeDamage(out int actualDamage, Pawn victim, Pawn instigatedBy, out vector hitLocation, out vector momentum, name damageType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the server wants to check if a players death should be prevented. * $PARAM victim The pawn that was killed. * $PARAM killer The pawn that has killed the victim. * $PARAM damageType Type of damage dealt to the victim. * $PARAM hitLocation Location where the damage was dealt. * $RETURN True if the players death should be prevented, false if not. * **************************************************************************************************/ function bool preventDeath(Pawn victim, Pawn killer, name damageType, vector hitLocation) { // To implement in subclass. return false; } /*************************************************************************************************** * * $DESCRIPTION Called when a player was killed by another player. * $PARAM killer The pawn that killed the other pawn. Might be none. * $PARAM victim Pawn that was the victim. * **************************************************************************************************/ function scoreKill(Pawn killer, Pawn victim) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this plugin that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ function configChanged(byte configType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Plugin timer driven by the Nexgen controller. Ticks at a frequency of 1 Hz and is * independent of the game speed. * **************************************************************************************************/ function virtualTimer() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the server is about to perform a server travel. Note that the server * travel may fail to switch to the desired map. In that case the server will * continue running the current game or a second notifyBeforeLevelChange() call may * occur when trying to switch to another map. So be carefull what you do in this * function!!! * **************************************************************************************************/ function notifyBeforeLevelChange() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Handles a potential command message. * $PARAM sender PlayerPawn that has send the message in question. * $PARAM msg Message send by the player, which could be a command. * $REQUIRE sender != none * $RETURN True if the specified message is a command, false if not. * **************************************************************************************************/ function bool handleMsgCommand(PlayerPawn sender, string msg) { return false; } h/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerListBox * $VERSION 1.03 (20-10-2007 14:45) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Player listbox GUI component. * **************************************************************************************************/ class NexgenPlayerListBox extends UWindowListBox; #exec TEXTURE IMPORT NAME=noCountry FILE=Resources\NoCountry.pcx GROUP="GFX" FLAGS=2 MIPS=Off var color baseColor; // Default color used to render textures in their original color. var color selectColor; // Background color of selected items. var color teamColor[6]; // Team colors used for drawing the items text. var bool bShowCountryFlag; // Indicates if little country flags should be displayed. /*************************************************************************************************** * * $DESCRIPTION Renders the specified listbox item. * $PARAM c The canvas object on which the rendering will be performed. * $PARAM item Item to render. * $PARAM x Horizontal offset on the canvas. * $PARAM y Vertical offset on the canvas. * $PARAM w Width of the item that is to be rendered. * $PARAM h Height of the item that is to be rendered. * $REQUIRE c != none && item != none * $OVERRIDE * **************************************************************************************************/ function drawItem(Canvas c, UWindowList item, float x, float y, float w, float h) { local int offsetX; local texture flagTex; local color backgroundColor; // Draw background. backgroundColor = getBackgroundColor(NexgenPlayerList(item)); if (backgroundColor != baseColor) { c.drawColor = backgroundColor; drawStretchedTexture(c, x, y, w, h - 1, Texture'WhiteTexture'); } // Draw country flag. offsetX = 2; if (bShowCountryFlag) { c.drawColor = baseColor; flagTex = NexgenPlayerList(item).getFlagTex(); if (flagTex == none) { flagTex = texture'noCountry'; } drawClippedTexture(c, x + offsetX, y + 1, flagTex); offsetX += 18; } // Draw text. c.drawColor = getDisplayColor(NexgenPlayerList(item)); c.font = getDisplayFont(NexgenPlayerList(item)); clipText(c, x + offsetX, y, getDisplayText(NexgenPlayerList(item))); } /*************************************************************************************************** * * $DESCRIPTION Called when an item was double clicked on. * $PARAM item The item which was double clicked. * $REQUIRE item != none * $OVERRIDE * **************************************************************************************************/ function doubleClickItem(UWindowListBoxItem item) { if (notifyWindow != none) { notifyWindow.notify(self, DE_DoubleClick); } } /*************************************************************************************************** * * $DESCRIPTION Returns the font in which the text should be displayed for a list item. * $PARAM item The item for which its display font has to be determined. * $REQUIRE item != none * $RETURN The font in which the text should be displayed for the specified item. * **************************************************************************************************/ function font getDisplayFont(NexgenPlayerList item) { return root.fonts[F_Bold]; } /*************************************************************************************************** * * $DESCRIPTION Returns the text displayed for a list item. * $PARAM item The item for which its display text has to be determined. * $REQUIRE item != none * $RETURN The text that should be displayed for the specified item in the listbox. * **************************************************************************************************/ function string getDisplayText(NexgenPlayerList item) { return "[" $ item.pTitle $ "] " $ item.pName; } /*************************************************************************************************** * * $DESCRIPTION Returns the color of the background in which the text should be displayed for a * list item. * $PARAM item The item for which its background color has to be determined. * $REQUIRE item != none * $RETURN The background color of the the specified item. * **************************************************************************************************/ function color getBackgroundColor(NexgenPlayerList item) { if (item.bSelected) { return selectColor; } else { return baseColor; } } /*************************************************************************************************** * * $DESCRIPTION Returns the color in which the text should be displayed for a list item. * $PARAM item The item for which its display color has to be determined. * $REQUIRE item != none * $RETURN The color in which the text should be displayed for the specified item. * **************************************************************************************************/ function color getDisplayColor(NexgenPlayerList item) { return teamColor[item.pTeam]; } /*************************************************************************************************** * * $DESCRIPTION Adds a new player to the list. * $RETURN The player item that was added to the list. * $ENSURE result != none * **************************************************************************************************/ function NexgenPlayerList addPlayer() { return NexgenPlayerList(items.append(listClass)); } /*************************************************************************************************** * * $DESCRIPTION Removes the player with the specified player code from the list. * $PARAM playerNum The player to remove. * $REQUIRE playerNum >= 0 * $ENSURE getPlayer(playerNum) == none * **************************************************************************************************/ function removePlayer(int playerNum) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pNum == playerNum) { item.remove(); } } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the player item with the specified player number. * $PARAM playerNum Indicates the player to return. * $REQUIRE playerNum >= 0 * $RETURN The player item that has the specified player number, or none if there is no * player item with the specified player number. * **************************************************************************************************/ function NexgenPlayerList getPlayer(int playerNum) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pNum == playerNum) { return item; } } // Player not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the player item with the specified player number. * $PARAM playerNum Indicates the player to return. * $REQUIRE playerNum >= 0 * $RETURN The player item that has the specified player number, or none if there is no * player item with the specified player number. * **************************************************************************************************/ function NexgenPlayerList getPlayerByID(string clientID) { local NexgenPlayerList item; // Search for player. for (item = NexgenPlayerList(items); item != none; item = NexgenPlayerList(item.next)) { if (item.pClientID ~= clientID) { return item; } } // Player not found, return none. return none; } /*************************************************************************************************** * * $DESCRIPTION Moves the selected item to the specified player listbox. * $PARAM target The player listbox where the currently selected item should be moved to. * $REQUIRE target != none * $ENSURE selectedItem == none * **************************************************************************************************/ function moveSelectedPlayerTo(NexgenPlayerListBox target) { local NexgenPlayerList item; if (selectedItem != none) { item = target.addPlayer(); NexgenPlayerList(selectedItem).copyTo(item); selectedItem.remove(); selectedItem = none; target.setSelectedItem(item); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ IC "Pause game"KC "(Dis)allow team switch"c?/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerList * $VERSION 1.04 (24-10-2007 20:25) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Player list item description class. Stores the basic player attributes, such as * name, team and clientID. * **************************************************************************************************/ class NexgenPlayerList extends UWindowListBoxItem; var int pNum; // Player num. var string pName; // Name used by the player. var string pTitle; // Title of the players account. var string pIPAddress; // IP Address. var string pClientID; // Client identification code. var string pCountry; // Country code based on the IP Address. var byte pTeam; // Team number. var texture flagTex; // Flag texture for the country. var bool bFlagTexSet; // Whether or not the flag texture has been set. const specTeam = 5; // Team used to indicate spectators. /*************************************************************************************************** * * $DESCRIPTION Sets the flag texture based on the current country code of the player. If the * country code is invalid the flag texture will be set to none. * $ENSURE bFlagTexSet * **************************************************************************************************/ function setFlagTex() { if (len(pCountry) == 2) { flagTex = texture(dynamicLoadObject(class'NexgenUtil'.default.countryFlagsPkg $ "." $ pCountry, class'Texture')); } else { flagTex = none; } bFlagTexSet = true; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the flag texture associated with the country code of the player. Note * that if the country code was changed setFlagTex() has to be called first in order * to update the texture. * $RETURN A texture of the country flag of the player, might be none if an invalid country * code is used. * **************************************************************************************************/ function texture getFlagTex() { if (!bFlagTexSet) { setFlagTex(); } return flagTex; } /*************************************************************************************************** * * $DESCRIPTION Copies the attributes of this player list item to the specified player list item. * $PARAM target The player list item where the attributes should be copied to. * $REQUIRE target != none * **************************************************************************************************/ function copyTo(NexgenPlayerList target) { target.pNum = self.pNum; target.pName = self.pName; target.pTitle = self.pTitle; target.pIPAddress = self.pIPAddress; target.pClientID = self.pClientID; target.pCountry = self.pCountry; target.pTeam = self.pTeam; target.flagTex = self.flagTex; target.bFlagTexSet = self.bFlagTexSet; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this player is a spectator. * $RETURN True if this player is a spectator, false if not. * **************************************************************************************************/ function bool isSpectator() { return pTeam == specTeam; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ LC "Reconnect as spectator"OC "Reconnect as player"su/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerData * $VERSION 1.00 (24-11-2007 15:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Saved player data support class. An instance of this class can be used to store * attributes of a leaving player, which can later be retrieved once the player * reconnects. * **************************************************************************************************/ class NexgenPlayerData extends info; struct AttributeEntry { // Attribute data structure. var string name; // Name of the attribute. var string value; // Value of the attribute. }; var private AttributeEntry attributes[64]; // Attributes for this player. var string clientID; // ClientID of the player. var NexgenPlayerData next; // Next player data instance. /*************************************************************************************************** * * $DESCRIPTION Removes all attributes stored in this NexgenPlayerData instance. * **************************************************************************************************/ function clearData() { local int index; // Clear entries. while (index < arrayCount(attributes)) { attributes[index].name = ""; attributes[index].value = ""; index++; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function string get(string name, optional string defaultValue) { local int index; local bool bFound; // Locate index of attribute. while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name ~= name) { bFound = true; } else { index++; } } // Return attribute value, or the default value if the attribute wasn't found. if (bFound) { return attributes[index].value; } else { return defaultValue; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the boolean value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function bool getBool(string name, optional bool defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return value ~= string(true); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the byte value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function byte getByte(string name, optional byte defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return byte(value); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the integer value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function int getInt(string name, optional int defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return int(value); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the floating point value of the specified attribute. * $PARAM name Name of the attribute that is to be retrieved. * $PARAM defaultValue Value to return if the attribute couldn't been found. * $RETURN The value of the specified attribute, or the default value if the attribute * doesn't exist. * **************************************************************************************************/ function float getFloat(string name, optional float defaultValue) { local string value; value = class'NexgenUtil'.static.trim(get(name)); if (value == "") { return defaultValue; } else { return float(value); } } /*************************************************************************************************** * * $DESCRIPTION Updates the specified attribute with the given value. * $PARAM name Name of the attribute that is to be stored. * $PARAM value (New) Value of the attribute to store. * $REQUIRE name != "" * $RETURN True if the attribute was updated/save, false if not. * $ENSURE result == true ? new.get(name) == value : true * **************************************************************************************************/ function bool set(string name, coerce string value) { local int index; local bool bFound; // Check if attribute already exists. while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name ~= name) { bFound = true; } else { index++; } } // It doesn't, search for an empty entry. if (!bFound) { index = 0; while (!bFound && index < arrayCount(attributes)) { if (attributes[index].name == "") { bFound = true; } else { index++; } } } // Save attribute. if (bFound) { attributes[index].name = name; attributes[index].value = value; } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ PC@$jXga(/^^^~^:-Fa/!|\@$l E$FRK QC "Send to URL"WCw>dH.GV\l Jj\l _ RC "Switch to %1"SC ""ZC "Delete"a/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerACListItem * $VERSION 1.01 (21-10-2007 11:24) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Online user acccount list item description class. * **************************************************************************************************/ class NexgenPlayerACListItem extends NexgenPlayerList; var bool bHasAccount; var byte accountNum; /*************************************************************************************************** * * $DESCRIPTION Compares two UWindowList items. * $PARAM a First item to compare. * $PARAM b Second item to compare. * $REQUIRE a != none && b != none * $RETURNS -1 If the first item is 'smaller' then the second item, otherwise 1 is returned. * $OVERRIDE * **************************************************************************************************/ function int compare(UWindowList a, UWindowList b) { local string pTitleA, pTitleB; local string pNameA, pNameB; pTitleA = caps(NexgenPlayerACListItem(a).pTitle); pTitleB = caps(NexgenPlayerACListItem(b).pTitle); pNameA = caps(NexgenPlayerACListItem(a).pName); pNameB = caps(NexgenPlayerACListItem(b).pName); if (pTitleA < pTitleB) { return -1; } else if (pTitleA == pTitleB) { if (pNameA < pNameB) { return -1; } else if (pNameA == pNameB) { return 0; } else { return 1; } } else { return 1; } } g%/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPlayerACListBox * $VERSION 1.00 (20-10-2007 15:47) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Online player account listbox GUI component. * **************************************************************************************************/ class NexgenPlayerACListBox extends NexgenPlayerListBox; var color accountColor; var color selectedAccountColor; /*************************************************************************************************** * * $DESCRIPTION Returns the font in which the text should be displayed for a list item. * $PARAM item The item for which its display font has to be determined. * $REQUIRE item != none * $RETURN The font in which the text should be displayed for the specified item. * $OVERRIDE * **************************************************************************************************/ function font getDisplayFont(NexgenPlayerList item) { if (NexgenPlayerACListItem(item).bHasAccount) { return root.fonts[F_Bold]; } else { return root.fonts[F_Normal]; } } /*************************************************************************************************** * * $DESCRIPTION Returns the color of the background in which the text should be displayed for a * list item. * $PARAM item The item for which its background color has to be determined. * $REQUIRE item != none * $RETURN The background color of the the specified item. * $OVERRIDE * **************************************************************************************************/ function color getBackgroundColor(NexgenPlayerList item) { if (item.bSelected) { if (NexgenPlayerACListItem(item).bHasAccount) { return selectedAccountColor; } else { return selectColor; } } else { return baseColor; } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ {M/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPasswordDialog * $VERSION 1.01 (23-12-2006 14:15) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenPasswordDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var UWindowSmallButton spectatorButton; // Spectator button component. var UWindowEditControl passwordInput; // Password input field component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string passwordText; // Label to display before the password field. var localized string reconnectText; // Text to display on the reconnect button. var localized string spectatorText; // Text to display on the spectator button. const SSTR_ServerPassword = "Password"; // Server password setting string. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE reconnectButton != none && spectatorButton != none && passwordInput != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); passwordInput = addEditControl(cy, passwordText, 64.0); spectatorButton = addButton(spectatorText, 64.0); reconnectButton = addButton(reconnectText, 64.0); // Set component properties. passwordInput.setMaxLength(24); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM allowSpecs Indicates if reconnecting as spectator should be allowed. * $PARAM str2 Not used. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string allowSpecs, optional string str2, optional string str3, optional string str4) { // sc.get(serverID, NexgenClient.SSTR_ServerPassword); // Bah, doesn't work! passwordInput.setValue(sc.get(serverID, SSTR_ServerPassword)); spectatorButton.bDisabled = !parseBool(allowSpecs); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Update password. sc.visitServer(serverID); sc.set(serverID, SSTR_ServerPassword, passwordInput.getValue()); sc.saveConfig(); // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } // Spectator button. if (control == spectatorButton && eventType == DE_Click && !spectatorButton.bDisabled) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ pCt,bECw!*:!4!&KMutate NSC START o\C "Add"XC]C "Update"_C "Offline"`C "Online"aC "User title"dC "Account type"R fG "User name"fC "Move up"gC "Delete account type"iC "Add account type"jC "Password"kC "Account title"lC "Account name"qC "%1: %2"CVCc@b 2nC֒CnMMtM> Y7XÔ> Y> YtM7XÑt V]$#Failed to login: invalid password.]DThis server is password protected. You have either entered an invalid password or no password at all. Please enter the password for this server and click reconnect to login with the new password.]] Password:i] Reconnectd] SpectatorJ/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPanelContainer * $VERSION 1.03 (11-3-2008 21:31) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page container. * **************************************************************************************************/ class NexgenPanelContainer extends NexgenPanel; var UWindowPageControl pages; // Page control for panels added on this container. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); pages = UWindowPageControl(createWindow(class'UWindowPageControl', 0, 0, winWidth - 2, winHeight - 3)); } /*************************************************************************************************** * * $DESCRIPTION Retrieves the panel with the specified name. * $PARAM panelName Name of the panel that is to be returned. * $REQUIRE panelName != "" * $RETURN The panel that was requested or none if the panel wasn't found. * **************************************************************************************************/ function NexgenPanel getPanel(string panelName) { local UWindowPageControlPage pageControl; local NexgenPanel panel; local bool bFound; // Search for panel. pageControl = pages.firstPage(); while (!bFound && pageControl != none) { panel = NexgenPanel(pageControl.page); // Is this the one we are looking for? if (panel != none) { if (panel.panelIdentifier ~= panelName) { // Yeah, stop looking and return result. bFound = true; } else if (panel.isA('NexgenPanelContainer')) { // No, but it is a panel container so it might contain the one we're looking for. panel = NexgenPanelContainer(panel).getPanel(panelName); bFound = panel != none; } } // Continue with next page if not found yet. if (!bFound) { pageControl = pageControl.nextPage(); } } // Return result. if (bFound) { return panel; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Adds a new NexgenPanel to the container or one of its subcontainers. To specify * a specific parent use the parent parameter to indicate the path, e.g. * "plugin,settings". If an invalid path is specified the panel won't be created. * $PARAM title Text to display in the tab header. * $PARAM panelClass Type of NexgenPanel to add/create. * $PARAM identifier Identifier to assign to the new panel. * $PARAM parent Path where to the parent of the new panel. * $REQUIRE panelClass != none * $RETURN The panel that was created and added to the container, or none if an invalid path * to the parent container was specified. * **************************************************************************************************/ function NexgenPanel addPanel(string title, class panelClass, optional string identifier, optional string parent) { local UWindowPageControlPage pageControl; local NexgenPanel newPanel; local string parentPanel; local string subPanels; local bool bFound; local NexgenPanelContainer container; // Add to subpanel? if (parent != "") { // Get local parent name. if (instr(parent, separator) > 0) { parentPanel = left(parent, instr(parent, separator)); subPanels = mid(parent, instr(parent, separator) + 1); } else { parentPanel = parent; } // Locate local parent. pageControl = pages.firstPage(); while (!bFound && pageControl != none) { container = NexgenPanelContainer(pageControl.page); if (container != none && container.panelIdentifier ~= parentPanel) { bFound = true; } else { pageControl = pageControl.nextPage(); } } // Delegate action. if (bFound) { // NOTE: Recursion can be avoided here, but the performance gain is insignificant. newPanel = container.addPanel(title, panelClass, identifier, subPanels); } } else { // Create panel. pageControl = pages.addPage(title, panelClass); if (pageControl != none) { newPanel = NexgenPanel(pageControl.page); if (identifier != "") { newPanel.panelIdentifier = identifier; } newPanel.client = self.client; newPanel.setContent(); } } // Return created panel. return newPanel; } /*************************************************************************************************** * * $DESCRIPTION Selects the panel with the specified name. * $PARAM panelName The name of the panel that is to be selected. * $RETURN True if the panel was selected, false if it wasn't found. * **************************************************************************************************/ function bool selectPanel(string panelName) { local UWindowPageControlPage pageControl; local NexgenPanel panel; local bool bFound; pageControl = pages.firstPage(); while (!bFound && pageControl != none) { panel = NexgenPanel(pageControl.page); // Check panel. if (panel.panelIdentifier ~= panelName) { // Panel found, select it. bFound = true; } else if (panel.isA('NexgenPanelContainer')) { // No, but page is a NexgenPanelContainer, so it may include the panel. bFound = NexgenPanelContainer(panel).selectPanel(panelName); } // Panel found? if (bFound) { // Panel found, select it. pages.gotoTab(pageControl, true); } else { // No, continue with next page. pageControl = pageControl.nextPage(); } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the sub panels on this panel that the server configuration has been * updated. * $PARAM configType Type of settings that have been changed. * $OVERRIDE * **************************************************************************************************/ function configChanged(byte configType) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).configChanged(configType); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * $OVERRIDE * **************************************************************************************************/ function gameInfoChanged(byte infoType) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).gameInfoChanged(infoType); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * $OVERRIDE * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).playerEvent(playerNum, eventType, args); } } } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * $OVERRIDE * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { local UWindowPageControlPage pageControl; for (pageControl = pages.firstPage(); pageControl != none; pageControl = pageControl.nextPage()) { if (NexgenPanel(pageControl.page) != none) { NexgenPanel(pageControl.page).notifyEvent(type, arguments); } } } wCbCs\tDr**:%:%'E Vk-N  o,{o v $(o v~R a|E ~-N 'R   R -R ~R  P%:%.~R  S%:%hob-R  rC "Send to %1: %2"sC "%1 has been unblocked."tC "%1 has been blocked."uC "History"yC "Send windowed PM"et/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenPanel * $VERSION 1.08 (11-3-2008 21:30) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page. * **************************************************************************************************/ class NexgenPanel extends NexgenContentPanel; var string panelIdentifier; // Unique panel identifier. var NexgenClient client; // Nexgen client instance. var float panelHeight; // Desired panel height. /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this panel. This function is automatically called after * the panel has been successfully created and all variables have been set. This * applies in particular for the client variable which is often required to setup the * contents of the panel. * **************************************************************************************************/ function setContent() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ function configChanged(byte configType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * **************************************************************************************************/ function gameInfoChanged(byte infoType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function playerEvent(int playerNum, string eventType, optional string args) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function notifyEvent(string type, optional string arguments) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Adds a new player to the specified player listbox. * $PARAM list The player listbox where the new player should be added to. * $PARAM playerNum The player code of the new player to add. * $PARAM args Player joined event arguments. * $REQUIRE list != none && playerNum >= 0 * $RETURN The player item that was added to the list. * $ENSURE result != none * **************************************************************************************************/ function NexgenPlayerList addPlayerToList(NexgenPlayerListBox list, int playerNum, string args) { local NexgenPlayerList playerItem; playerItem = list.addPlayer(); playerItem.pNum = playerNum; playerItem.pName = class'NexgenUtil'.static.getProperty(args, client.PA_Name); playerItem.pTitle = class'NexgenUtil'.static.getProperty(args, client.PA_Title); playerItem.pIPAddress = class'NexgenUtil'.static.getProperty(args, client.PA_IPAddress); playerItem.pClientID = class'NexgenUtil'.static.getProperty(args, client.PA_ClientID); playerItem.pCountry = class'NexgenUtil'.static.getProperty(args, client.PA_Country); playerItem.pTeam = byte(class'NexgenUtil'.static.getProperty(args, client.PA_Team)); return playerItem; } /*************************************************************************************************** * * $DESCRIPTION Updates the attributes of the specified player in the given player listbox. * $PARAM list The player listbox where the player info should be updated. * $PARAM playerNum The player code of the player to update. * $PARAM args Player attribute change event arguments. * $REQUIRE list != none && playerNum >= 0 * $RETURN The player item that was updated in the list. Might be none if the list didn't * contain the player with the specified player code. * **************************************************************************************************/ function NexgenPlayerList updatePlayerInfo(NexgenPlayerListBox list, int playerNum, string args) { local NexgenPlayerList playerItem; local string value; playerItem = list.getPlayer(playerNum); if (playerItem != none) { // Name attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Name); if (value != "") { playerItem.pName = value; } // Title attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Title); if (value != "") { playerItem.pTitle = value; } // Country attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Country); if (value != "") { playerItem.pCountry = value; playerItem.setFlagTex(); } // Team attribute. value = class'NexgenUtil'.static.getProperty(args, client.PA_Team); if (value != "") { playerItem.pTeam = byte(value); } } return playerItem; } /*************************************************************************************************** * * $DESCRIPTION Adds a new component container panel to the current region. * $PARAM bgType Panel border/background style. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenPanel addSubPanel(class panelClass, optional EPanelBackType bgType) { local NexgenPanel panel; panel = NexgenPanel(addComponent(panelClass)); panel.panelBGType = bgType; panel.parentCP = self; panel.client = self.client; panel.setContent(); return panel; } xCi \uJh1zSi ::$ w* -y :% -Z-Z't:%-Z-Z(- -H [S-H 'p-H --H (pp-H : A% -zS[< A-z'E:!4!,a(b #???,b _ ?,h ?,w-F Ez_ w.-h i h ?% t%ww%D?& th &_ i _ ?% p%w&w%D?& p_ fw% w, w_  i e@ 'w%MNexgenIdleKickedDialogKDisconnectw%_ ?,h ?,wf::$n?%Rg?(w* s%:!4!,nv $ni n?%v $v =:Cn&a ?%a i a ?%{ ${ =:Ca &fI?%Ii UI?%i$fi=:CI& Cc g9?::$-{ - {F w* w *w.* wG*] VG-{ (w-{  SAa(a-{ -y -f w!*!d% w* {f -I -t'S?, c %-t c ,wc I*-tc Inc X-t-y (a(`-^ U-y  n?%:!4!,nSn?%Sn@ -q-SAt|22AutoSSNormalGamefalsetrue?|22AutoSSMatchtruetrue -GA -q'::$1-^  r*a(a-^  SAa(R2Login timeout forRU zC "Send normal PM"{C "Message"|C "Block all private messages"}C "Block / Unblock"AD "Blocked"n/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenNullMessage * $VERSION 1.00 (6-4-2008 11:59) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Special placeholder message. This localized message is used by the * NexgenHUDWrapper class to clear the local message queue in the original HUD. * **************************************************************************************************/ class NexgenNullMessage extends LocalMessagePlus; /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ @D];QL-2a v a  a a?@'Oa  GDc|7Y.::$.a?@'W::$-{ '-y 'w BD "Players"CD) "Auto screenshot at the end of matches"DD. "Auto screenshot at the end of normal games"ED "Miscellaneous settings"FD/ "Play a sound when a private message arrives"ID, "Show player location on teamsay messages"HD 10ND 10JD! "Enable message 'flash' effect"KD "Enable Nexgen message HUD"LD "User interface"MD "Pause game"PD "Open control panel"OD 30.0SD 2.0\D "Open map vote"o@v  "0123456789ABCDEF"TD 4.0XD 1.25uVXu22u ClientKeyK u22u ClientIDzXX wK  U DXu2Eu ClientKeyXu2Eu ClientIDK u2b{ U DXK X wK  U DXu2Eu ClientKeyXu2Eu ClientIDK u2bluv '2uFu PasswordQ{l #[w u Passwordl $[w u ClientKeyX  w0/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenNoPlayRightDialog * $VERSION 1.00 (23-12-2006 19:18) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player hasn't got the right to play on the server. * **************************************************************************************************/ class NexgenNoPlayRightDialog extends NexgenPopupDialog; var UWindowSmallButton spectatorButton; // Spectator button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string spectatorText; // Text to display on the spectator button. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE spectatorButton != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); spectatorButton = addButton(spectatorText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the spectator button has been clicked and deals with it accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); // Spectator button. if (control == spectatorButton && eventType == DE_Click) { getplayerowner().updateURL(SSTR_OverrideClass, spectatorClass, true); getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ CVDM@d 2WDfCnŔ> Y> Y V].-Failed to login: you have no playing rights.]DThis server has playing rights disabled by default. You can't play on this server unless an administrator has explicitly allowed you to play. Meanwhile you can still be a spectator on the server.d] SpectatorZD 0.50L[/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainPanelBar * $VERSION 1.02 (4-3-2007 23:06) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel root toolbar. * **************************************************************************************************/ class NexgenMainPanelBar extends UWindowDialogClientWindow; var UWindowSmallButton closeButton; // Button to close the control panel. var UWindowSmallButton sendButton; // Button to send the message entered in the msgInp EditBox. var UWindowEditControl msgInp; // Input field for the chat message. var localized string closeButtonText; // Text displayed on the close button. var localized string sendButtonText; // Text displayed on the send button. var localized string msgLabelText; // Label text displayed in front of the input field. const borderDist = 4.0; // Minimum distance between a component and vertical edges. const separatorDist = 4.0; // Extra distance between components for separation. const componentDist = 4.0; // Horizontal distance between components (in pixels). const buttonWidth = 64.0; // Width of button components. const buttonHeight = 16.0; // Height of button components. const labelWidth = 48.0; // Width of label components. const labelHeight = 12.0; // Height of label components. const editCtrlHeight = 16.0; // Height of edit control components. const sayCommand = "say"; // Chat message console command. /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this GUI component. * $OVERRIDE * **************************************************************************************************/ function created() { local float cx, cy, cw, ch; local UMenuLabelControl label; super.created(); // Add close button. cw = buttonWidth; ch = buttonHeight; cx = winWidth - cw - borderDist; cy = (winHeight - ch) / 2.0; closeButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); closeButton.setText(closeButtonText); // Add send button. cx = cx - cw - componentDist - separatorDist; sendButton = UWindowSmallButton(createControl(class'UWindowSmallButton', cx, cy, cw, ch)); sendButton.setText(sendButtonText); // Add message input field. ch = editCtrlHeight; cw = cx - borderDist - labelWidth - 2 * componentDist; cx = borderDist + labelWidth + componentDist; cy = (winHeight - ch) / 2.0; msgInp = UWindowEditControl(createControl(class'UWindowEditControl', cx, cy, cw, ch)); msgInp.editBoxWidth = cw; msgInp.setMaxLength(250); msgInp.setHistory(true); // Add 'say' label. cw = labelWidth; ch = labelHeight; cx = borderDist; cy = (winHeight - ch) / 2.0; label = UMenuLabelControl(createControl(class'UMenuLabelControl', cx, cy, cw, ch)); label.setText(msgLabelText); label.align = TA_Center; } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ super.paint(c, x, y); drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType){ super.notify(control, eventType); if (control == closeButton && eventType == DE_Click) { UWindowFramedWindow(getParent(class'UWindowFramedWindow')).close(); } if (control == sendButton && eventType == DE_Click) { msgInp.editBox.keyDown(getPlayerOwner().EInputKey.IK_Enter, 0.0, 0.0); } if (control == msgInp && eventType == DE_EnterPressed) { sendMessage(); } } /*************************************************************************************************** * * $DESCRIPTION Sends the message in the msgInp EditBox to the server and clears the value of the * editBox. No message will be send if the message is an empty string. * $ENSURE msgInp.getValue() == "" * **************************************************************************************************/ function sendMessage() { local string msg; // Get chat message. msg = msgInp.getValue(); // Send message & reset edit box value. if (msg != "") { msgInp.setValue(""); getPlayerOwner().consoleCommand(sayCommand @ msg); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ _D 15UD^WFR -S  b^\-S k (H^l ^ ClientKey-S  gk-S -S z U Dk^\-S   bD "Suicide"S _,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainPanel * $VERSION 1.01 (3-3-2007 17:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel root. * **************************************************************************************************/ class NexgenMainPanel extends NexgenPanelContainer; const barHeight = 22; /*************************************************************************************************** * * $DESCRIPTION Creates the layout for this panel. * $OVERRIDE * **************************************************************************************************/ function created() { super(NexgenPanel).created(); pages = UWindowPageControl(createWindow(class'UWindowPageControl', 0, 0, winWidth, winHeight - barHeight - 3)); createWindow(class'NexgenMainPanelBar', 0, winHeight - barHeight - 3, winWidth, barHeight); } /*************************************************************************************************** * * $DESCRIPTION Notifies the window that a new level is going to be loaded. * $OVERRIDE * **************************************************************************************************/ function notifyBeforeLevelChange() { super.notifyBeforeLevelChange(); close(); } /*************************************************************************************************** * * $DESCRIPTION Closes the dialog. * $PARAM bByParent The close call was issued by the parent of the dialog. * $OVERRIDE * **************************************************************************************************/ function close(optional bool bByParent) { local UWindowRootWindow rootWin; local UWindowWindow win; local bool bWindowVisible; // Check if there is another visible window. rootWin = WindowConsole(getPlayerOwner().player.console).root; if (rootWin != none) { win = rootWin.firstChildWindow; while (!bWindowVisible && win != none) { // Current window visible? bWindowVisible = win != parentWindow && win.windowIsVisible() && win.bLeaveOnscreen; // Continue with next window. win = win.nextSiblingWindow; } } // Close the window. if (!bWindowVisible) { WindowConsole(getPlayerOwner().player.console).closeUWindow(); } super.close(bByParent); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ aD 4M/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenMainFrame * $VERSION 1.00 (28-1-2007 18:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen GUI main window class. Defines the frame for the Nexgen main window. * **************************************************************************************************/ class NexgenMainFrame extends UMenuFramedWindow; var float windowWidth; // Width of the main window frame (in pixels). var float windowHeight; // Height of the main window frame (in pixels). var NexgenMainPanel mainPanel; // Control panel root. /*************************************************************************************************** * * $DESCRIPTION Makes sure the main frame is properly setup. * $ENSURE mainPanel != none * $OVERRIDE * **************************************************************************************************/ function created() { super.created(); windowTitle = "Nexgen Server Controller v" $ left(class'NexgenUtil'.default.version, 4); clientArea = createWindow(class'NexgenMainPanel', 4, 16, winWidth - 4, winHeight - 16); mainPanel = NexgenMainPanel(clientArea); bLeaveOnScreen = true; bMoving = true; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ nD 192dD "Switch to yellow"B_/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenLogFile * $VERSION 1.01 (17-6-2008 13:53) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Class that handles the writing of Nexgen logs to a file. * **************************************************************************************************/ class NexgenLogFile extends NexgenTextFile; var NexgenController control; // The Nexgen controller. var NexgenLang lng; // The language support instance. var string timeStampFormat; // Time stamp format used. const defaultLogFileExtension = "log"; // The default log file extension, if none is specified. const tempLogFileExtension = "tmp"; // File extension for temp files. /*************************************************************************************************** * * $DESCRIPTION Initializes the log file. Start the log based on the settings contained by the * NexgenConfig object in the Nexgen server controller (control.sConf). If the log * file was successfully created bIsOpen will be set to true. * $REQUIRE owner != none && owner.isA('NexgenController') * **************************************************************************************************/ function preBeginPlay() { local string logFilePath; local string logFileName; local string logFileExtension; local string tempLogFile; local string logFile; local bool bSuccess; super.preBeginPlay(); // Get controller. control = NexgenController(owner); lng = control.lng; // Construct log file name. logFileName = class'NexgenUtil'.static.autoFormat(control, control.sConf.logFileNameFormat); logFileName = class'NexgenUtil'.static.validateFileName(logFileName); if (logFileName != "") { logFileExtension = class'NexgenUtil'.static.trim(control.sConf.logFileExtension); if (left(logFileExtension, 1) == ".") { logFileExtension = mid(logFileExtension, 1); } if (logFileExtension == "") { logFileExtension = defaultLogFileExtension; } logFilePath = class'NexgenUtil'.static.trim(control.sConf.logPath); if (logFilePath != "") { logFilePath = class'NexgenUtil'.static.replace(logFilePath, "\\", "/"); if (right(logFilePath, 1) == "/") { logFilePath = left(logFilePath, len(logFilePath) - 1); } } logFile = logFilePath $ "/" $ logFileName $ "." $ logFileExtension; tempLogFile = logFile $ "." $ tempLogFileExtension; } // Open the log file. bSuccess = openFile(tempLogFile, logFile); if (bSuccess) { timeStampFormat = control.sConf.logFileTimeStampFormat; beginLog(); control.nscLog(lng.format(lng.logFileCreated, tempLogFile)); } else { control.nscLog(lng.format(lng.logFileCreateFailed, logFileName)); //destroy(); } } /*************************************************************************************************** * * $DESCRIPTION Writes the log header. * **************************************************************************************************/ function beginLog() { local NexgenLogEntry logEntry; local string logLine; // Write header. println(lng.logFileTitle, true); println("", true); println(lng.format(lng.logFileEngineVersion, level.engineVersion), true); println(lng.format(lng.logFileNexgenVersion, class'NexgenCorePlugin'.default.pluginVersion), true); println(lng.format(lng.logFileServerID, class'NexgenUtil'.static.formatGUID(control.sConf.serverID)), true); println(lng.format(lng.logFileServerName, control.sConf.serverName), true); println(lng.format(lng.logFileServerPort, level.game.getServerPort()), true); println(lng.format(lng.logFileGameClass, level.game.class), true); println(lng.format(lng.logFileLevelName, class'NexgenUtil'.static.getLevelFileName(level)), true); println(lng.format(lng.logFileLevelTitle, level.summary.title), true); println("", true); println(lng.format(lng.logFileStart, lng.getCurrentDate(lng.longDateTimeFormat)), true); println("", true); // Write buffered logs. for (logEntry = control.logBuffer; logEntry != none; logEntry = logEntry.nextLogEntry) { logLine = control.getLogTypeTag(logEntry.type) @ logEntry.message; if (timeStampFormat != "") { logLine = lng.getDate(timeStampFormat, logEntry.year, logEntry.month, logEntry.day, logEntry.dayOfWeek, logEntry.hour, logEntry.minute, logEntry.second) @ logLine; } println(logLine, true); } // Flush output. flush(); } /*************************************************************************************************** * * $DESCRIPTION Ends the log file. * $ENSURE !bIsOpen * **************************************************************************************************/ function endLog() { if (bIsOpen) { println("", true); println(lng.format(lng.logFileClose, lng.getCurrentDate(lng.longDateTimeFormat)), true); closeFile(); } } /*************************************************************************************************** * * $DESCRIPTION Adds a new entry to the log file. * $PARAM msg Message that should be written to the log. * $PARAM logType The type of log message. * **************************************************************************************************/ function addLog(string msg, optional byte logType) { if (bIsOpen) { if (timeStampFormat == "") { println(control.getLogTypeTag(logType) @ msg); } else { println(lng.getCurrentDate(timeStampFormat) @ control.getLogTypeTag(logType) @ msg); } } } tD "Switch to green"_( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"eD "5-5-5-5-5-5-4"fD "NSC"gD ","hD "="iD "\\"jD "\\/*?:<>\"|"kDmDw,Gd%d}5-5-5-5-5-5-4 5-5-5-5-5-5-4d&x0  x 9K!J g%gK!I pI ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789&gnI pI  dI   I @qD "country"|/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenLogEntry * $VERSION 1.00 (16-6-2008 11:31) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Class that describes a log entry. * **************************************************************************************************/ class NexgenLogEntry extends info; var NexgenLogEntry nextLogEntry; // Next log entry. var string message; // The log message. var byte type; // The type of message. var int year; // Year when the message was generated. var int month; // Month when the message was generated. var int day; // Day when the message was generated. var int dayOfWeek; // Day of the week when the message was generated. var int hour; // Hour when the message was generated. var int minute; // Minute when the message was generated. var int second; // Second when the message was generated. /*************************************************************************************************** * * $DESCRIPTION Called when the NexgenLogEntry was destroyed. Destroys the next log entry. * $ENSURE nextLogEntry == none * $OVERRIDE * **************************************************************************************************/ function destroyed() { if (nextLogEntry != none) { nextLogEntry.destroy(); nextLogEntry = none; } } A/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenLang * $VERSION 1.37 (2-8-2008 14:17) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Localization support class. Provides the language dependant functionality of the * Nexgen server controller. * **************************************************************************************************/ class NexgenLang extends Info; const dateFormatStr = "dd/mm/yyyy hh:mm"; const defaultLogFileNameFormat = "nsc_%Y_%m_%d_%H_%i_%s"; const defaultLogFileTimeStampFormat = "[%d/%m/%Y %H:%i:%s]"; const longDateTimeFormat = "%l, %F %j, %Y, %H:%i:%s"; const startingControllerMsg = "Starting Nexgen Server Controller..."; const noDedicatedServerMsg = "Failed, your server should be dedicated."; const compatibilityModeMsg = "%1 has been detected, entering compatibility mode..."; const autoInstallMsg = "First time run, executing default installation..."; const autoUpdateMsg = "Updating Nexgen configuration to version %1..."; const invalidConfigMsg = "Configuration file has been automatically repaired."; const attrServerIDMsg = "ServerID = %1"; const nexgenBootMsg = "Server crash/reboot detected, executing Nexgen boot sequence..."; const nexgenMapLoadFailMsg = "Warning, attempt to load %1 has failed!"; const nexgenBootFailMsg = "Nexgen boot sequence failed, resuming to normal mode."; const execCommandMsg = "EXEC %1"; const bootLevelSwitchMsg = "Restarting server on %1"; const nexgenActiveMsg = "Nexgen Server Controller is active."; const logFileCreateFailed = "Failed to create log file: %1"; const logFileCreated = "Log file created, logging to: %1"; const loadingPluginMsg = "Loading %1 %2..."; const initFailedMsg = "Failed to initialize %1, destroying..."; const regFailedMsg = "Failed to register %1, destroying..."; const noHUDReplacementClassMsg = "No replacement class found for %1"; const loginRequestMsg = "Login request for %1"; const attrClientIPMsg = "IP = %1"; const attrClientIDMsg = "ClientID = %1"; const attrPasswordMsg = "Password = %1"; const illegalLoginParametersMsg = "Illegal login parameters (possible hack)."; const duplicateIDMsg = "Client ID already in use."; const bannedMsg = "Kicked/banned from the server."; const invalidPassMsg = "Invalid password."; const serverCapacityMsg = "Server is full."; const noPlayRightMsg = "No playing rights."; const loginAcceptedMsg = "Login accepted."; const loginRejectedMsg = "Login denied. Reason: %1"; const logFileTitle = "NEXGEN LOG FILE"; const logFileEngineVersion = "engine-version: %1"; const logFileNexgenVersion = "nexgen-version: %1"; const logFileServerID = "server-id : %1"; const logFileServerName = "server-name : %1"; const logFileServerPort = "server-port : %1"; const logFileGameClass = "game-class : %1"; const logFileLevelName = "level-name : %1"; const logFileLevelTitle = "level-title : %1"; const logFileStart = "Log started at %1"; const logFileClose = "Log closed at %1"; const gameStartedMsg = "The game has started."; const gameEndedMsg = "The game has ended after %1 sec."; const playerJoinMsg = "(+%2) %1"; const playerLeaveMsg = "(-%2) %1"; const playerNameChangeMsg = "%1 has changed his/her name to %2."; const playerLeaveLogMsg = "%1 has left the server."; const teamSwitchFailMsg = "Failed to switch team, %1."; const teamsLockedMsg = "the teams are locked"; const nameChangeFailMsg = "Failed to change name, %1."; const nameChangeDisabled = "name change has been disabled"; const invalidCommandMsg = "Invalid Nexgen command or parameters."; const commandFailedMsg = "Failed to execute command, %1."; const internalErrorMsg = "internal error"; const teamSwitchFailedMsg = "Failed to switch team, %1."; const specTeamSwitchMsg = "spectators can't switch team"; const teamSwitchDisabledMsg = "team switching is disabled on this server"; const playerTeamSwitchDisabledMsg = "you are not allowed to change from team"; const invalidTeamMsg = "team doesn't exist"; const sameTeamMsg = "you are already on %1"; const noSpecsAllowedMsg = "this server doesn't allow spectators"; const noMoreSpecSlotsMsg = "all spectator slots are taken"; const noPlayingRightsMsg = "you do not have playing rights on this server"; const noMorePlayerSlotsMsg = "all player slots are taken"; const teamBalanceFailedMsg = "Failed to balance teams, %1."; const notATeamGameMsg = "this command is only available for team games"; const teamBalanceDisabledMsg = "team balancing is disabled"; const teamsAlreadyBalancedMsg = "the teams are already balanced"; const balanceMsg = "%1 has balanced the teams."; const launchMsg = "%1 has started the game."; const playerReadyMsg = "%1 is READY!"; const forcedEndMsg = "The game has been forced to an end."; const rootAdminTitle = "Root Admin"; const specTitle = "Spectator"; const deathPreventedMsg = "The server has prevented you from dying."; const mutedReminderMsg = "Unable to send message, you are muted."; const accountTypeNameStr = "Account type %1"; const teamKillAttemptMsg = "%1 had a team kill attempt against %2."; const httpClientErrorMsg = "HTTP client error: %1"; const controllerSystemLogTag = "[NSC-SYS]"; const eventLogTag = "[ EVENT ]"; const messageLogTag = "[MESSAGE]"; const chatMessageLogTag = "[ SAY ]"; const teamSayMessageLogTag = "[TEAMSAY]"; const privateMessageLogTag = "[ PMSAY ]"; const adminActionLogTag = "[ ADMIN ]"; const allowedToPlayRightDesc = "Allowed to play on server."; const vipSlotAccessRightDesc = "VIP slot access."; const adminSlotAccessRightDesc = "Admin slot access."; const needsNoPWRightDesc = "Password immune."; const canBeIdleRightDesc = "Can be idle."; const matchAdminRightDesc = "Game supervisor."; const matchSetRightDesc = "Setup matches."; const moderatorRightDesc = "Moderate."; const banOpRightDesc = "Ban operator."; const accountMngrRightDesc = "Manage accounts."; const serverAdminRightDesc = "Server administrator."; const canBanAccountsRightDesc = "Ban registered players."; const hiddenAdminRightDesc = "Hidden admin."; const launchGameMsg = "Press [Fire] to start the game."; const adminLaunchGameMsg = "Please wait, an administrator is going to start the game."; const tournamentLaunchGameMsg = "Waiting for ready signals, press [Fire] to send your ready signal!"; const serverCrashedClientMsg = "The server has crashed and has been reloaded!"; const bootFailedClientMsg = "The Nexgen boot sequence has failed, please contact the server administrator!"; const serverAdminRebootClientMsg = "The server was restarted by an administrator and has been reloaded!"; const welcomeMsg = "Welcome to Nexgen, type !open to open the control panel."; const settingsSavedMsg = "New settings have been saved."; const receivedPMMsg = "[PM] %3 %1: %2"; const receivedPWMsg = "Match password received: '%1'"; const passwordSendMsg = "The password has been send."; const noBanAccountRightMsg = "You are not allowed to kick or ban players that have an account on this server."; const adminTeamSwitchMsg = "%1 has moved %2 to %3."; const adminPauseGameMsg = "%1 has paused the game."; const adminResumeGameMsg = "%1 has resumed the game."; const adminRestartGameMsg = "%1 has restarted the game."; const adminStopGameMsg = "%1 has stopped the game."; const adminPlayerTeamSwitchDisableMsg = "%1 has disabled team switching for %2."; const adminPlayerTeamSwitchEnableMsg = "%1 has enabled team switching for %2."; const adminReconnectAsPlayerMsg = "%1 has reconnected %2 as player."; const adminReconnectAsSpecMsg = "%1 has reconnected %2 as spectator."; const adminSendToURLMsg = "%1 has send %2 to %3."; const adminDisableTeamSwitchMsg = "%1 has disabled team switching."; const adminEnableTeamSwitchMsg = "%1 has enabled team switching."; const adminDisableTeamBalanceMsg = "%1 has disabled team balancing."; const adminEnableTeamBalanceMsg = "%1 has enabled team balancing."; const adminLockTeamsMsg = "%1 has locked the teams."; const adminUnlockTeamsMsg = "%1 has unlocked the teams."; const adminAddBanMsg = "%1 has added %2 to the banlist."; const adminDeleteBanMsg = "%1 has removed %2 from the banlist."; const adminEnableMatchModeMsg = "%1 has set the server in match mode."; const adminDisableMatchModeMsg = "%1 has reset the server in normal mode."; const adminMutePlayerMsg = "%1 has muted %2."; const adminUnmutePlayerMsg = "%1 has unmuted %2."; const adminSetNameMsg = "%1 has renamed %2 to %3."; const adminKickPlayerMsg = "%1 has kicked %2 from the server."; const adminBanPlayerMsg = "%1 has banned %2 from the server."; const adminMuteAllMsg = "%1 has muted all players."; const adminUnmuteAllMsg = "%1 has unmuted all players."; const adminEnableNameChangeMsg = "%1 has allowed nickname changing on the server."; const adminDisableNameChangeMsg = "%1 has disabled nickname changing on the server."; const adminRebootServerMsg = "%1 has rebooted the server."; const adminEnableTournamentModeMsg = "%1 has enabled tournament mode."; const adminDisableTournamentModeMsg = "%1 has disabled tournament mode."; const adminAddAccountType = "%1 has created a new account type named: %2."; const adminUpdateAccountType = "%1 has modified the %2 account type."; const adminRemoveAccountType = "%1 has removed the %2 account type."; const adminMoveAccountType = "%1 has repositioned the %2 account type."; const adminRemoveAccount = "%1 has deleted the account %3 for %2."; const adminUpdateAccount = "%1 has modified the account for %2."; const adminAddAccount = "%1 has created a new %3 account for %2."; const adminUpdateBanMsg = "%1 has modified the ban entry for %2."; const adminUpdateBootControl = "%1 has modified the boot control settings."; const adminSeparateByTag = "%1 has separated the players by their tag."; const adminUpdateMatchSettings = "%1 has modified the match settings."; const adminUpdateIgnoredWeaponList = "%1 has modified the spawn protect ignored weapon list."; const adminUpdateHUDReplacementList = "%1 has modified the HUD replacement list."; const adminUpdateGlobalServerSettings = "%1 has modified the basic server settings."; const adminUpdateMiscNexgenSettings = "%1 has modified the general Nexgen settings."; const adminUpdateLogServerSettings = "%1 has modified the log settings."; const invalidPasswordMsg = "Unable to login as administrator, invalid password."; const adminLoginMsg = "%1 has logged in as %2."; const autoReconnectAlert = "Connection lost!\\nReconnecting in %1..."; const reconnectingAlert = "Connection lost!\\nReconnecting now..."; const rebootAlert = "Warning, reboot sequence activated!\\nRebooting server in %1..."; const idleAlert = "Idle / camper detection activated!\\nMove or be kicked in %1..."; const waitingState = "Waiting [%1]"; const waitingStateUnknownTime = "Waiting..."; const readyState = "Ready..."; const readySignalWaitState = "Ready [%1/%2]"; const startingState = "Starting [%1]"; const onlineState = "Online [%1]"; const offlineState = "Offline"; const offlineStateRCN = "Offline [%1]"; const endedState = "Ended"; const pausedState = "Paused"; const loginState = "Logging in"; const idleState = "Idle [%1]"; const mutedState = "Muted"; const protectedState = "Protected [%1]"; const deadState = "Dead"; const matchState = "Match [%1]"; const clientTabTxt = "Client"; const homeTabTxt = "Home"; const settingsTabTxt = "Settings"; const privateMessageTabTxt = "Private message"; const gameTabTxt = "Game"; const playersTabTxt = "Players"; const moderatorTabTxt = "Moderator"; const matchControlTabTxt = "Match control"; const matchSetupTabTxt = "Match setup"; const serverTabTxt = "Server"; const infoTabTxt = "Info"; const banControlTabTxt = "Ban control"; const accountsTabTxt = "Accounts"; const accountTypesTabTxt = "Account types"; const basicSettingsTabTxt = "Basic"; const nexgenSettingsTabTxt = "Nexgen"; const bootTabTxt = "Boot control"; const pluginsTabTxt = "Plugins"; const aboutTabTxt = "About"; const welcomeTxt = "Welcome %1, you are logged in as %2."; const rightsOverviewTxt = "The following privileges / rights are available to you on this server:"; const rightNotDefinedTxt = "Privilege %1 (not defined)."; const teamBalanceTxt = "Team balance"; const redTeamTxt = "Red"; const blueTeamTxt = "Blue"; const greenTeamTxt = "Green"; const goldTeamTxt = "Yellow"; const playTxt = "Play"; const spectateTxt = "Spectate"; const reconnectTxt = "Reconnect"; const disconnectTxt = "Disconnect"; const exitTxt = "Exit"; const mapVoteTxt = "Open mapvote"; const startTxt = "Start game"; const loginTxt = "Admin login"; const serverNameTxt = "Server name:"; const shortServerNameTxt = "Short server name:"; const MOTDLineTxt = "MOTD line %1"; const adminNameTxt = "Admin name:"; const adminEmailTxt = "Admin email:"; const serverPasswordTxt = "Server password:"; const adminPasswordTxt = "Admin password:"; const playerSlotsTxt = "Player slots:"; const vipSlotsTxt = "VIP slots:"; const adminSlotsTxt = "Admin slots:"; const specSlotsTxt = "Spectator slots:"; const advertiseTxt = "Advertise server:"; const resetTxt = "Reset"; const saveTxt = "Save"; const keyBindsTxt = "Keybinds"; const balanceBindTxt = "Balance teams"; const switchRedBindTxt = "Switch to red"; const switchBlueBindTxt = "Switch to blue"; const switchGreenBindTxt = "Switch to green"; const switchGoldBindTxt = "Switch to yellow"; const suicideBindTxt = "Suicide"; const openMapVoteBindTxt = "Open map vote"; const openCPBindTxt = "Open control panel"; const pauseGameBindTxt = "Pause game"; const UISettingsTxt = "User interface"; const enableMsgHUDTxt = "Enable Nexgen message HUD"; const msgFlashEffectTxt = "Enable message 'flash' effect"; const showPlayerLocationTxt = "Show player location on teamsay messages"; const pmSoundTxt = "Play a sound when a private message arrives"; const miscSettingsTxt = "Miscellaneous settings"; const autoSSNormalGameTxt = "Auto screenshot at the end of normal games"; const autoSSMatchTxt = "Auto screenshot at the end of matches"; const playerListTxt = "Players"; const blockedListTxt = "Blocked"; const blockToggleTxt = "Block / Unblock"; const blockAllPMsTxt = "Block all private messages"; const messageTxt = "Message"; const sendNormalPMTxt = "Send normal PM"; const sendWindowedPMTxt = "Send windowed PM"; const historyTxt = "History"; const blockMsg = "%1 has been blocked."; const unblockMsg = "%1 has been unblocked."; const sendMsgTxt = "Send to %1: %2"; const receivedMsgTxt = "%1: %2"; const accountNameTxt = "Account name"; const accountTitleTxt = "Account title"; const passwordTxt = "Password"; const addAccountTypeTxt = "Add account type"; const delAccountTypeTxt = "Delete account type"; const moveUpTxt = "Move up"; const moveDownTxt = "Move down"; const userNameTxt = "User name"; const accountTypeTxt = "Account type"; const userTitleTxt = "User title"; const onlineTxt = "Online"; const offlineTxt = "Offline"; const updateTxt = "Update"; const addTxt = "Add"; const deleteTxt = "Delete"; const customAccountTxt = ""; const switchToTeamTxt = "Switch to %1"; const sendToURLTxt = "Send to URL"; const reconnectAsPlayerTxt = "Reconnect as player"; const reconnectAsSpecTxt = "Reconnect as spectator"; const disableTeamSwitchTxt = "(Dis)allow team switch"; const pauseGameTxt = "Pause game"; const endGameTxt = "End game"; const restartGameTxt = "Restart game"; const allowTeamSwitchTxt = "Allow team switching"; const allowTeamBalanceTxt = "Allow team balancing"; const lockTeamsTxt = "Lock the game"; const tournamentModeTxt = "Tournament mode"; const playerNameTxt = "Player name"; const banReasonTxt = "Ban/kick reason"; const banPeriodTxt = "Ban period"; const banForeverTxt = "Forever"; const banMatchesTxt = "'x' matches"; const banDaysTxt = "'x' days"; const banUntilDateTxt = "Until 'date'"; const ipAddressesTxt = "IP addresses"; const clientIDsTxt = "Client IDs"; const addBanTxt = "Create new ban"; const updateBanTxt = "Update ban"; const delBanTxt = "Remove ban"; const removeTxt = "Remove"; const enableBootCtrlTxt = "Enable Nexgen boot control"; const restartOnLastGameTxt = "Restart game on last map"; const inclMutatorsTxt = "Mutators used"; const exclMutatorsTxt = "Mutators not used"; const bootCmdLineTxt = "Server boot command line"; const gameTypeTxt = "Game type"; const mapPrefixTxt = "Map prefix"; const extraCmdLineOptTxt = "Additional command line options"; const preSwitchCommandsTxt = "Pre switch server console commands"; const rebootTxt = "Reboot"; const administratorTxt = "Administrator"; const contactAddrTxt = "Contact address"; const msgOfTheDayTxt = "Message of the day"; const serverIDTxt = "Server ID"; const statisticsTxt = "Stats"; const totalGamesTxt = "Games hosted"; const totalFragsTxt = "Total frags"; const totalDeathsTxt = "Total deaths"; const totalFlagsTxt = "Total caps"; const bestPlayersTxt = "Top players"; const FPHTxt = "FPH"; const recordSetTxt = "Date"; const timeLimitTxt = "Time limit"; const scoreLimitTxt = "Score limit"; const teamScoreLimitTxt = "Team score limit"; const gameSpeedTxt = "Game speed"; const teamSwitchEnabledTxt = "Team switch enabled"; const teamBalanceEnabledTxt = "Team balancing enabled"; const teamsLockedTxt = "Teams are locked"; const nameChangeAllowedTxt = "Name change allowed"; const mutatorsTxt = "Mutators"; const levelTxt = "Level"; const fileTxt = "File"; const titleTxt = "Title"; const authorTxt = "Author"; const idealPlayerCountTxt = "Players"; const matchSettingsTxt = "Match settings"; const matchNumOfGamesTxt = "Number of games"; const matchCurrGameNumTxt = "Current game"; const matchSpecNoPassTxt = "Allow spectators to enter the game without a password"; const matchMuteSpecsTxt = "Mute spectators during the game"; const matchBootControlTxt = "Switch back to last map when the server has crashed"; const matchAutoLockTeamsTxt = "Automatically lock teams at the beginning of each game"; const matchAutoPauseTxt = "Automatically pause the game when a player leaves"; const matchSeparateByTagTxt = "Separate players by tag"; const matchAutoTagSeparateTxt = "Automatically separate by tag"; const matchDoSeparateTxt = "Separate now"; const startMatchTxt = "Start match"; const stopMatchTxt = "Stop match"; const sendPasswordTxt = "Send password"; const allPlayersTxt = ""; const clientIDTxt = "Client ID"; const ipAddressTxt = "IP address"; const muteToggleTxt = "(Un)mute"; const setPlayerNameTxt = "Set name"; const kickPlayerTxt = "Kick"; const banPlayerTxt = "Ban"; const muteAllTxt = "Mute all players"; const allowNameChangeTxt = "Allow players to change their name"; const showAdminMessageTxt = "Show message"; const copyTxt = "Copy"; const nexgenMiscSettingsPanelTitle = "General Nexgen settings"; const autoUpdateBansTxt = "Automatically update ban entries"; const autoDelExpiredBansTxt = "Automatically remove expired ban entries"; const announceTeamKillsTxt = "Broadcast team kill attempts"; const restoreScoreOnTeamSwitchTxt = "Don't decrease score on team switch"; const enableNexgenStartControlTxt = "Let Nexgen handle the game start"; const broadcastAdminActionsTxt = "Broadcast administrator actions to all players"; const enableNexgenMessageHUDTxt = "Enable the Nexgen message HUD"; const defaultAllowTeamSwitchTxt = "Team switch allowed by default"; const defaultAllowTeamBalanceTxt = "Team balance allowed by default"; const defaultAllowNameChangeTxt = "Name change allowed by default"; const autoRegisterServerTxt = "Register server in Nexgen database"; const gameWaitTimeTxt = "Game wait time (sec)"; const gameStartDelayTxt = "Game start delay (sec)"; const autoReconnectTimeTxt = "Auto reconnect time (sec)"; const maxIdleTimeTxt = "Max idle time (sec)"; const maxIdleTimeCPTxt = "Max idle time in control panel (sec)"; const spawnProtectTimeTxt = "Spawn protect time (sec)"; const teamKillDmgProtectTxt = "Team kill damage protect time (sec)"; const teamKillPushProtectTxt = "Team kill push protect time (sec)"; const autoDisableMatchTimeTxt = "Auto disable match mode time (min)"; const ignoredWeaponsTxt = "Weapons ignored by spawn protector."; const weaponClassTxt = "Weapon class"; const ignorePrimaryFireTxt = "Ignore primary fire"; const ignoreAltFireTxt = "Ignore alternate fire"; const hudReplaceClassesTxt = "HUD replacement classes."; const originalHUDClassTxt = "Original HUD class"; const replacementHUDClassTxt = "Replacement HUD class"; const addNewItemTxt = ""; const logSettingsPanelTitle = "Log settings"; const logToConsoleTxt = "Write log messages to console (stdout)"; const logToFileTxt = "Write log messages to file"; const logEventsTxt = "Write Nexgen events to the server log"; const logFilePathTxt = "Log path"; const logFileExtensionTxt = "Log extension"; const logFileNameFormatTxt = "Log file name"; const logTimeStampFormatTxt = "Time format"; const logMessagesTxt = "Write system messages to the server log"; const logChatMessagesTxt = "Write chat messages to the server log"; const logPrivateMessagesTxt = "Write private messages to the server log"; const logAdminActionsTxt = "Write administrator actions to the server log"; /*************************************************************************************************** * * $DESCRIPTION Returns the name of the team specified by the given number. * $PARAM team Number of the team whose name is to be returned. * $RETURN The name of the team, or ? if an invalid team is specified. * **************************************************************************************************/ static function string getTeamName(int team) { switch (team) { case 0: return "red"; case 1: return "blue"; case 2: return "green"; case 3: return "yellow"; } } /*************************************************************************************************** * * $DESCRIPTION Formats the given string by inserting the specified strings into the proper * positions. The positions are indicated by the "%n" tags, where n is number of the * string to insert. * $PARAM source The string that is to be formatted. * $PARAM str1 String number 1 to insert. * $PARAM str2 String number 2 to insert. * $PARAM str3 String number 3 to insert. * $PARAM str4 String number 4 to insert. * $RETURN The formatted string. * **************************************************************************************************/ static function string format(string source, optional coerce string str1, optional coerce string str2, optional coerce string str3, optional coerce string str4) { return class'NexgenUtil'.static.format(source, str1, str2, str3, str4); } /*************************************************************************************************** * * $DESCRIPTION Converts a ban period string into a human readable format. * $PARAM banPeriodStr The string describing the ban period. * $RETURN A string describing (in words) how long the ban lasts. * **************************************************************************************************/ function string getBanPeriodDescription(string banPeriodStr) { local string description; if (banPeriodStr == "") { description = "forever"; } else if (left(banPeriodStr, 1) ~= "M") { description = "for" @ mid(banPeriodStr, 1) @ "matches"; } else if (left(banPeriodStr, 1) ~= "U") { description = "until" @ getLocalizedDateStr(mid(banPeriodStr, 1)); } else { description = "forever"; } return description; } /*************************************************************************************************** * * $DESCRIPTION Returns a compact date description string for the specified date. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE (1 <= month && moth <= 12) && (1 <= day && day <= 31) && * (0 <= hour && hour <= 23) && (0 <= minute && minute <= 59) * $RETURN The date description string for the given date. * **************************************************************************************************/ static function string getCompactDateStr(int year, int month, int day, int hour, int minute) { return class'NexgenUtil'.static.lfill(day, 2, "0") $ "/" $ class'NexgenUtil'.static.lfill(month, 2, "0") $ "/" $ class'NexgenUtil'.static.lfill(year, 4, "0") $ " " $ class'NexgenUtil'.static.lfill(hour, 2, "0") $ ":" $ class'NexgenUtil'.static.lfill(minute, 2, "0"); } /*************************************************************************************************** * * $DESCRIPTION Parses the given date string. * $PARAM dateStr The date string to parse. * $PARAM year Year of the specified date. * $PARAM month Month of the specified date. * $PARAM day Day of the specified date. * $PARAM hour Hour of the specified date. * $PARAM minute Minute of the specified date. * $REQUIRE 'dateStr follows dateFormatStr' * $RETURN True if the specified date string was valid, false if not. When false is returned * the outcome (the date) should be ignored. * **************************************************************************************************/ static function bool parseDate(string dateStr, out int year, out int month, out int day, out int hour, out int minute) { local bool bValid; local string remaining; local int index; bValid = true; remaining = class'NexgenUtil'.static.trim(dateStr); // Parse day. index = instr(remaining, "/"); if (index >= 0) { day = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } // Parse month. if (bValid) { index = instr(remaining, "/"); if (index >= 0) { month = int(left(remaining, index)); remaining = class'NexgenUtil'.static.trim(mid(remaining, index + 1)); } else { bValid = false; } } // Parse year. if (bValid) { index = instr(remaining, " "); if (index >= 0) { year = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse hour. if (bValid) { index = instr(remaining, ":"); if (index >= 0) { hour = int(left(remaining, index)); remaining = mid(remaining, index + 1); } else { bValid = false; } } // Parse minute. if (bValid) { minute = int(remaining); } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Converts the given date string into a localized format. * $PARAM dateStr The date string to convert. * $RETURN The converted date into a localized format. * **************************************************************************************************/ function string getLocalizedDateStr(string dateStr) { local bool bValid; local int year, month, day, hour, minute; bValid = class'NexgenUtil'.static.readDate(dateStr, year, month, day, hour, minute); if (bValid) { return getCompactDateStr(year, month, day, hour, minute); } else { return dateStr; } } /*************************************************************************************************** * * $DESCRIPTION Converts the given localized date string into a standard format. * $PARAM dateStr The date string to convert. * $RETURN The converted date into a standard format. * **************************************************************************************************/ function string getDelocalizedDateStr(string dateStr) { local bool bValid; local int year, month, day, hour, minute; bValid = parseDate(dateStr, year, month, day, hour, minute); if (bValid) { return class'NexgenUtil'.static.serializeDate(year, month, day, hour, minute); } else { return dateStr; } } /*************************************************************************************************** * * $DESCRIPTION Returns the current date in the specified format. * $PARAM The format of the date. * $RETURN The current date in the the specified format. * **************************************************************************************************/ function string getCurrentDate(string format) { return getDate(format, level.year, level.month, level.day, level.dayOfWeek, level.hour, level.minute, level.second); } /*************************************************************************************************** * * $DESCRIPTION Returns given date in the specified format. * $PARAM The format of the date. * $RETURN The given date in the the specified format. * **************************************************************************************************/ static function string getDate(string format, int year, int month, int day, int dayOfWeek, int hour, int minute, int second) { local string dateStr; local int value; local string strValue; dateStr = format; // Year. if (instr(dateStr, "%Y") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%Y", class'NexgenUtil'.static.lfill(year, 4, "0")); } if (instr(dateStr, "%y") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%y", class'NexgenUtil'.static.lfill(year, 2, "0", 2)); } if (instr(dateStr, "%L") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%L", byte(class'NexgenUtil'.static.isLeapYear(year))); } // Month. if (instr(dateStr, "%m") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%m", class'NexgenUtil'.static.lfill(month, 2, "0")); } if (instr(dateStr, "%n") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%n", month); } if (instr(dateStr, "%F") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%F", getMonthName(month)); } if (instr(dateStr, "%M") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%M", getShortMonthName(month)); } if (instr(dateStr, "%t") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%t", class'NexgenUtil'.static.daysInMonth(year, month)); } // Day. if (instr(dateStr, "%d") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%d", class'NexgenUtil'.static.lfill(day, 2, "0")); } if (instr(dateStr, "%j") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%j", day); } if (instr(dateStr, "%w") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%w", dayOfWeek); } if (instr(dateStr, "%N") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%N", (dayOfWeek + 1)); } if (instr(dateStr, "%l") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%l", getDayName(dayOfWeek)); } if (instr(dateStr, "%D") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%D", getShortDayName(dayOfWeek)); } // Hour. if (instr(dateStr, "%H") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%H", class'NexgenUtil'.static.lfill(hour, 2, "0")); } if (instr(dateStr, "%h") >= 0) { value = hour % 12; if (value == 0) value = 12; dateStr = class'NexgenUtil'.static.replace(dateStr, "%h", class'NexgenUtil'.static.lfill(value, 2, "0")); } if (instr(dateStr, "%G") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%G", hour); } if (instr(dateStr, "%g") >= 0) { value = hour % 12; if (value == 0) value = 12; dateStr = class'NexgenUtil'.static.replace(dateStr, "%g", value); } if (instr(dateStr, "%a") >= 0) { if (hour == 0 || hour > 12) { strValue = "am"; } else { strValue = "pm"; } dateStr = class'NexgenUtil'.static.replace(dateStr, "%a", strValue); } if (instr(dateStr, "%A") >= 0) { if (hour == 0 || hour > 12) { strValue = "AM"; } else { strValue = "PM"; } dateStr = class'NexgenUtil'.static.replace(dateStr, "%A", strValue); } // Minute. if (instr(dateStr, "%i") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%i", class'NexgenUtil'.static.lfill(minute, 2, "0")); } // Second. if (instr(dateStr, "%s") >= 0) { dateStr = class'NexgenUtil'.static.replace(dateStr, "%s", class'NexgenUtil'.static.lfill(second, 2, "0")); } return dateStr; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the full textual representation of the specified day of the week. * $PARAM day The number of day of the week whose name is to be retrieved. * $REQUIRE 0 <= day && day <= 6 * $RETURN A full textual representation of the day of the week. * $ENSURE result != "" * **************************************************************************************************/ static function string getDayName(int day) { switch (day) { case 0: return "Sunday"; case 1: return "Monday"; case 2: return "Tuesday"; case 3: return "Wednesday"; case 4: return "Thursday"; case 5: return "Friday"; case 6: return "Saturday"; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the short textual representation of the specified day of the week, three * letters. * $PARAM day The number of day of the week whose name is to be retrieved. * $REQUIRE 0 <= day && day <= 6 * $RETURN A short textual representation of the day of the week. * $ENSURE len(result) == 3 * **************************************************************************************************/ static function string getShortDayName(int day) { switch (day) { case 0: return "Sun"; case 1: return "Mon"; case 2: return "Tue"; case 3: return "Wed"; case 4: return "Thu"; case 5: return "Fri"; case 6: return "Sat"; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the full textual representation of the specified month, such as January * or March. * $PARAM month The number of the month whose name is to be retrieved. * $REQUIRE 1 <= month && month <= 12 * $RETURN A full textual representation of the specified month. * $ENSURE result != "" * **************************************************************************************************/ static function string getMonthName(int month) { switch (month) { case 1: return "January"; case 2: return "February"; case 3: return "March"; case 4: return "April"; case 5: return "May"; case 6: return "June"; case 7: return "July"; case 8: return "August"; case 9: return "September"; case 10: return "October"; case 11: return "November"; case 12: return "December"; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the short textual representation of the specified month, three letters. * $PARAM month The number of the month whose short name is to be retrieved. * $REQUIRE 1 <= month && month <= 12 * $RETURN A short textual representation of the specified month. * $ENSURE len(result) == 3 * **************************************************************************************************/ static function string getShortMonthName(int month) { switch (month) { case 1: return "Jan"; case 2: return "Feb"; case 3: return "Mar"; case 4: return "Apr"; case 5: return "May"; case 6: return "Jun"; case 7: return "Jul"; case 8: return "Aug"; case 9: return "Sep"; case 10: return "Oct"; case 11: return "Nov"; case 12: return "Dec"; } } rD "team"uD "title"lDS#L K S#-P!e~K O!Pe%-P!'t pt K t ppt K eR#K K e}O! t   vD "Switch to blue"xD "name"}D "Switch to red"P!@KE "ip"c /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenJustBannedDialog * $VERSION 1.00 (17-11-2007 18:34) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player just got kicked / banned from the server. * **************************************************************************************************/ class NexgenJustBannedDialog extends NexgenBannedDialog; /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ H"yDK 2zD/H"& V].-You have been kicked/banned from the server.]RAn administrator has just kicked or banned you from the server. This means you are probably not welcome for the time being. Please go play somewhere else, there are enough of other servers and games out there.L/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenImageControl * $VERSION 1.00 (10-03-2007 22:54) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Image GUI component. * **************************************************************************************************/ class NexgenImageControl extends UWindowDialogControl; var Texture image; // Image to display. var bool bStretch; // Whether to stretch the image if the dimensions do not match. /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { local float xPos; local float yPos; if (image != none) { if (bStretch) { drawStretchedTexture(c, 0, 0, winWidth, winHeight, image); } else { xPos = int((winWidth - image.uSize) / 2.0); yPos = int((winHeight - image.vSize) / 2.0); drawClippedTexture(c, xPos, yPos, image); } } } sDQ# vpvQ#%1P#vv%2O#vv%3N#vv%4M#v  ~D "Balance teams"D "Keybinds"@E "Save"AE "Reset"EE "Advertise server:"v@\/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenIllegalLoginDialog * $VERSION 1.00 (2-8-2008 14:26) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the players client ID is already in use. * **************************************************************************************************/ class NexgenIllegalLoginDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ |DL# fSL#=Z}SzSZ& Z SSZZ}S&Z%zSZ& Z_SSZ&S  JE "Spectator slots:"Z@CCEB@I 2GE3Cn V])(Failed to login: illegal login attempt.]uYour client has send illegal login information to the server. This is most likely the result of a modified/hacked Nexgen package, therefore you are not allowed to enter the server.e"Ok,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenIDUsedDialog * $VERSION 1.01 (20-1-2008 12:58) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the players client ID is already in use. * **************************************************************************************************/ class NexgenIDUsedDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reconnectText; // Text to display on the reconnect button. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ DEK#7m|K#1}|S!|pJ#| ecS!}|c||c|  LE "Admin slots:"ME "id"QE "VIP slots:"RE "ac"|@CHE|?H 2OECnŔ> Y V],+Failed to login: client ID already in use.mUYour client ID appears to be already in use by another player. This is probably caused because you just left the server and the server wasn't able to receive your logout notification. This can also be caused by someone that has copied your Unreal Tournament installation or by someone that has acquired your Nexgen key. If you continue to experience this problem contact the administrator of this server.i]Retrye"OIEI#uAmI#1}^!pH# ea^!}aa  SE "Player slots:"TE "pl"XE "Admin password:"YE "pj"@l,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenIdleKickedDialog * $VERSION 1.00 (13-10-2007 17:54) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player is kicked for being idle on the server. * **************************************************************************************************/ class NexgenIdleKickedDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reconnectText; // Text to display on the reconnect button. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ PEG#Xv" L G#\~L H\%{L\NSC(L L\\~L \%c!L'c!L\LpL\ \%QY \}L d!, fL\&3zf 0Q% -]Y \zf"dQ%Q\&-]'{h!\Y \-](Q%Q\Y %d!F#LQY Q\""QY h!f\'  ZE "Server password:"^E ","gE "Admin email:"LCVEr?G 2\E=CnŔ> Y V]! You are kicked from the server.]ywThe server has disconnected your client because you were idle for too long. You can reconnect as soon as you get back.i] Reconnecte"OI{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxTDM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended TDM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxTDM extends ChallengeTeamHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } `E "Botpack.CHSpectator"]EL0F 6 _E9^>:>&:>&ElElEl:>&u&OcE "Mutate NSC START"O{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxDOM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended DOM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxDOM extends ChallengeDominationHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } aEI3E 6 bELUO4:>&:>&ElElEl:>&u&OfE "Exit"WEg[EV+g[E#,p\,Bzggppi!=[egppppg,i!=[  B{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxDM * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended DM HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxDM extends ChallengeHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } hE "Disconnect"jE "Admin name:"kE "Reconnect"dED#HcY0copD#,w~o,9-Y w%w}\%zow}\}\\~pp~ow}\,oow},w~o,6~p~ow|~}Z}=pZ=-Y'6~oow},w~o, [-Y~}Z}=aC#  lE "MOTD line %1"oE 2sE "Short server name:"w@eEV,D 6 nE6&^!:>&:>&ElElEl:>&u&O@F 1H{/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxCTF * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended CTF HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxCTF extends ChallengeCTFHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } pEP1C 6 qE?:>&:>&ElElEl:>&u&OiEB#_a7ZB#<ZcEZcEE ZPEUZPEUU, U&E<ZPEUhhZPEUhU, U&EhZ  yE "Server name:"~z/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDxAS * $VERSION 1.01 (31-12-2006 16:01) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended AS HUD that uses the Nexgen HUD to render the messages. * **************************************************************************************************/ class NexgenHUDxAS extends AssaultHUD; struct SavedMessage { // Structure used for buffered messages. var string msg; // Message string. var name msgType; // Type of the message. var PlayerReplicationInfo pri1; // Related player information 1. var PlayerReplicationInfo pri2; // Related player information 2. }; var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). var float lastCheck; // Last time we checked if the NexgenHUD is available. var SavedMessage savedMessages[10]; // Messages received before the NexgenHUD was available. var int savedMsgCount; // Number of messages saved. /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the parent class * and the renderMessageBox() function on the NexgenHUD if it is available. When the * NexgenHUD is not available an attempt is made to locate it. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function postRender(Canvas c) { super.postRender(c); if (nscHUD == none) { setNexgenHUD(); } else { nscHUD.renderMessageBox(c); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { super.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { super.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the NexgenHUD instance. Once the HUD has been located any saved * messages will be restored. To save processing resources the attemps will have a * minimum interval of 0.2 seconds. * $REQUIRE nscHUD == none * $ENSURE nscHUD != none ? savedMsgCount == 0 : true * **************************************************************************************************/ simulated function setNexgenHUD() { local int index; if (level.timeSeconds - lastCheck >= 0.2) { // Locate NexgenHUD instance. lastCheck = level.timeSeconds; foreach allActors(class'NexgenHUD', nscHUD) { if (nscHUD != none) { break; } } // Restore saved messages. if (nscHUD != none) { for (index = 0; index < savedMsgCount; index++) { nscHUD.message(savedMessages[index].msg, savedMessages[index].msgType, savedMessages[index].pri1, savedMessages[index].pri2); } savedMsgCount = 0; } } } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int index; if (nscHUD == none) { // Buffer the message. if (savedMsgCount < arrayCount(savedMessages)) { index = savedMsgCount; savedMsgCount++; } else { for (index = 1; index < savedMsgCount; index++) { savedMessages[index - 1] = savedMessages[index]; } savedMsgCount = savedMsgCount - 1; } savedMessages[index].msg = msg; savedMessages[index].msgType = msgType; savedMessages[index].pri1 = pri1; savedMessages[index].pri2 = pri2; } else { // Pass it to the Nexgen HUD. nscHUD.message(msg, msgType, pri1, pri2); } } tEy6B 6 uEH:>&:>&ElElEl:>&u&OB/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUDWrapper * $VERSION 1.02 (6-4-2008 11:46) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION HUD Wrapper class. Used to support the Nexgen message HUD with custom HUDs. This * class simply passes all calls to the original HUD, except in the cases where the * Nexgen message HUD kicks in. * **************************************************************************************************/ class NexgenHUDWrapper extends ChallengeHUD; var ChallengeHUD originalHUD; // Original HUD. var NexgenHUD nscHUD; // The Nexgen HUD instance (which will do the rendering). /*************************************************************************************************** * * $DESCRIPTION Initializes the HUD wrapper. * $PARAM originalHUD The original HUD that was used by the player. * $PARAM nscHUD The Nexgen message HUD. * $REQUIRE originalHUD != none && nscHUD != none * **************************************************************************************************/ function initialize(ChallengeHUD originalHUD, NexgenHUD nscHUD) { local int index; // Copy control variables. self.originalHUD = originalHUD; self.nscHUD = nscHUD; playerOwner = originalHUD.playerOwner; // Clear messages. for (index = 0; index < arrayCount(shortMessageQueue); index++) { originalHUD.message(none, "", ''); } for (index = 0; index < arrayCount(localMessages); index++) { originalHUD.localizedMessage(class'NexgenNullMessage'); } } /*************************************************************************************************** * * $DESCRIPTION Prepares the system for the HUD render phase. Probably never used. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated event preRender(Canvas c) { if (originalHUD != none) originalHUD.preRender(c); } /*************************************************************************************************** * * $DESCRIPTION Renders the HUD. It will simply call the postRender() function of the original HUD * and also calls the renderMessageBox() function on the NexgenHUD. * $PARAM c The canvas object on which the rendering will be done. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated event postRender(Canvas c) { local Console cons; local bool bWasTyping; if (originalHUD != none) { // Copy HUD settings. originalHUD.bHideHUD = bHideHUD; originalHUD.bHideAllWeapons = bHideAllWeapons; originalHUD.bHideStatus = bHideStatus; originalHUD.bHideAmmo = bHideAmmo; originalHUD.bHideTeamInfo = bHideTeamInfo; originalHUD.bHideFrags = bHideFrags; originalHUD.bUseTeamColor = bUseTeamColor; originalHUD.opacity = opacity; originalHUD.hudScale = hudScale; originalHUD.weaponScale = weaponScale; originalHUD.statusScale = statusScale; originalHUD.favoriteHUDColor = favoriteHUDColor; originalHUD.crosshairColor = CrosshairColor; originalHUD.hudMutator = hudMutator; // Save data & fool the original HUD. cons = playerOwner.player.console; bWasTyping = cons.bTyping; cons.bTyping = false; originalHUD.bHideFaces = true; // Let the original HUD render it's stuff. originalHUD.postRender(c); // Restore data. cons.bTyping = bWasTyping; } nscHUD.renderMessageBox(c); } /*************************************************************************************************** * * $DESCRIPTION Called when the player switches to the specified weapon group. Doesn't appear to * be used anywhere. * $PARAM f The number of the weapon group to which the player is about to switch. * $OVERRIDE * **************************************************************************************************/ simulated function inputNumber(byte f) { if (originalHUD != none) originalHUD.inputNumber(f); } /*************************************************************************************************** * * $DESCRIPTION Changes the HUD mode. Not used in Unreal Tournament. * $PARAM d Number to add to the HUD mode number (delta). * $OVERRIDE * **************************************************************************************************/ simulated function changeHud(int d) { if (originalHUD != none) originalHUD.changeHud(d); } /*************************************************************************************************** * * $DESCRIPTION Changes the crosshair to use. * $PARAM d Number to add to the crosshair number (delta). * $OVERRIDE * **************************************************************************************************/ simulated function changeCrosshair(int d) { if (originalHUD != none) originalHUD.changeCrosshair(d); } /*************************************************************************************************** * * $DESCRIPTION Renders the crosshair on the screen. * $PARAM c The canvas object on which the rendering will be done. * $PARAM startX Horizontal offset. * $PARAM startX Vertical offset. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ simulated function drawCrossHair(Canvas c, int startX, int startY) { if (originalHUD != none) originalHUD.drawCrossHair(c, startX, startY); } /*************************************************************************************************** * * $DESCRIPTION Displays a new message on the screen. * $PARAM pri Information about the player that is related with this message. * $PARAM msg String containing the message to be displayed. * $PARAM msgType Type of the message. * $OVERRIDE * **************************************************************************************************/ simulated function message(PlayerReplicationInfo pri, coerce string msg, name msgType) { local bool bHandleByParent; // Check class responsible for this message. bHandleByParent = msgType == 'CriticalEvent'; // Handle message. if (bHandleByParent) { if (originalHUD != none) originalHUD.message(pri, msg, msgType); } else { addMessage(msg, msgType, pri, none); } } /*************************************************************************************************** * * $DESCRIPTION Displays a new localized message on the screen. * $PARAM message Class used to construct the localized message string. * $PARAM switch Message selection switch number. * $PARAM relatedPRI_1 First player that is related to this message. * $PARAM relatedPRI_2 Second player that is related to this message. * $PARAM optionalObject Object involved in the construction of the message string. * $PARAM criticalString Critical message string. * $OVERRIDE * **************************************************************************************************/ simulated function localizedMessage(class message, optional int switch, optional PlayerReplicationInfo relatedPRI_1, optional PlayerReplicationInfo relatedPRI_2, optional Object optionalObject, optional string criticalString) { local bool bHandleByParent; local string msgStr; // Check class responsible for this message. bHandleByParent = message.default.bIsSpecial; // Handle message. if (bHandleByParent) { if (originalHUD != none) originalHUD.localizedMessage(message, switch, relatedPRI_1, relatedPRI_2, optionalObject, criticalString); } else { // Get message string. if (message.default.bComplexString) { msgStr = criticalString; } else { msgStr = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); } // Add the message. addMessage(msgStr, '', relatedPRI_1, relatedPRI_2); } } /*************************************************************************************************** * * $DESCRIPTION Displays the specified message. Doesn't appear to be used. * $PARAM s The message to display. * $PARAM pName Name of the player that created the message? * $PARAM pZone Location of the player that created the message? * $OVERRIDE * **************************************************************************************************/ simulated function playReceivedMessage(string s, string pName, ZoneInfo pZone) { if (originalHUD != none) originalHUD.playReceivedMessage(s, pName, pZone); } /*************************************************************************************************** * * $DESCRIPTION Checks whether this HUD is responsible for displaying messages. * $RETURN True if this HUD handles the messages instead of the console. * $ENSURE result == true * $OVERRIDE * **************************************************************************************************/ simulated function bool displayMessages(Canvas c) { return true; } /*************************************************************************************************** * * $DESCRIPTION Called by the engine when a key, mouse, or joystick button is pressed or released, * or any analog axis movement is processed. * $PARAM key The key that was involved. * $PARAM action The action that was taken on the involved key. * $PARAM delta Amount of time elapsed/used for the action. * $OVERRIDE * **************************************************************************************************/ function bool processKeyEvent(int key, int action, float delta) { if (originalHUD != none) return originalHUD.processKeyEvent(key, action, delta); return false; } /*************************************************************************************************** * * $DESCRIPTION Sets the location where the player took damage. * $PARAM hitLoc The location where the player was hit. * $PARAM damage The amount of damage taken by the player. * $OVERRIDE * **************************************************************************************************/ function setDamage(vector hitLoc, float damage) { if (originalHUD != none) originalHUD.setDamage(hitLoc, damage); } /*************************************************************************************************** * * $DESCRIPTION Draws the typing prompt on the specified canvas. * $PARAM canv The canvas on which the typing prompt should be rendered. * $PARAM cons Console containing the typed message. * $OVERRIDE * **************************************************************************************************/ simulated function drawTypingPrompt(Canvas canv, Console cons) { // Suppress! This is taken care of in NexgenHUD.renderMessageBox(). } /*************************************************************************************************** * * $DESCRIPTION Adds a new message to display. If the message can't be added directly because the * NexgenHUD object hasn't been set, the message will be buffered. * $PARAM msg String containing the message to add. * $PARAM msgType Type of the message. * $PARAM pri1 First player that is related to this message. * $PARAM pri2 Second player that is related to this message. * **************************************************************************************************/ simulated function addMessage(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { if (nscHUD != none) nscHUD.message(msg, msgType, pri1, pri2); } /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHUD * $VERSION 1.19 (6-4-2008 16:31) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen HUD extension class. * **************************************************************************************************/ class NexgenHUD extends Mutator; #exec OBJ LOAD FILE=..\Textures\LadrStatic.utx PACKAGE=Botpack.LadrStatic #exec TEXTURE IMPORT NAME=base FILE=Resources\base.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=grad32 FILE=Resources\grad32.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=grad64 FILE=Resources\grad64.pcx GROUP="GFX" FLAGS=2 MIPS=OFF #exec TEXTURE IMPORT NAME=playerIcon FILE=Resources\PlayerIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=playerIcon2 FILE=Resources\PlayerIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=serverIcon FILE=Resources\ServerIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=serverIcon2 FILE=Resources\ServerIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=offlineIcon FILE=Resources\OfflineIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=offlineIcon2 FILE=Resources\OfflineIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=waitIcon FILE=Resources\WaitIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=waitIcon2 FILE=Resources\WaitIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=specIcon FILE=Resources\SpecIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=specIcon2 FILE=Resources\SpecIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=shieldIcon FILE=Resources\ShieldIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=shieldIcon2 FILE=Resources\ShieldIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=idleIcon FILE=Resources\IdleIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=idleIcon2 FILE=Resources\IdleIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=mutedIcon FILE=Resources\MutedIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=mutedIcon2 FILE=Resources\MutedIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=matchIcon FILE=Resources\MatchIcon.pcx GROUP="GFX" FLAGS=3 MIPS=OFF #exec TEXTURE IMPORT NAME=matchIcon2 FILE=Resources\MatchIcon2.pcx GROUP="GFX" FLAGS=3 MIPS=OFF var NexgenClient client; // Client owning this HUD. var float lastSetupTime; // Last time setup() was called. var Font baseFont; // Base font to use for rendering text. var Color blankColor; // White color (#FFFFFF). var Color baseHUDColor; // Base color of the HUD (background color). var Color baseColors[6]; // Base colors available for the HUD. var Color colors[11]; // List of colors for text. var float baseFontHeight; // Height of the base font. struct MessageInfo { // Structure for storing message information. var string text[5]; // Message text list. var int col[5]; // Message text colors. var float timeStamp; // Time at which the message was received. }; struct PanelInfo { // Panel info container structure. var string text; // Text displayed on the panel. var Color textCol; // Color of the text to display. var Texture icon; // Icon displayed in front of the text. var Texture solidIcon; // Solid version of the icon. var bool bBlink; // Indicates if the text should 'blink'. }; var MessageInfo chatMessages[3]; // List of chat messages. var MessageInfo messages[5]; // List of all other messages. var int chatMsgCount; // Number of chat messages stored in the list. var int msgCount; // Number of other messages stored. var Texture faceImg; // Face texture to display in the chat message box. var float msgBoxWidth; // Width of the message box. var float msgBoxLineHeight; // Height of a line in the message box. var float msgBoxHeight; // Total height of the message box. var float minPanelWidth; // Minimum size of the panel. var bool bFlashMessages; // Whether new messages should flash. var float lastResX; // Horizontal resolution at last setup call. var float lastResY; // Vertical resolution at last setup call. var byte lastTeam; // Team number at last setup call. var float lastLevelTimeSeconds; // Last value of level.time seconds. var float timeSeconds; // Gamespeed independent timeSeconds, that keeps counting // even when the game is paused. var bool bShowPlayerLocation; // Show player location name in teamsay messages? const iconSize = 24.0; // Size of the status icons. const chatMessageLifeTime = 16.0; // The amount of time a chat message is diplayed (in sec). const messageLifeTime = 12.0; // Amount of time any other message is displayed (in sec). const messageBlinkTime = 4.0; // How long a message is highlighted (in sec). const messageBlinkRate = 4.0; // Message highlight blink rate (in Hz). const panelBlinkRate = 2.0; // Panel text highlight blink rate (in Hz). const blinkColorDamp = 0.70; // Dampening factor for blinking text. const connectionAlertDelay = 2; // Time to wait before showing the bad connection alert. const secPerMinute = 60; // Number of seconds per minute -- duh! const matchInfoSwitchTime = 4; // Number of seconds to wait before switching between time // remaining and match num. // Alert window settings. const hDefBarSize = 20; // Animated horizontal bar size. const alertAnimTime = 0.5; // Animation cycle time. const alertAnimDist = 64; // Distance the animated bar moves away from the window. const alertBorderSize = 32; // Distance between borders and text of the window. const newLineToken = "\\n"; // Token used to break the text in multiple lines. // Colors. const C_RED = 0; const C_BLUE = 1; const C_GREEN = 2; const C_YELLOW = 3; const C_WHITE = 4; const C_BLACK = 5; const C_PINK = 6; const C_CYAN = 7; const C_METAL = 8; const C_ORANGE = 9; // Server states. const SS_Offline = 'ssoffline'; // Connection with server is failing. const SS_Waiting = 'sswaiting'; // Server is waiting for players. const SS_Ready = 'ssready'; // Game is ready to start. const SS_Starting = 'ssstaring'; // Game is counting down to start. const SS_Ended = 'ssended'; // Game is ended. const SS_Paused = 'sspaused'; // Game is paused. const SS_Match = 'ssmatch'; // A match is in progress. const SS_Normal = 'ssnormal'; // A normal game is in progress. // Client states. const CS_Login = 'cslogin'; // Client is logging in. const CS_Idle = 'csidle'; // Client is idle or camping. const CS_Protected = 'csprotected'; // Client is (damage) protected. const CS_Muted = 'csmuted'; // Client is muted. const CS_Dead = 'csdead'; // Client is dead. const CS_Normal = 'csnormal'; // Client is in normal state. /*************************************************************************************************** * * $DESCRIPTION Initializes the extended HUD. * $REQUIRE owner != none && owner.isA('NexgenClient') * $ENSURE client != none * $OVERRIDE * **************************************************************************************************/ function postBeginPlay() { client = NexgenClient(owner); registerHUDMutator(); bFlashMessages = client.gc.get(client.SSTR_FlashMessages, "true") ~= "true"; bShowPlayerLocation = client.gc.get(client.SSTR_ShowPlayerLocation, "true") ~= "true"; } /*************************************************************************************************** * * $DESCRIPTION Renders the extended Nexgen HUD. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * $OVERRIDE * **************************************************************************************************/ function postRender(Canvas c) { local float badConnectionTime; local int timeRemaining; // Let other HUD mutators do their job first. if (nextHUDMutator != none) { nextHUDMutator.postRender(c); } // Render alerts. setup(c); badConnectionTime = client.timeSeconds - client.badConnectionSince; if (client.bBadConnectionDetected && client.sConf.autoReconnectTime > 0 && (badConnectionTime > connectionAlertDelay || client.gInf.rebootCountDown > 0)) { // Connection lost alert. timeRemaining = client.sConf.autoReconnectTime - badConnectionTime + 1; if (timeRemaining > 0) { renderAlert(c, client.lng.format(client.lng.autoReconnectAlert, timeRemaining), colors[C_RED], colors[C_RED]); } else { renderAlert(c, client.lng.reconnectingAlert, colors[C_RED], colors[C_RED]); } } else if (client.gInf.rebootCountDown > 0) { // Reboot warning. timeRemaining = client.gInf.rebootCountDown; renderAlert(c, client.lng.format(client.lng.rebootAlert, timeRemaining), colors[C_WHITE], baseHUDColor); } else if (client.idleTimeRemaining >= 0 && client.idleTimeRemaining <= client.idleTimeWarning) { // Idle warning. timeRemaining = client.idleTimeRemaining; renderAlert(c, client.lng.format(client.lng.idleAlert, timeRemaining), colors[C_WHITE], baseHUDColor); } } /*************************************************************************************************** * * $DESCRIPTION Renders an alert window. * $PARAM msg The message to display on the alert window. * $PARAM textColor Color of the text displayed on the alert window. * $PARAM baseColor Background color of the alert window. * **************************************************************************************************/ simulated function renderAlert(Canvas c, string msg, color textColor, color baseColor) { local int windowWidth; local int windowHeight; local int cx, cy; local float animIndex; local int dist; local int hBarSize; local float textWidth; local int maxTextWidth; local float textHeight; local int lineCount; local string remaining; local int index; local string text; // Initialize. c.font = ChallengeHUD(c.viewport.actor.myHUD).myFonts.getStaticHugeFont(c.clipX); remaining = msg; lineCount = 0; while (remaining != "") { index = instr(remaining, newLineToken); if (index > 0) { text = left(remaining, index); remaining = mid(remaining, index + len(newLineToken)); } else { text = remaining; remaining = ""; } c.strLen(text, textWidth, textHeight); maxTextWidth = max(maxTextWidth, textWidth); lineCount++; } windowWidth = maxTextWidth + 2 * alertBorderSize; windowHeight = lineCount * textHeight + 2 * alertBorderSize; cx = (c.clipX - windowWidth) / 2; cy = (c.clipY - windowHeight) / 2; // Render frame background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseColor * 0.4; c.setPos(cx, cy); c.drawTile(Texture'grad64', windowWidth, windowHeight, 0.0, 0.0, 64.0, 64.0); // Render borders. c.drawColor = baseColor * 0.8; c.setPos(cx - 3.0, cy - 1.0); c.drawTile(Texture'base', 2.0, windowHeight + 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 1.0 + windowWidth, cy - 1.0); c.drawTile(Texture'base', 2.0, windowHeight + 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0, cy - 2.0); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0, cy + 1.0 + windowHeight); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hDefBarSize, cy - 2.0); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hDefBarSize, cy + 1.0 + windowHeight); c.drawTile(Texture'base', hDefBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); // High detail animation effect. if (level.bHighDetailMode) { animIndex = (client.timeSeconds % alertAnimTime) / alertAnimTime; c.drawColor = baseColor * (1.0 - animIndex); dist = sin(animIndex * pi * 0.5) * alertAnimDist; hBarSize = hDefBarSize + hDefBarSize * ((windowWidth + alertAnimDist) / windowWidth) * animIndex; c.setPos(cx - 3.0 - dist, cy - 1.0 - dist); c.drawTile(Texture'base', 2.0, windowHeight + 2.0 + 2 * dist, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 1.0 + windowWidth + dist, cy - 1.0 - dist); c.drawTile(Texture'base', 2.0, windowHeight + 2.0 + 2 * dist, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0 - dist, cy - 2.0 - dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx - 3.0 - dist, cy + 1.0 + windowHeight + dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hBarSize + dist, cy - 2.0 - dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(cx + 3.0 + windowWidth - hBarSize + dist, cy + 1.0 + windowHeight + dist); c.drawTile(Texture'base', hBarSize, 1.0, 0.0, 0.0, 1.0, 1.0); } // Render text. remaining = msg; lineCount = 0; c.drawColor = textColor; while (remaining != "") { index = instr(remaining, newLineToken); if (index > 0) { text = left(remaining, index); remaining = mid(remaining, index + len(newLineToken)); } else { text = remaining; remaining = ""; } c.setPos(cx + alertBorderSize, cy + alertBorderSize + lineCount * textHeight); c.drawText(text, false); lineCount++; } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the message HUD. * $PARAM msg The message that is to be displayed. * $PARAM msgType Message type identifier. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * **************************************************************************************************/ simulated function message(string msg, name msgType, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local int playerColor; local int messageColor; local bool bIsSpecSayMsg; local PlayerReplicationInfo specPRI; local GameReplicationInfo gri; local int index; local string locationName; // Check if the message was send by a spectator using say. if (msgType == 'Event' && instr(msg, ":") >= 0) { // Get shortcut to the game replication info. gri = client.player.gameReplicationInfo; // Find a player. while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { if (gri.PRIArray[index].bIsSpectator && left(msg, len(gri.PRIArray[index].playerName) + 1) ~= (gri.PRIArray[index].playerName $ ":")) { if (bIsSpecSayMsg) { if (len(gri.PRIArray[index].playerName) > len(pri1.playerName)) { pri1 = gri.PRIArray[index]; } } else { bIsSpecSayMsg = true; pri1 = gri.PRIArray[index]; } } index++; } } // Check message type. if (bIsSpecSayMsg) { // Chat message. Special case: player is spec and using say (not teamsay). if (pri1.talkTexture != none) { faceImg = pri1.talkTexture; } addChatMsg(C_METAL, pri1.playerName $ ": ", C_METAL, mid(msg, len(pri1.playerName) + 1)); } else if (pri1 != none && msg != "" && (msgType == 'Say' || msgType == 'TeamSay')) { // Chat message. playerColor = getPlayerColor(pri1); if (pri1.talkTexture != none) { faceImg = pri1.talkTexture; } if (msgType == 'TeamSay') { if (pri1.bIsSpectator && !pri1.bWaitingPlayer) { messageColor = C_WHITE; } else { messageColor = playerColor; } if (!pri1.bIsSpectator) { if (pri1.playerLocation != none) { locationName = pri1.playerLocation.locationName; } else if (pri1.playerZone != none) { locationName = pri1.playerZone.zoneName; } } } else { if (pri1.bIsSpectator && !pri1.bWaitingPlayer) { messageColor = C_METAL; } else { messageColor = C_ORANGE; } } if (locationName != "" && bShowPlayerLocation) { addChatMsg(playerColor, pri1.playerName, C_CYAN, " (" $ locationName $ "): ", messageColor, msg); } else { addChatMsg(playerColor, pri1.playerName $ ": ", messageColor, msg); } } else if (msg != "") { // Other message. addColorizedMessage(msg, pri1, pri2); } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the area just below the chatbox. Before the message is added an * attempt will be made to highlight player names. This is done by checking if the * messages contain the names of the given player replication info objects. * $PARAM msg Message to add. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * **************************************************************************************************/ simulated function addColorizedMessage(string msg, PlayerReplicationInfo pri1, PlayerReplicationInfo pri2) { local string firstPlayerName; local string secondPlayerName; local int firstIndex; local int secondIndex; local int firstPlayerColor; local int secondPlayerColor; local string msgPart1; local string msgPart2; local string msgPart3; local int msgColor; local string tempPlayerName; local int tempIndex; local int tempPlayerColor; // Get message color. msgColor = class'NexgenUtil'.static.getMessageColor(msg); if (msgColor < 0) { msgColor = C_ORANGE; } msg = class'NexgenUtil'.static.removeMessageColorTag(msg); // Get player name indices. getPlayerNameIndices(msg, pri1, pri2, firstIndex, secondIndex); // Get player names & colors. if (pri1 != none) { firstPlayerName = pri1.playerName; firstPlayerColor = getPlayerColor(pri1); } if (pri2 != none) { secondPlayerName = pri2.playerName; secondPlayerColor = getPlayerColor(pri2); } // Swap first and second player if necessary. if (secondIndex >= 0 && (secondIndex < firstIndex || firstIndex < 0)) { tempPlayerName = secondPlayerName; tempIndex = secondIndex; tempPlayerColor = secondPlayerColor; secondPlayerName = firstPlayerName; secondIndex = firstIndex; secondPlayerColor = firstPlayerColor; firstPlayerName = tempPlayerName; firstIndex = tempIndex; firstPlayerColor = tempPlayerColor; } // Split message. if (firstIndex >= 0 && secondIndex >= 0) { msgPart1 = left(msg, firstIndex); msgPart2 = mid(msg, firstIndex + len(firstPlayerName), secondIndex - firstIndex - len(firstPlayerName)); msgPart3 = mid(msg, secondIndex + len(secondPlayerName)); } else if (firstIndex >= 0) { msgPart1 = left(msg, firstIndex); msgPart2 = mid(msg, firstIndex + len(firstPlayerName)); secondPlayerName = ""; } else { firstPlayerName = ""; secondPlayerName = ""; msgPart1 = msg; } // Add message. addMsg(msgColor, msgPart1, firstPlayerColor, firstPlayerName, msgColor, msgPart2, secondPlayerColor, secondPlayerName, msgColor, msgPart3); } /*************************************************************************************************** * * $DESCRIPTION Attemps to locate the indices of player names in the given message. To speed up * the locating process you can pass the player replication info actors of the * players that are most likely to be included in the message. * $PARAM msg The message which may contain player names. * $PARAM pri1 Replication info of the first player involved. * $PARAM pri2 Replication info of the second player involved. * $PARAM index1 The location in the string where the first player name occurs. * $PARAM index2 The location in the string where the second player name occurs. * $ENSURE (index1 >= 0 ? pri1 != none : true) && (index2 >= 0 ? pri2 != none : true) * **************************************************************************************************/ simulated function getPlayerNameIndices(string msg, out PlayerReplicationInfo pri1, out PlayerReplicationInfo pri2, out int index1, out int index2) { local PlayerReplicationInfo tmpPRI; local GameReplicationInfo gri; local int index; local int nameIndex; local int tmpIndex; // Get shortcut to the game replication info. gri = client.player.gameReplicationInfo; // Initially no indices have been found. index1 = -1; index2 = -1; // Check if the first PRI is actually in the message. This appears not to be the case for some // messages (for example with the Stranglove weapon mod). if (pri1 != none && instr(msg, pri1.playerName) < 0) { pri1 = none; } // Swap player replication info's if needed. if (pri1 == none && pri2 != none) { pri1 = pri2; pri2 = none; } else if (pri1 != none && pri2 != none && len(pri2.playerName) > len(pri1.playerName)) { // Ensure the longest playername is located first. tmpPRI = pri1; pri1 = pri2; pri2 = tmpPRI; } // Get the position of the first player name in the message. if (pri1 == none) { // No PRI found, try to find one. index = 0; while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { // Get current player replication info. tmpPRI = gri.PRIArray[index]; // Get position of the players name in the message. nameIndex = instr(msg, tmpPRI.playerName); // Select PRI? if (nameIndex >= 0 && (pri1 == none || len(tmpPRI.playerName) > len(pri1.playerName))) { // Yes, no name has been found so far or a longer player name has been found. pri1 = tmpPRI; index1 = nameIndex; } // Continue with next player name. index++; } } else { // Already got PRI, just find the index of the name. index1 = instr(msg, pri1.playerName); } // Get the position of the second player name in the message. if (pri1 != none && pri2 == none) { // No PRI found, try to find one. index = 0; while (index < arrayCount(gri.PRIArray) && gri.PRIArray[index] != none) { // Get current player replication info. tmpPRI = gri.PRIArray[index]; // Get position of the players name in the message. nameIndex = instr(msg, tmpPRI.playerName); // Check for overlap. if (index1 >=0 && nameIndex >= 0 && index1 <= nameIndex && nameIndex < index1 + len(pri1.playerName)) { // Overlap detected, check if name occurs after the first player name. nameIndex = instr(mid(msg, index1 + len(pri1.playerName)), tmpPRI.playerName); if (nameIndex >= 0) { nameIndex += index1 + len(pri1.playerName); } } // Select PRI? if (nameIndex >= 0 && (pri2 == none || len(tmpPRI.playerName) > len(pri2.playerName))) { // Yes, no name has been found so far or a longer player name has been found. pri2 = tmpPRI; index2 = nameIndex; } // Continue with next player name. index++; } } else if (pri2 != none) { // Already got PRI, just find the index of the name. nameIndex = instr(msg, pri2.playerName); // Check for overlap. if (index1 >= 0 && nameIndex >= 0 && index1 <= nameIndex && nameIndex < index1 + len(pri1.playerName)) { // Overlap detected, check if name occurs after the first player name. nameIndex = instr(mid(msg, index1 + len(pri1.playerName)), pri2.playerName); if (nameIndex >= 0) { nameIndex += index1 + len(pri1.playerName); } } // Set index. index2 = nameIndex; } } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the chatbox. The message is split in several parts, so each can * be displayed in a specified color. * $PARAM col1 Color of the first part of the message. * $PARAM text1 First part of the message. * $PARAM col2 Color of the second part of the message. * $PARAM text2 Second part of the message. * $PARAM col3 Color of the third part of the message. * $PARAM text3 Third part of the message. * $PARAM col4 Color of the fourth part of the message. * $PARAM text4 Fourth part of the message. * $PARAM col5 Color of the fifth part of the message. * $PARAM text5 Fifth part of the message. * **************************************************************************************************/ simulated function addChatMsg(int col1, string text1, optional int col2, optional string text2, optional int col3, optional string text3, optional int col4, optional string text4, optional int col5, optional string text5) { local int index; // Find position in messages list. if (chatMsgCount < arrayCount(chatMessages)) { index = chatMsgCount; chatMsgCount++; } else { // List is full, shift messages. for (index = 1; index < chatMsgCount; index++) { chatMessages[index - 1] = chatMessages[index]; } index = chatMsgCount - 1; } // Store message. chatMessages[index].text[0] = text1; chatMessages[index].text[1] = text2; chatMessages[index].text[2] = text3; chatMessages[index].text[3] = text4; chatMessages[index].text[4] = text5; chatMessages[index].col[0] = col1; chatMessages[index].col[1] = col2; chatMessages[index].col[2] = col3; chatMessages[index].col[3] = col4; chatMessages[index].col[4] = col5; chatMessages[index].timeStamp = timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Adds a message to the area below the chatbox. The message is split in several * parts, so each can be displayed in a specified color. * $PARAM col1 Color of the first part of the message. * $PARAM text1 First part of the message. * $PARAM col2 Color of the second part of the message. * $PARAM text2 Second part of the message. * $PARAM col3 Color of the third part of the message. * $PARAM text3 Third part of the message. * $PARAM col4 Color of the fourth part of the message. * $PARAM text4 Fourth part of the message. * $PARAM col5 Color of the fifth part of the message. * $PARAM text5 Fifth part of the message. * **************************************************************************************************/ simulated function addMsg(int col1, string text1, optional int col2, optional string text2, optional int col3, optional string text3, optional int col4, optional string text4, optional int col5, optional string text5) { local int index; // Find position in messages list. if (msgCount < arrayCount(messages)) { index = msgCount; msgCount++; } else { // List is full, shift messages. for (index = 1; index < msgCount; index++) { messages[index - 1] = messages[index]; } index = msgCount - 1; } // Store message. messages[index].text[0] = text1; messages[index].text[1] = text2; messages[index].text[2] = text3; messages[index].text[3] = text4; messages[index].text[4] = text5; messages[index].col[0] = col1; messages[index].col[1] = col2; messages[index].col[2] = col3; messages[index].col[3] = col4; messages[index].col[4] = col5; messages[index].timeStamp = timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Initializes/updates the variables used in the rendering procedure. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * $ENSURE c.font != none * **************************************************************************************************/ simulated function setup(Canvas c) { local int index; local bool bUpdateBase; // Make sure the font ain't none. if (baseFont == none) baseFont = Font'SmallFont'; c.font = baseFont; // Prevent redundant setups. if (lastSetupTime == level.timeSeconds) { return; } // Timer control. timeSeconds += (level.timeSeconds - lastLevelTimeSeconds) / level.timeDilation; lastLevelTimeSeconds = level.timeSeconds; // Check if the base variables need to be updated. bUpdateBase = lastResX != c.clipX || lastResY != c.clipY || lastTeam != client.player.playerReplicationInfo.team; // Update HUD base variables. if (bUpdateBase) { // General variables. baseFont = ChallengeHUD(c.viewport.actor.myHUD).myFonts.getStaticSmallestFont(c.clipX); c.font = baseFont; c.strLen("Online [00:00]", minPanelWidth, baseFontHeight); if (client.player.playerReplicationInfo.bIsSpectator && !client.player.playerReplicationInfo.bWaitingPlayer) { index = 4; } else if (0 <= client.player.playerReplicationInfo.team && client.player.playerReplicationInfo.team <= 3) { index = client.player.playerReplicationInfo.team; } else { index = 5; } baseHUDColor = baseColors[index]; lastResX = c.clipX; lastResY = c.clipY; lastTeam = client.player.playerReplicationInfo.team; // Message box info. msgBoxWidth = int(c.clipX * 0.75); msgBoxLineHeight = int(baseFontHeight + 4.0); msgBoxHeight = msgBoxLineHeight * arrayCount(chatMessages); } // Remove expired messages. if (chatMsgCount > 0 && timeSeconds - chatMessages[0].timeStamp > chatMessageLifeTime) { for (index = 1; index < chatMsgCount; index++) { chatMessages[index - 1] = chatMessages[index]; } chatMsgCount--; if (chatMsgCount == 0) { faceImg = none; } } if (msgCount > 0 && timeSeconds - messages[0].timeStamp > messageLifeTime) { for (index = 1; index < msgCount; index++) { messages[index - 1] = messages[index]; } msgCount--; } lastSetupTime = level.timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Renders the chat message box. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * **************************************************************************************************/ simulated function renderMessageBox(Canvas c) { local float panelWidth; local float panelHeight; local PanelInfo serverState; local PanelInfo clientState; local int index; local float cx; local float cy; // Initialize. setup(c); panelHeight = int((msgBoxHeight - 3.0) / 2.0); serverState = getServerState(); clientState = getClientState(); panelWidth = fMax(getPanelWidth(serverState, c, panelHeight), getPanelWidth(clientState, c, panelHeight)); // Background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.4; c.setPos(1.0, 1.0); c.drawTile(Texture'grad64', msgBoxWidth - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, 64.0, 64.0); // Borders. c.drawColor = baseHUDColor * 0.8; c.setPos(0.0, 0.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, msgBoxHeight - 1.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0 - panelWidth, panelHeight + 1.0); c.drawTile(Texture'base', panelWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxHeight - 1.0, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 2.0 - panelWidth, 1.0); c.drawTile(Texture'base', 1.0, msgBoxHeight - 2.0, 0.0, 0.0, 1.0, 1.0); // Panels. renderPanel(serverState, c, panelHeight, msgBoxWidth - panelWidth - 1.0, 1.0); renderPanel(clientState, c, panelHeight, msgBoxWidth - panelWidth - 1.0, panelHeight + 2.0); // Face image. if (faceImg != none) { c.style = ERenderStyle.STY_Normal; c.drawColor = blankColor; c.setPos(1.0, 1.0); c.drawTile(faceImg, msgBoxHeight - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, faceImg.uSize, faceImg.vSize); c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.25; c.setPos(1.0, 1.0); c.drawTile(Texture'LadrStatic.Static_a00', msgBoxHeight - 2.0, msgBoxHeight - 2.0, 0.0, 0.0, Texture'LadrStatic.Static_a00'.uSize, Texture'LadrStatic.Static_a00'.vSize); } // Typing prompt. if (client.player.player.console.bTyping) { renderTypingPromt(c, "(>" $ client.player.player.console.typedStr $ "_"); } // Chat messages. cx = msgBoxHeight + 2.0; cy = (msgBoxLineHeight - baseFontHeight) / 2.0; for (index = 0; index < chatMsgCount; index++) { renderMessage(c, cx, cy, chatMessages[index]); cy += msgBoxLineHeight; } // Other messages. cx = 1.0; cy = msgBoxHeight + 2.0; if (client.player.player.console.bTyping) { cy += msgBoxLineHeight; } for (index = 0; index < msgCount; index++) { renderMessage(c, cx, cy, messages[index]); cy += baseFontHeight; } } /*************************************************************************************************** * * $DESCRIPTION Renders the typing promt for the chat message box. * $PARAM c Canvas object that provides the drawing capabilities. * $REQUIRE c != none * **************************************************************************************************/ simulated function renderTypingPromt(Canvas c, string msg) { local float msgOffset; // Background. c.style = ERenderStyle.STY_Translucent; c.drawColor = baseHUDColor * 0.4; c.setPos(1.0, msgBoxHeight); c.drawTile(Texture'grad32', msgBoxWidth - 2.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 32.0, 32.0); // Borders. c.drawColor = baseHUDColor * 0.8; c.setPos(0.0, msgBoxHeight + msgBoxLineHeight - 1.0); c.drawTile(Texture'base', msgBoxWidth, 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(0.0, msgBoxHeight); c.drawTile(Texture'base', 1.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 1.0, 1.0); c.setPos(msgBoxWidth - 1.0, msgBoxHeight); c.drawTile(Texture'base', 1.0, msgBoxLineHeight - 1.0, 0.0, 0.0, 1.0, 1.0); // Message. msgOffset = (msgBoxLineHeight - baseFontHeight) / 2.0; c.font = baseFont; c.style = ERenderStyle.STY_Normal; c.drawColor = colors[C_WHITE]; c.setPos(msgOffset, msgOffset + msgBoxHeight); c.drawText(msg, false); } /*************************************************************************************************** * * $DESCRIPTION Renders the specified message. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $PARAM msg The message that is to be rendered. * $REQUIRE c != none && msg != none * **************************************************************************************************/ simulated function renderMessage(Canvas c, float x, float y, MessageInfo msg) { local float cx; local int msgIndex; local float msgWidth; local float msgHeight; local float lifeTime; local float blinkFactor; local float blinkIntensity; // Check if the message should blink. lifeTime = (timeSeconds - msg.timeStamp); if (bFlashMessages && lifeTime < messageBlinkTime) { blinkFactor = (1.0 + cos(lifeTime * 2 * pi * messageBlinkRate)) / 2.0; blinkIntensity = (1.0 - blinkFactor) * 255.0; } // Render message. cx = x; c.font = baseFont; c.style = ERenderStyle.STY_Normal; for (msgIndex = 0; msgIndex < 5; msgIndex++) { c.setPos(cx, y); c.drawColor = colors[msg.col[msgIndex]]; if (bFlashMessages && lifeTime < messageBlinkTime) { c.drawColor = c.drawColor * blinkColorDamp; c.drawColor.r = int(float(c.drawColor.r) * blinkFactor + blinkIntensity); c.drawColor.g = int(float(c.drawColor.g) * blinkFactor + blinkIntensity); c.drawColor.b = int(float(c.drawColor.b) * blinkFactor + blinkIntensity); } c.drawText(msg.text[msgIndex], false); c.strLen(msg.text[msgIndex], msgWidth, msgHeight); cx += msgWidth; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current state of the server. The result will be stored in a * PanelInfo struct so it can be immediately rendered. * $RETURN The current server state. * $ENSURE result != none * **************************************************************************************************/ simulated function PanelInfo getServerState() { local PanelInfo pInf; local string stateInfo; local int remainingTime; local int minutes, seconds; local name stateType; local byte bBlink; local int index; // Check current state. if (client.player.bBadConnectionAlert) { // Server is offline. stateType = SS_Offline; if (client.sConf.autoReconnectTime > 0) { stateInfo = string(max(0, 1 + client.sConf.autoReconnectTime - (client.timeSeconds - client.badConnectionSince))); pInf.text = client.lng.format(client.lng.offlineStateRCN, stateInfo); } else { pInf.text = client.lng.offlineState; } pInf.textCol = colors[C_RED]; pInf.icon = Texture'offlineIcon'; pInf.solidIcon = Texture'offlineIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Waiting) { // Waiting for players. stateType = SS_Waiting; if (client.gInf.countDown <= 0 || !client.sConf.enableNexgenStartControl) { pInf.text = client.lng.waitingStateUnknownTime; } else { pInf.text = client.lng.format(client.lng.waitingState, client.gInf.countDown); } pInf.textCol = colors[C_RED]; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Ready) { // Ready to start the game. stateType = SS_Ready; if (client.gInf.bTournamentMode) { pInf.text = client.lng.format(client.lng.readySignalWaitState, client.gInf.numReady, client.gInf.numRequiredReady); } else { pInf.text = client.lng.readyState; } pInf.textCol = colors[C_ORANGE]; pInf.bBlink = true; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Starting) { // Game is starting. stateType = SS_Starting; pInf.text = client.lng.format(client.lng.startingState, client.gInf.countDown); pInf.textCol = colors[C_YELLOW]; pInf.bBlink = true; pInf.icon = Texture'waitIcon'; pInf.solidIcon = Texture'waitIcon2'; } else if (client.gInf.gameState == client.gInf.GS_Ended) { // Game has ended. stateType = SS_Ended; pInf.text = client.lng.endedState; pInf.textCol = colors[C_YELLOW]; pInf.icon = Texture'serverIcon'; pInf.solidIcon = Texture'serverIcon2'; } else if (level.pauser != "") { // Game has been paused. stateType = SS_Paused; pInf.text = client.lng.pausedState; pInf.textCol = colors[C_METAL]; pInf.bBlink = true; pInf.icon = Texture'offlineIcon'; pInf.solidIcon = Texture'offlineIcon2'; } else if (client.sConf.matchModeActivated) { // Match in progress. stateType = SS_Match; remainingTime = client.player.gameReplicationInfo.remainingTime ; if (remainingTime > 0 && (remainingTime / matchInfoSwitchTime) % 2 == 1) { minutes = remainingTime / secPerMinute; seconds = remainingTime % secPerMinute; stateInfo = right("0" $ minutes, 2) $ ":" $ right("0" $ seconds, 2); } else { stateInfo = client.sConf.currentMatch $ "/" $ client.sConf.matchesToPlay; } pInf.text = client.lng.format(client.lng.matchState, stateInfo); pInf.textCol = colors[C_YELLOW]; pInf.icon = Texture'matchIcon'; pInf.solidIcon = Texture'matchIcon2'; } else { // Game in progress. stateType = SS_Normal; remainingTime = client.player.gameReplicationInfo.remainingTime ; if (remainingTime > 0) { minutes = remainingTime / secPerMinute; seconds = remainingTime % secPerMinute; stateInfo = right("0" $ minutes, 2) $ ":" $ right("0" $ seconds, 2); } else { stateInfo = right("0" $ level.hour, 2) $ ":" $ right("0" $ level.minute, 2); } pInf.text = client.lng.format(client.lng.onlineState, stateInfo); pInf.textCol = colors[C_GREEN]; pInf.icon = Texture'serverIcon'; pInf.solidIcon = Texture'serverIcon2'; } // Allow plugins to modify the state panel. bBlink = byte(pInf.bBlink); while (index < arrayCount(client.clientCtrl) && client.clientCtrl[index] != none) { if (client.clientCtrl[index].bCanModifyHUDStatePanel) { client.clientCtrl[index].modifyServerState(stateType, pInf.text, pInf.textCol, pInf.icon, pInf.solidIcon, bBlink); } index++; } pInf.bBlink = bool(bBlink); // Return state panel. return pInf; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current state of the client. The result will be stored in a * PanelInfo struct so it can be immediately rendered. * $RETURN The current client state. * $ENSURE result != none * **************************************************************************************************/ simulated function PanelInfo getClientState() { local PanelInfo pInf; local string stateInfo; local byte protectTime; local name stateType; local byte bBlink; local int index; pInf.icon = Texture'playerIcon'; pInf.solidIcon = Texture'playerIcon2'; // Check current state. if (!client.loginComplete) { // Logging in to the server. stateType = CS_Login; pInf.text = client.lng.loginState; pInf.textCol = colors[C_WHITE]; } else if (client.idleTimeRemaining >= 0) { // Idle / camping. stateType = CS_Idle; stateInfo = string(client.idleTimeRemaining); pInf.text = client.lng.format(client.lng.idleState, stateInfo); pInf.textCol = colors[C_CYAN]; pInf.icon = Texture'idleIcon'; pInf.solidIcon = Texture'idleIcon2'; pInf.bBlink = true; } else if (client.spawnProtectionTime > 0 || client.tkDmgProtectionTime > 0 || client.tkPushProtectionTime > 0) { // Client is damage protected. stateType = CS_Protected; protectTime = max(max(client.spawnProtectionTime, client.tkDmgProtectionTime), client.tkPushProtectionTime); pInf.text = client.lng.format(client.lng.protectedState, protectTime); if (client.tkDmgProtectionTime > 0) { pInf.textCol = colors[C_ORANGE]; } else { pInf.textCol = colors[C_YELLOW]; } pInf.icon = Texture'shieldIcon'; pInf.solidIcon = Texture'shieldIcon2'; } else if (client.bMuted || client.gInf.bMuteAll) { // Client is muted. stateType = CS_Muted; pInf.text = client.lng.mutedState; pInf.textCol = colors[C_RED]; pInf.icon = Texture'mutedIcon'; pInf.solidIcon = Texture'mutedIcon2'; } else if (client.player.health <= 0) { // Player is dead. stateType = CS_Dead; pInf.text = client.lng.deadState; pInf.textCol = colors[C_RED]; } else { // Normal state. stateType = CS_Normal; pInf.text = client.title; if (client.bSpectator) { pInf.textCol = colors[C_CYAN]; pInf.icon = Texture'specIcon'; pInf.solidIcon = Texture'specIcon2'; } else { pInf.textCol = colors[C_GREEN]; } } // Allow plugins to modify the state panel. bBlink = byte(pInf.bBlink); while (index < arrayCount(client.clientCtrl) && client.clientCtrl[index] != none) { if (client.clientCtrl[index].bCanModifyHUDStatePanel) { client.clientCtrl[index].modifyClientState(stateType, pInf.text, pInf.textCol, pInf.icon, pInf.solidIcon, bBlink); } index++; } pInf.bBlink = bool(bBlink); // Return state panel. return pInf; } /*************************************************************************************************** * * $DESCRIPTION Returns the space necessary for the given panel if it is to be rendered. * $PARAM pInf Panel contents. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM panelHeight Vertical space available for the panel (in pixels). * $REQUIRE pInf != none && c != none && panelHeight > 0 * $RETURN * $ENSURE result > 0 * **************************************************************************************************/ simulated function float getPanelWidth(PanelInfo pInf, canvas c, float panelHeight) { local float separatorWidth; local float width; local float temp; // Get text width. separatorWidth = int(baseFontHeight * 0.4); c.font = baseFont; c.strLen(pInf.text, width, temp); width = fMax(minPanelWidth, width); // And add icon and separator width. if (pInf.icon == none) { width += 2.0 * separatorWidth; } else { if (iconSize > panelHeight) { width += panelHeight + 3.0 * separatorWidth; } else { width += iconSize + 3.0 * separatorWidth; } } return width; } /*************************************************************************************************** * * $DESCRIPTION Renders a panel with the specified contents at the given location. * $PARAM pInf Panel contents. * $PARAM c Canvas object that provides the drawing capabilities. * $PARAM panelHeight Vertical space available for the panel (in pixels). * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $REQUIRE pInf != none && c != none && panelHeight > 0 * **************************************************************************************************/ simulated function renderPanel(PanelInfo pInf, canvas c, float panelHeight, float x, float y) { local float separatorWidth; local float cx; local float cy; local float iconHeight; local float lifeTime; local float blinkFactor; local float blinkIntensity; if (pInf.bBlink) { blinkFactor = (1.0 + cos(timeSeconds * 2 * pi * panelBlinkRate)) / 2.0; blinkIntensity = (1.0 - blinkFactor) * 255.0; } separatorWidth = int(baseFontHeight * 0.4); // Draw icon. cx = x + separatorWidth; if (pInf.icon != none) { if (iconSize > panelHeight) { iconHeight = panelHeight; cy = y; } else { iconHeight = iconSize; cy = y + int((panelHeight - iconSize) / 2.0); } c.style = ERenderStyle.STY_Translucent; c.drawColor = blankColor; c.setPos(cx, cy); c.drawTile(pInf.icon, iconHeight, iconHeight, 0.0, 0.0, iconSize, iconSize); if (pInf.solidIcon != none) { c.style = ERenderStyle.STY_Normal; c.setPos(cx, cy); c.drawTile(pInf.solidIcon, iconHeight, iconHeight, 0.0, 0.0, iconSize, iconSize); } cx += separatorWidth + iconHeight; } // Draw the text. cy = y + (panelHeight - baseFontHeight) / 2.0; c.style = ERenderStyle.STY_Normal; c.drawColor = pInf.textCol; if (pInf.bBlink) { c.drawColor = c.drawColor * blinkColorDamp; c.drawColor.r = int(float(c.drawColor.r) * blinkFactor + blinkIntensity); c.drawColor.g = int(float(c.drawColor.g) * blinkFactor + blinkIntensity); c.drawColor.b = int(float(c.drawColor.b) * blinkFactor + blinkIntensity); } c.setPos(cx, cy); c.font = baseFont; c.drawText(pInf.text, false); } /*************************************************************************************************** * * $DESCRIPTION Returns the color for the specified player. * $PARAM pri Replication info of the player whose color is to be returned. * $RETURN The color of the player (index in the base color palette). * $ENSURE 0 <= result && result < arrayCount(colors) * **************************************************************************************************/ simulated function int getPlayerColor(PlayerReplicationInfo pri) { local int colorNum; if (pri.bIsSpectator && !pri.bWaitingPlayer) { colorNum = C_METAL; } else if (0 <= pri.team && pri.team <= 3) { colorNum = pri.team; } else { colorNum = C_WHITE; } return colorNum; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ rEA#c;EA#nm  |E "Admin login"xE@#Po>" &, *,'E@#,*, 2,, :,, B,, J,, R,, Z,, b, , j, , r, , z, ,   }E "Start game"CF "Open mapvote"{EXE/B=?X??%$?X?,?%?X?,d?%  FF 0E~"dE-}'n &d~"-} n ,@~d.en , @%-}(@% n ,-}(@%gd@dd@&gdd-}-}{g }g,@%b-}@}gVg@&-}0V V9@-}-}Jgn -}  VF "Exit"}N'/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenHTTPClient * $VERSION 1.01 (4-8-2008 16:13) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION The Nexgen HTTP client used to communicate with the Nexgen master server. * **************************************************************************************************/ class NexgenHTTPClient extends UBrowserHTTPClient; var NexgenController control; // Server settings. const serverHost = "130.89.163.70"; const serverPort = 80; /*************************************************************************************************** * * $DESCRIPTION Creates the HTTP client. * **************************************************************************************************/ function preBeginPlay() { super.preBeginPlay(); control = NexgenController(owner); } /*************************************************************************************************** * * $DESCRIPTION Registers the server in the Nexgen server database. * **************************************************************************************************/ function registerServer() { local string url; url = "/regnscsvr.php?ver=" $ class'NexgenUtil'.default.versionCode $ "&port=" $ level.game.getServerPort() $ "&name="; url = url $ class'NexgenUtil'.static.urlEncode(control.sConf.serverName, 255 - len(url)); browse(serverHost, url, serverPort); } /*************************************************************************************************** * * $DESCRIPTION Called when the HTTP request failed. * **************************************************************************************************/ function HTTPError(int code) { control.nscLog(class'NexgenUtil'.static.format(control.lng.httpClientErrorMsg, code)); } /*************************************************************************************************** * * $DESCRIPTION Called when the HTTP request has been replied and the data has been received. * $PARAM data The data that has been received. * **************************************************************************************************/ function HTTPReceivedData(string data) { // Ignore. } JF "M"`,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenGameInfo * $VERSION 1.06 (14-6-2008 13:39) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Extended game info container class. Stores all Nexgen related game extension data. * **************************************************************************************************/ class NexgenGameInfo extends ReplicationInfo; var int updateCount; // How many times the settings have been updated during the current // game. Used to detect setting changes clientside. var bool bTeamsLocked; // Teams have been locked. Nobody may switch or join (as player). var bool bNoTeamSwitch; // Whether team switching has been disabled. var bool bNoTeamBalance; // Whether team balancing has been disabled. var byte gameState; // Current state of the game. var int countDown; // Countdown timer for the gamestate. var byte matchAdminCount; // Number of match admins logged in. var byte maxTeams; // Number of teams available in the game. var bool bMuteAll; // Whether all players are muted. var float gameSpeed; // Game speed multiplier. var bool bNoNameChange; // Whether players can't change their name during the game. var byte rebootCountDown; // Seconds remaining before the server will be rebooted. var bool bTournamentMode; // Whether tournament mode is enabled. var byte numReady; // Number of players that have send a ready signal. var byte numRequiredReady; // Required number of players that should send a ready signal. // Game states. const GS_Waiting = 0; // Waiting for players. const GS_Ready = 1; // Ready for launch. const GS_Starting = 2; // Game is starting. const GS_Playing = 3; // Game is in progress. const GS_Ended = 4; // The game has ended. //const GS_Loading = 5; // Loading a new game. // Game info change events const IT_GlobalRights = 0; // Global game rights. const IT_GameSettings = 1; // General game settings. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) bTeamsLocked, bNoTeamSwitch, bNoTeamBalance, gameState, countDown, maxTeams, bMuteAll, gameSpeed, bNoNameChange, rebootCountDown, bTournamentMode, numReady, numRequiredReady, updateCount; } b/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenEditControl * $VERSION 1.00 (20-10-2007 23:13) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extended edit control GUI component class. * **************************************************************************************************/ class NexgenEditControl extends UWindowEditControl; /*************************************************************************************************** * * $DESCRIPTION Creates the setup for this GUI component. * $OVERRIDE * **************************************************************************************************/ function created() { super(UWindowDialogControl).created(); editBox = UWindowEditBox(createWindow(class'NexgenEditBox', 0, 0, winWidth, winHeight)); editBox.notifyOwner = self; editBox.bSelectOnFocus = true; editBoxWidth = winWidth / 2; setEditTextColor(lookAndFeel.editBoxTextColor); } /*************************************************************************************************** * * $DESCRIPTION Disables or enables the component. In the disabled state an user can't change the * value entered in the editbox. * $PARAM bDisabled Indicates whether or not the component should be disabled. * **************************************************************************************************/ function setDisabled(bool bDisabled) { NexgenEditBox(editBox).bDisabled = bDisabled; editBox.bCanEdit = !bDisabled; } }!/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenEditBox * $VERSION 1.01 (21-10-2007 11:02) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen extended editbox GUI component class. * **************************************************************************************************/ class NexgenEditBox extends UWindowEditBox; var bool bDisabled; // Whether the component is enabled or not. /*************************************************************************************************** * * $DESCRIPTION Renders the GUI component. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y) { local float w, h; local float textY; if (bDisabled) { c.drawColor.r = 192; c.drawColor.g = 192; c.drawColor.b = 192; drawStretchedTexture(c, 0, 0, winWidth, winHeight, Texture'UWindow.WhiteTexture'); } super.paint(c, x, y); } /*************************************************************************************************** * * $DESCRIPTION Changes the value entered in the edit box. This is a fixed version of the * UWindow.UWindowEditBox setValue function, which didn't updated the caretOffset * when bAllSelected was set to true. * $PARAM newValue The new value of the edit box. * $PARAM newValue2 New alternate value. * $OVERRIDE * **************************************************************************************************/ function setValue(string newValue, optional string newValue2) { value = newValue; value2 = newValue2; if (bAllSelected || caretOffset > len(value)) { caretOffset = len(value); } notify(DE_Change); } LF "L"BFm!b=K-j}m!, -j S, { m!S&0{  { 9 A{  { F-j(S-j  NF "K"jOF "J"RF "I"q/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenDummyComponent * $VERSION 1.00 (7-3-2007 20:49) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dummy GUI component. This component only marks the occupied area, but doesn't * have any means of interaction with the user. Should only be used for testing. * **************************************************************************************************/ class NexgenDummyComponent extends UWindowWindow; /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ super.paint(c, x, y); drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); } KFRg^*O=-\ '4-\ Q}5-5-5-5-5-5-4I 5-5-5-5-5-5-4Q&x0I  xI 9z!JI O%-\  Oz!@ }R~ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789R@ &%-\ (O@ z*#{R@ &I -\ (*@ Q-\   UF "H"\ G/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCPKeyBind * $VERSION 1.04 (19-1-2008 17:51) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen keybind settings panel. * **************************************************************************************************/ class NexgenCPKeyBind extends NexgenPanel; var UMenuRaisedButton bindButton[9]; // Keybind buttons. var string bindCommand[9]; // Console commands for the key binds. var int selectedBind; // Currently selected key. var bool bPolling; // Waiting for a new key assignment. const bindSeparator = "|"; // Token used to seperate commands in an action string. const getKeyNameCommand = "keyname"; // Console command to retrieve a key name. const getKeyBindCommand = "keybinding"; // Console command to retrieve the action bound to a key. const setKeyBindCommand = "set input"; // Console command to change a key binding. /*************************************************************************************************** * * $DESCRIPTION Creates the contents of the panel. * $OVERRIDE * **************************************************************************************************/ function setContent() { local int index; // Create layout & add components. setAcceptsFocus(); createPanelRootRegion(); splitRegionH(16); addLabel(client.lng.keyBindsTxt, true, TA_Center); divideRegionV(2); divideRegionH(arrayCount(bindButton)); divideRegionH(arrayCount(bindButton)); addLabel(client.lng.balanceBindTxt); addLabel(client.lng.switchRedBindTxt); addLabel(client.lng.switchBlueBindTxt); addLabel(client.lng.switchGreenBindTxt); addLabel(client.lng.switchGoldBindTxt); addLabel(client.lng.suicideBindTxt); addLabel(client.lng.openMapVoteBindTxt); addLabel(client.lng.openCPBindTxt); addLabel(client.lng.pauseGameBindTxt); for (index = 0; index < arrayCount(bindButton); index++) { bindButton[index] = addRaisedButton(); bindButton[index].align = TA_Center; bindButton[index].bAcceptsFocus = false; bindButton[index].bIgnoreLDoubleClick = true; bindButton[index].bIgnoreMDoubleClick = true; bindButton[index].bIgnoreRDoubleClick = true; bindButton[index].index = index; bindButton[index].register(self); } loadKeyBinds(); } /*************************************************************************************************** * * $DESCRIPTION Loads the keybind settings and displays them on the config panel. * **************************************************************************************************/ function loadKeyBinds() { local int keyNum; local string keyName; local string keyAction; local int index; // Iterate over all keys. for (keyNum = 0; keyNum < 255; keyNum++) { keyName = client.player.consoleCommand(getKeyNameCommand @ keyNum); if (keyName != "") { // Get action assigned to key. keyAction = client.player.consoleCommand(getKeyBindCommand @ keyName); // Check action string with the keybind commands. for (index = 0; index < arrayCount(bindButton); index++) { if (containsCommand(keyAction, bindCommand[index])) { bindButton[index].text = keyName; } } } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether a keybind action contains one of the specified commands. Commands * are separated by the 'separator' token (comma). * $PARAM action Keybind action string. * $PARAM commands List of commands to check for. * $REQUIRE commands != "" * $RETURN True if the action string contains one of the specified commands. * **************************************************************************************************/ function bool containsCommand(string action, string commands) { local string cmd; local string remaining; local int index; local bool bFound; action = caps(action); // For each command in the command string (separated by commas). remaining = caps(commands); while (remaining != "" && !bFound) { // Get next command. index = instr(remaining, separator); if (index < 0) { cmd = remaining; remaining = ""; } else { cmd = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Compare command. bFound = instr(action, cmd) >= 0; } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local int index; super.notify(control, eventType); if (eventType == DE_Click) { // Keybind button clicked? if (control != none && control.isA('UMenuRaisedButton')) { index = UMenuRaisedButton(control).index; if (bPolling && selectedBind == index) { // Clicked on same button, cancel polling. bindButton[selectedBind].bDisabled = false; bPolling = false; } else if (bPolling) { // New key bind button selected. bindButton[selectedBind].bDisabled = false; bindButton[index].bDisabled = true; selectedBind = index; } else { // No key bind button selected yet. bindButton[index].bDisabled = true; selectedBind = index; bPolling = true; } } else if (bPolling) { // Clicked elsewhere, but still polling, cancel action. bindButton[selectedBind].bDisabled = false; bPolling = false; } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of a key press event. * $PARAM key The number of the key that was pressed. * $PARAM x Unknown, x location of mouse cursor? * $PARAM y Unknown, x location of mouse cursor? * $OVERRIDE * **************************************************************************************************/ function keyDown(int key, float x, float y) { local string keyName; keyName = client.player.consoleCommand(getKeyNameCommand @ key); // Assign new key binding? if (bPolling && keyName != "") { // Remove old binding. removeKeybind(bindButton[selectedBind].text, bindCommand[selectedBind]); // Add new binding. addKeybind(keyName, bindCommand[selectedBind]); // Update buttons. bindButton[selectedBind].bDisabled = false; bindButton[selectedBind].text = keyName; bPolling = false; } } /*************************************************************************************************** * * $DESCRIPTION Removes the specified commands from the action bound to the given key. * $PARAM keyName Name of the key for which the bindings should be updated. * $PARAM commands List of commands to remove from the action string. * $REQUIRE keyName != "" * **************************************************************************************************/ function removeKeybind(String keyName, string commands) { local string actionStr; local string remaining; local string cmd; local int index; // Get action string. actionStr = client.player.consoleCommand(getKeyBindCommand @ keyName); // Update action string. remaining = caps(commands); while (remaining != "") { // Get next command. index = instr(remaining, separator); if (index < 0) { cmd = remaining; remaining = ""; } else { cmd = left(remaining, index); remaining = mid(remaining, index + len(separator)); } // Remove command from action string. index = instr(caps(actionStr), cmd); if (index >= 0) { actionStr = left(actionStr, index) $ mid(actionStr, index + len(cmd)); if (mid(actionStr, index, len(bindSeparator)) == bindSeparator) { // Remove | token after command. actionStr = left(actionStr, index) $ mid(actionStr, index + len(bindSeparator)); } } } // Store action string. client.player.consoleCommand(setKeyBindCommand @ keyName @ actionStr); } /*************************************************************************************************** * * $DESCRIPTION Adds the specified command from to the action bound to the given key. * $PARAM keyName Name of the key for which the bindings should be updated. * $PARAM command Commands to add to the action string. * $REQUIRE keyName != "" * **************************************************************************************************/ function addKeybind(String keyName, string command) { local string actionStr; local string cmd; // Get action string. actionStr = client.player.consoleCommand(getKeyBindCommand @ keyName); // Update action string. if (instr(command, separator) >= 0) { cmd = left(command, instr(command, separator)); } else { cmd = command; } if (class'NexgenUtil'.static.trim(actionStr) == "") { // No action bound yet. actionStr = cmd; } else { // Some actions already bound. actionStr = actionStr $ bindSeparator $ cmd; } // Store action string. client.player.consoleCommand(setKeyBindCommand @ keyName @ actionStr); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ WF "G"[F "Disconnect"XF "F"iF "E"s/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCorePlugin * $VERSION 1.06 (15-6-2008 12:11) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Base Nexgen controller plugin. * **************************************************************************************************/ class NexgenCorePlugin extends NexgenPlugin; var Actor ipToCountry; // IpToCountry actor. // Controller settings. const timerFreq = 2.0; // Timer tick frequency. // Extra player attributes. const PA_Muted = "muted"; // Whether the player is muted. const PA_NoTeamSwitch = "noTeamSwitch"; // Whether the player is not allowed to switch to // another team. /*************************************************************************************************** * * $DESCRIPTION Initializes the plugin. Note that if this function returns false the plugin will * be destroyed and is not to be used anywhere. * $RETURN True if the initialization succeeded, false if it failed. * $OVERRIDE * **************************************************************************************************/ function bool initialize() { local Actor a; // Set panel classes. control.sConf.serverInfoPanelClass = class'NexgenRCPServerInfo'; control.sConf.gameInfoPanelClass = class'NexgenRCPGameInfo'; control.sConf.matchControlPanelClass = class'NexgenRCPMatchControl'; // Get IP-to-country actor. foreach allActors(class'Actor', a, 'IpToCountry') { ipToCountry = a; break; } // Set timer. setTimer(1.0 / timerFreq, true); return true; } /*************************************************************************************************** * * $DESCRIPTION Plugin main loop. * $OVERRIDE * **************************************************************************************************/ function timer() { // Update client country codes. if (ipToCountry != none) { updateCountryCodes(); } } /*************************************************************************************************** * * $DESCRIPTION Updates the country codes for players that don't have a country code set. * **************************************************************************************************/ function updateCountryCodes() { local NexgenClient client; local bool bCancelled; local string ipInfo; // Search for clients with no country codes. client = control.clientList; while (client != none && !bCancelled) { // Country not set? if (client.country == "") { // Get ip info. ipInfo = ipToCountry.getItemName(client.ipAddress); // Parse ip info string. if (left(ipInfo, 1) == "!") { // Status code returned. switch (caps(ipInfo)) { //case "!ADDED TO QUEUE": break //case "!WAITING IN QUEUE": break //case "!RESOLVING NOW": break //case "!AOL - TRYING TO CLARIFY": break case "!BAD INPUT": client.country = "none"; break; //case "!QUEUE FULL": break case "!DISABLED": bCancelled = true; break; } } else if (ipInfo != "") { // Ip information received. if (right(ipInfo, 4) ~= "none") { client.country = "none"; } else { client.country = right(ipInfo, 2); control.announcePlayerAttrChange(client, client.PA_Country, client.country); } } } // Continue with next client. client = client.nextClient; } // Disable IpToCountry support in case of errors. if (bCancelled) { ipToCountry = none; } } /*************************************************************************************************** * * $DESCRIPTION Called when a new client has been created. Use this function to setup the new * client with your own extensions (in order to support the plugin). * $PARAM client The client that was just created. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function clientCreated(NexgenClient client) { client.addController(class'NexgenClientCore'); } /*************************************************************************************************** * * $DESCRIPTION Called whenever a player has joined the game (after its login has been accepted). * $PARAM newClient The player that has joined the game. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerJoined(NexgenClient newClient) { local NexgenClient client; local string args; local string playerName; // Signal player join events, step 1: new client to all others. args = getPlayerJoinEventArgs(newClient); for (client = control.clientList; client != none; client = client.nextClient) { // Cannot send to itself yet, client has to be initialized first. This can be safely done // in step 2 (see clientInitialized). if (client != newClient) { client.playerEvent(newClient.playerNum, client.PE_PlayerJoined, args); } } // Restore saved player data. playerName = class'NexgenUtil'.static.trim(newClient.pDat.get(newClient.PA_Name)); if (control.gInf.bNoNameChange && playerName != "" && playerName != newClient.playerName) { newClient.changeName(playerName); } newClient.bMuted = newClient.pDat.getBool(PA_Muted); newClient.bNoTeamSwitch = newClient.pDat.getBool(PA_NoTeamSwitch); } /*************************************************************************************************** * * $DESCRIPTION Called whenever a client has finished its initialisation process. During this * process things such as the remote control window are created. So only after the * client is fully initialized all functions can be safely called. * $PARAM client The client that has finished initializing. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function clientInitialized(NexgenClient newClient) { local NexgenClient client; local string args; // Signal player join events, step 2: all others to new client. for (client = control.clientList; client != none; client = client.nextClient) { args = getPlayerJoinEventArgs(client); newClient.playerEvent(client.playerNum, client.PE_PlayerJoined, args); } } /*************************************************************************************************** * * $DESCRIPTION Generates a string containing the relevant arguments for a player join event. * $PARAM client The player that has joned the game. * $REQUIRE client != none * $RETURN A string representation of the arguments. * $ENSURE result != "" * **************************************************************************************************/ function string getPlayerJoinEventArgs(NexgenClient client) { local string args; local int teamNum; teamNum = client.team; if (client.bSpectator) { teamNum = 5; } else if (teamNum < 0 || teamNum > 3) { teamNum = 4; } class'NexgenUtil'.static.addProperty(args, client.PA_ClientID, client.playerID); class'NexgenUtil'.static.addProperty(args, client.PA_IPAddress, client.ipAddress); class'NexgenUtil'.static.addProperty(args, client.PA_Name, client.playerName); class'NexgenUtil'.static.addProperty(args, client.PA_Title, client.title); class'NexgenUtil'.static.addProperty(args, client.PA_Team, teamNum); class'NexgenUtil'.static.addProperty(args, client.PA_Country, client.country); return args; } /*************************************************************************************************** * * $DESCRIPTION Called if a player has left the server. * $PARAM oldClient The player that has left the game. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerLeft(NexgenClient oldClient) { local NexgenClient client; // Signal player left events. for (client = control.clientList; client != none; client = client.nextClient) { client.playerEvent(oldClient.playerNum, client.PE_PlayerLeft); } // Store saved player data. oldClient.pDat.set(oldClient.PA_Name, oldClient.playerName); oldClient.pDat.set(PA_Muted, oldClient.bMuted); oldClient.pDat.set(PA_NoTeamSwitch, oldClient.bNoTeamSwitch); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { control.announcePlayerAttrChange(client, client.PA_Team, client.team); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his or her name. * $PARAM client The client that has changed name. * $PARAM oldName The old name of the player. * $PARAM bWasForcedChanged Whether the name change was forced by the controller. * $REQUIRE client != none * $OVERRIDE * **************************************************************************************************/ function playerNameChanged(NexgenClient client, string oldName, bool bWasForcedChanged) { control.announcePlayerAttrChange(client, client.PA_Name, client.playerName); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ QF|"ahUtpppppppp7S|",0_7S{",0_7Sz",0_7Sy",0_7Sx",0  \F "Reconnect"]F "Spectate"^F "Play"_F "Yellow"bF "Green"@ZFw"`Y-c'x  w"Y~x_hY%v"JxYxxY&p-c(-cY~x_Y%u"JxYx  xY&-c(--cY~x_%Y%t"JxYxxY&--c(-cY~x_|Y%s"JxYxxY&-c(-cr"Jx-c  cF "Blue"dF "Red"eF "Team balance"fF "Privilege %1 (not defined)."gFJ "The following privileges / rights are available to you on this server:"pF( "Welcome %1, you are logged in as %2."cnF "D"@ /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenController * $VERSION 1.57 (10-8-2008 11:07) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION The Nexgen Server Controller. * **************************************************************************************************/ class NexgenController extends Mutator config(Nexgen); var config bool bUseExternalConfig; // Use an external configuration file rather than using the // servers system config file. //var config string language; // Language to use. var bool bSpecialMode; // Indicates if the server is running in special mode. // During this mode nexgen is executing a special server // process and the current game isn't open for players. var bool bBootSeqFailed; // True if the Nexgen boot sequence failed. var bool bServerReloaded; // Whether the server has been reloaded after a crash. var bool bIsNexgenBoot; // Whether the current map has been loaded by the Nexgen // boot controller. var bool bIsAdminReboot; // Whether the server has been rebooted by an admin using // the Nexgen control panel. var NexgenConfig sConf; // Server configuration. var NexgenClient clientList; // First client of the linked client list. var NexgenCommandHandler cmdHandler; // Handles the execution of nexgen commands. var NexgenGameInfo gInf; // Extended game info. var NexgenLang lng; // Language instance to support localization. var NexgenPlugin plugins[16]; // Controller plugins. var NexgenTeamBalancer teamBalancer; // Team balancer. var NexgenPlayerData playerDataList; // First player data object of the linked player data list. var class loginHandler; // Client login handler. var NexgenLogEntry logBuffer; // Buffered log messages. var NexgenLogFile logFile; // The log file. var NexgenHTTPClient httpClient; // The HTTP client. var int nextPlayerNum; // Next client ID num. var int joinOverrideCodes[8]; // Override codes for players trying to enter a locked game. var float overrideCodeLeaseTimes[8]; // Time at which an override code was leased. const maxOverrideCodeLeaseTime = 15.0; // Maximum time an override code is valid. const joinOverrideCodeOption = "NXOC"; // Player login option name of the login override code. var bool bUTPureEnabled; // Whether UTPure has been found on the server. // Event detection support variables. var float lastTimeDilation; // Last known time dilation value. var bool bServerTravelDetected; // Has the server travel been detected? // Timer control. var float virtualTimerCounter; // Just like Actor.TimerCounter for our virtual timer. var float timeSeconds; // Just like level.timeSeconds, but independent of the gamespeed. var bool bFirstTickPassed; // Whether the first tick has been executed. // Timings. var float lastPlayerLeftTime; // Time at which the last player has left the server. var float gameStartTime; // Time at which the game has started. var float gameEndTime; // Time at which the game has ended. // Controller settings. const logTag = 'NSC'; // Console log tag. const timerFreq = 10.0; // Frequency of the main timer in Hz. const serverPauserName = "server"; // Name to use for level.pauser when the server pauses the // game instead of a player. const maxBootAttempts = 5; // Maximum number of times Nexgen may try to boot from a // random map. // Nexgen commands. const CMD_Prefix = "NSC"; const CMD_SwitchTeam = "SETTEAM"; const CMD_BalanceTeams = "BALANCETEAMS"; const CMD_Play = "PLAY"; const CMD_Spectate = "SPECTATE"; const CMD_StartGame = "START"; const CMD_Exit = "EXIT"; const CMD_Disconnect = "DISCONNECT"; const CMD_Open = "OPENRCP"; const CMD_OpenVote = "OPENVOTE"; const CMD_Pause = "PAUSE"; // Console commands. const rebootCommand = "debug gpf"; // Server console command for rebooting the server. // Damage types. const suicideDamageType = 'Suicided'; const fallDamageType = 'Fell'; const burnDamageType = 'Burned'; const corrodeDamageType = 'CorrodedMessage'; // Log types. const LT_System = 0; // Nexgen system generated log message. const LT_Event = 1; // Nexgen broadcasted message. const LT_Message = 2; // Mutator broadcasted message. const LT_Say = 3; // Normal chat message. const LT_TeamSay = 4; // Team say chat message. const LT_PrivateMsg = 5; // Private chat message. const LT_AdminAction = 6; // Admin actions. // Reject types. const RT_IllegalLoginParameters = 'illegallogin'; const RT_DuplicateID = 'duplicateid'; const RT_Banned = 'banned'; const RT_InvalidPassword = 'invalidpass'; const RT_ServerFull = 'serverfull'; const RT_NoPlayRight = 'noplayright'; // Misc constants. const secondsPerMinute = 60; // The number of seconds in a minute. const wildcardToken = "*"; // Token used to denote a wildcard. /*************************************************************************************************** * * $DESCRIPTION Starts the Nexgen server controller. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { local int index; // Load language localization support. lng = spawn(class'NexgenLang'); nscLog(lng.startingControllerMsg); // Check server mode. if (level.netMode != NM_DedicatedServer) { nscLog(lng.noDedicatedServerMsg); destroy(); return; } // Check for compatibility issues with other mutators. doCompatibilityCheck(); // Load settings. if (bUseExternalConfig) { sConf = spawn(class'NexgenConfigExt', self); } else { sConf = spawn(class'NexgenConfigSys', self); } if (!sConf.bInstalled) { nscLog(lng.autoInstallMsg); sConf.install(); } // Update configuration to current version. if (sConf.lastInstalledVersion < class'NexgenUtil'.default.versionCode) { nscLog(lng.format(lng.autoUpdateMsg, left(string(class'NexgenUtil'.default.version), 4))); sConf.updateConfig(); } // Check configuration. if (!sConf.checkConfig()) { nscLog(lng.invalidConfigMsg); } // Check boot status. bIsNexgenBoot = sConf.isNexgenBoot; if (isFirstGame() || bIsNexgenBoot) { bServerReloaded = true; bIsAdminReboot = sConf.isAdminReboot; } // Check if nexgen boot should be executed. if (isFirstGame() && (sConf.enableBootControl || sConf.enableMatchBootControl && sConf.matchModeActivated)) { nscLog(lng.nexgenBootMsg); // Execute Nexgen boot. bSpecialMode = doNexgenBoot(); // Check if Nexgen boot has been initialized. if (bSpecialMode) { // Yes, stop the initialization process. sConf.isNexgenBoot = true; sConf.saveConfig(); level.nextSwitchCountdown = 0; // We do not wish to wait another 4 seconds. return; } else { // No, continue in normal mode. nscLog(lng.nexgenBootFailMsg); bBootSeqFailed = true; } } // Clear admin reboot flag. if (bServerReloaded) { sConf.isAdminReboot = false; } // Post initialize (replication) info. sConf.postInitialize(); nscLog(lng.format(lng.attrServerIDMsg, class'NexgenUtil'.static.formatGUID(sConf.serverID))); // Begin file logging (if enabled). if (sConf.logToFile) { logFile = spawn(class'NexgenLogFile', self); } clearLogBuffer(); // Apply configuration. applyConfig(); // Setup current game. initGameInfo(); // Setup for Nexgen controlled game state. if (sConf.enableNexgenStartControl) { DeathMatchPlus(level.game).bNetReady = false; if (bUTPureEnabled) { // Disable UTPure warmup, which doesn't work with NSC. DeathMatchPlus(level.game).countDown = 1; } } // Replace HUD class. if (sConf.useNexgenMessageHUD && !bUTPureEnabled) { setReplacementHUDClass(); } // Load command handler. cmdHandler = spawn(class'NexgenCommandHandler', self); // Load team balancer. if (level.game.isA('TeamGamePlus')) { teamBalancer = spawn(class'NexgenTeamBalancer', self); } // Register controller. nextMutator = level.game.baseMutator; level.game.baseMutator = self; level.game.registerMessageMutator(self); level.game.registerDamageMutator(self); // Set join override codes. for (index = 0; index < arrayCount(joinOverrideCodes); index++) { joinOverrideCodes[index] = -1; } // Load core plugin. spawn(class'NexgenCorePlugin', self); // Let mutator class initialize. super.preBeginPlay(); // Make sure the checksums are up to date. sConf.updateDynamicChecksums(); sConf.updateStaticChecksum(); // Get time dilation value. lastTimeDilation = level.timeDilation; // Set client login handler class. loginHandler = class'NexgenClientLoginHandler'; // Register server in the Nexgen database. if (sConf.autoRegisterServer) { httpClient = spawn(class'NexgenHTTPClient', self); httpClient.registerServer(); } // Start running the main loop (via a timer). setTimer(1.0 / timerFreq * level.timeDilation, true); } /*************************************************************************************************** * * $DESCRIPTION Checks whether this is the first game played since the server was started. * $RETURN True if this is the the first game, false if not. * **************************************************************************************************/ function bool isFirstGame() { return getURLMap() == ""; } /*************************************************************************************************** * * $DESCRIPTION Starts the Nexgen boot sequence. * $RETURN True if the boot sequence was successfully executed, false if not. * **************************************************************************************************/ function bool doNexgenBoot() { local class gameTypeClass; local string randomMap; local string bootURL; local string remaining; local string command; local string result; local int attempts; local bool bMapLoaded; // Check boot method. if (sConf.restartOnLastGame || sConf.enableMatchBootControl && sConf.matchModeActivated) { // Last game + map. // Check last server url. if (sConf.lastServerURL == "") { return false; // Invalid URL. } // Perform the map switch. level.serverTravel(sConf.lastServerURL, false); } else { // Custom game + random map. // Check if game class exists. gameTypeClass = class(dynamicLoadObject(sConf.bootGameType, class'Class')); if (gameTypeClass == none) { return false; // Game class doesn't exist. } // Select a random map. while (!bMapLoaded && attempts < maxBootAttempts) { randomMap = selectRandomBootMap(); if (randomMap != "") { bMapLoaded = preloadMap(randomMap); if (!bMapLoaded) { nscLog(lng.format(lng.nexgenMapLoadFailMsg, randomMap)); } } attempts++; } if (!bMapLoaded) { return false; // Failed to load a map. } // Execute pre switch commands. remaining = sConf.bootCommands; while (remaining != "") { class'NexgenUtil'.static.split(remaining, command, remaining); nscLog(lng.format(lng.execCommandMsg, command)); result = consoleCommand(command); if (result != "") { nscLog("> " $ result); } } // Assemble boot command line string. bootURL = randomMap $ "?game=" $ sConf.bootGameType $ "?mutator=" $ sConf.bootMutators $ sConf.bootOptions; // Perform the map switch. nscLog(lng.format(lng.bootLevelSwitchMsg, randomMap)); level.serverTravel(bootURL, false); } // Boot sequence successfully executed. return true; } /*************************************************************************************************** * * $DESCRIPTION Selects a random map matching the boot map prefix. * $RETURN The filename of the randomly selected map. * **************************************************************************************************/ function string selectRandomBootMap() { local string mapName; local string firstMap; local string shortMapName; local bool bFirst; local int mapCount; local int index; local int randomMapIndex; local string randomMap; // Count number of maps with the specified prefix. mapName = getMapName("", "", 0); firstMap = mapName; bFirst = true; while(mapName != "" && (bFirst || mapName != firstMap)) { bFirst = false; // Valid map? if (left(mapName, len(sConf.bootMapPrefix) + 1) ~= (sConf.bootMapPrefix $ "-") && class'NexgenUtil'.static.isValidLevel(mapName)) { mapCount++; } mapName = getMapName("", mapName, 1); } // Cancel if there are no matching maps. if (mapCount == 0) { return ""; } // Select random map. randomMapIndex = rand(mapCount); mapName = getMapName("", "", 0); firstMap = mapName; bFirst = true; while(mapName != "" && (bFirst || mapName != firstMap) && randomMap == "") { bFirst = false; // Valid map? if (left(mapName, len(sConf.bootMapPrefix) + 1) ~= (sConf.bootMapPrefix $ "-") && class'NexgenUtil'.static.isValidLevel(mapName)) { if (index == randomMapIndex) { randomMap = mapName; } index++; } mapName = getMapName("", mapName, 1); } // Return result. return randomMap; } /*************************************************************************************************** * * $DESCRIPTION Preloads the specified map. * $REQUIRE mapName != "" * $RETURN True if the specified map was successfully loaded, false if not. * **************************************************************************************************/ function bool preloadMap(string mapName) { local object levelSummary; local string lvlSummaryObjectStr; local int index; // Get level summary object name. index = instr(mapName, "."); if (index >= 0) { lvlSummaryObjectStr = left(mapName, index); } else { lvlSummaryObjectStr = mapName; } lvlSummaryObjectStr = lvlSummaryObjectStr $ ".LevelSummary"; // Attempt to load level summary. levelSummary = dynamicLoadObject(lvlSummaryObjectStr, class'LevelSummary', true); // Return result. return levelSummary != none; } /*************************************************************************************************** * * $DESCRIPTION Applies the current configuration to the server. * $REQUIRE sConf != none * **************************************************************************************************/ function applyConfig() { // Apply global server settings. consoleCommand("set Engine.GameInfo GamePassword" @ sConf.decode(sConf.globalServerPassword)); consoleCommand("set Engine.GameInfo AdminPassword" @ sConf.decode(sConf.globalAdminPassword)); level.game.gameReplicationInfo.serverName = sConf.serverName; level.game.gameReplicationInfo.shortName = sConf.shortName; level.game.gameReplicationInfo.adminName = sConf.adminName; level.game.gameReplicationInfo.adminEmail = sConf.adminEmail; level.game.maxPlayers = sConf.playerSlots + sConf.vipSlots + sConf.adminSlots; level.game.maxSpectators = sConf.spectatorSlots; if (sConf.enableUplink) { consoleCommand("set IpServer.UdpServerUplink DoUplink True"); } else { consoleCommand("set IpServer.UdpServerUplink DoUplink False"); } level.game.gameReplicationInfo.MOTDLine1 = sConf.MOTDLine[0]; level.game.gameReplicationInfo.MOTDLine2 = sConf.MOTDLine[1]; level.game.gameReplicationInfo.MOTDLine3 = sConf.MOTDLine[2]; level.game.gameReplicationInfo.MOTDLine4 = sConf.MOTDLine[3]; } /*************************************************************************************************** * * $DESCRIPTION Initializes the extended game (replication) info. * $REQUIRE sConf != none * $ENSURE gInf != none * **************************************************************************************************/ function initGameInfo() { // Setup current game. gInf = spawn(class'NexgenGameInfo', self); gInf.gameState = gInf.GS_Waiting; gInf.countDown = sConf.waitTime; gInf.gameSpeed = level.game.gameSpeed; if (level.game.isA('TeamGamePlus')) { gInf.maxTeams = TeamGamePlus(level.game).maxTeams; } gInf.bNoTeamSwitch = !sConf.allowTeamSwitch; gInf.bNoTeamBalance = !sConf.allowTeamBalance; gInf.bNoNameChange = !sConf.allowNameChange; gInf.updateCount = 1; if (level.game.isA('DeathMatchPlus')) { gInf.bTournamentMode = DeathMatchPlus(level.game).bTournament; if (sConf.enableNexgenStartControl) { doTournamentModeReadySignalCheck(); } } } /*************************************************************************************************** * * $DESCRIPTION Attemps to replace the default HUD class with a Nexgen HUD class. * $RETURN True if the default HUD has been successfully replaced. * $ENSURE result == true ? classIsChildOf(new.level.game.HUDType, old.level.game.HUDType) : true * **************************************************************************************************/ function bool setReplacementHUDClass() { local string hudClassName; local string hudReplaceClassName; local string srcHudClassName; local class replacementClass; local bool bFound; local int index; local int tokenIndex; // Find a suitable replacement class for the current HUD class. hudClassName = string(level.game.HUDType); while (!bFound && index < arrayCount(sConf.replacementClass) && sConf.replacementClass[index] != "") { tokenIndex = instr(sConf.replacementClass[index], "="); if (tokenIndex > 0) { srcHudClassName = left(sConf.replacementClass[index], tokenIndex); if (srcHudClassName ~= hudClassName || srcHudClassName ~= wildcardToken) { // Get replacement class. hudReplaceClassName = mid(sConf.replacementClass[index], tokenIndex + 1); replacementClass = class(dynamicLoadObject(hudReplaceClassName, class'Class')); // Valid replacement class? if (replacementClass != none && (replacementClass == class'NexgenHUDWrapper' || classIsChildOf(replacementClass, level.game.HUDType))) { bFound = true; } else { index++; } } else { index++; } } } // Apply new HUD class if found. if (bFound) { //level.game.HUDType = replacementClass; sConf.HUDReplacementClass = replacementClass; } else { nscLog(lng.format(lng.noHUDReplacementClassMsg, hudClassName)); } // Return result. return bFound; } /*************************************************************************************************** * * $DESCRIPTION Registers a new controller plugin. Since there a limit to the amount of plugins * this action may fail. * $PARAM plugin The plugin to register. * $REQUIRE plugin != none * $RETURN True if the plugin was succesfully added to the server controller, false if the * plugin limit has been reached. * **************************************************************************************************/ function bool registerPlugin(NexgenPlugin plugin) { local bool bFound; local int index; // Locate empty slot. while (!bFound && index < arrayCount(plugins)) { if (plugins[index] == none) { bFound = true; plugins[index] = plugin; // Store plugin in this free slot. } else { index++; } } // Return result. return bFound; } /*************************************************************************************************** * * $DESCRIPTION Handles the connection of a new client. * $PARAM client The playerpawn instance of the new client. * $REQUIRE client != none * **************************************************************************************************/ function newClient(PlayerPawn client) { local NexgenClient clientHandler; local int index; // Create new client handler. clientHandler = spawn(class'NexgenClient', client); // Set attributes. clientHandler.serverID = sConf.serverID; clientHandler.control = self; clientHandler.sConf = sConf; clientHandler.gInf = gInf; clientHandler.lng = lng; clientHandler.playerNum = nextPlayerNum++; clientHandler.loginHandler = loginHandler; clientHandler.loginHandlerChecksum = class'NexgenUtil'.static.stringHash(string(loginHandler)); // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].clientCreated(clientHandler); index++; } } /*************************************************************************************************** * * $DESCRIPTION Called when a player attempts to login to the server. Allows mutators to modify * some of the login parameters. * $PARAM spawnClass The PlayerPawn class to use for the player. * $PARAM portal Name of the portal where the player wishes to spawn. * $PARAM option Login option parameters. * **************************************************************************************************/ function modifyLogin(out class spawnClass, out string portal, out string options) { local int overrideCode; local int index; // Only continue if the login wasn't denied. if (spawnClass == none) { return; } // Check if the player isn't allowed to join the game as player. overrideCode = level.game.getIntOption(options, joinOverrideCodeOption, -1); if (gInf.bTeamsLocked && !isValidJoinOverrideCode(overrideCode)) { spawnClass = class'Botpack.CHSpectator'; } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyLogin(spawnClass, portal, options); index++; } // Allow other mutators to do their job. if (nextMutator != none) { nextMutator.modifyLogin(spawnClass, portal, options); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the given login override code is correct. * **************************************************************************************************/ function bool isValidJoinOverrideCode(int overrideCode) { local bool bValid; local bool bInvalid; local int index; // Check override code. while (!bValid && !bInvalid && index < arrayCount(joinOverrideCodes)) { if (joinOverrideCodes[index] == overrideCode) { // Check if lease has expired. if (timeSeconds - overrideCodeLeaseTimes[index] <= maxOverrideCodeLeaseTime) { bValid = true; } else { bInvalid = true; } // Clear override code. joinOverrideCodes[index] = -1; overrideCodeLeaseTimes[index] = 0; } else { index++; } } // Return result. return bValid; } /*************************************************************************************************** * * $DESCRIPTION Gives the specified client a login override code, so he or she can join a locked * game as player. * **************************************************************************************************/ function giveJoinOverrideCode(NexgenClient client) { local int index; local bool bFound; // Find a free slot. while (!bFound && index < arrayCount(joinOverrideCodes)) { // Check if current slot is free. if (joinOverrideCodes[index] < 0 || timeSeconds - overrideCodeLeaseTimes[index] > maxOverrideCodeLeaseTime) { // Slot is free, use this one. bFound = true; } else { // Nope, maybe next one. index++; } } // Create override code & send to client. if (bFound) { joinOverrideCodes[index] = rand(maxInt); overrideCodeLeaseTimes[index] = timeSeconds; client.updateLoginOption(joinOverrideCodeOption, joinOverrideCodes[index]); } } /*************************************************************************************************** * * $DESCRIPTION Checks if the login request of the specified client should be accepted. If the * request is rejected, this function will automatically kill the client. * $PARAM client The client whose login request is to be checked. * $REQUIRE client != none * **************************************************************************************************/ function checkLogin(NexgenClient client) { local string password; local bool bRejected; local string reason; local string banReason; local string banPeriod; local bool allowSpecReconnect; local int index; local int k; local string cs; local name rejectType; local string popupWindowClass; local string popupArgs[4]; // Get login options. password = class'NexgenUtil'.static.getProperty(client.loginOptions, client.SSTR_ServerPassword); // Log the login request. nscLog(lng.format(lng.loginRequestMsg, client.playerName)); nscLog(lng.format(lng.attrClientIPMsg, client.ipAddress)); nscLog(lng.format(lng.attrClientIDMsg, class'NexgenUtil'.static.formatGUID(client.playerID))); if (password != "") { nscLog(lng.format(lng.attrPasswordMsg, password)); } // Check login parameters. if (!loginHandler.static.checkLoginParameters(client)) { bRejected = true; rejectType = RT_IllegalLoginParameters; reason = lng.illegalLoginParametersMsg; popupWindowClass = "NexgenIllegalLoginDialog"; } // Check for duplicate ID's. if ((!bRejected) && !hasUniqueKey(client)) { bRejected = true; rejectType = RT_DuplicateID; reason = lng.duplicateIDMsg; popupWindowClass = "NexgenIDUsedDialog"; } // Check for bans. if ((!bRejected) && isBanned(client, banReason, banPeriod)) { bRejected = true; rejectType = RT_Banned; reason = lng.bannedMsg; popupWindowClass = "NexgenBannedDialog"; popupArgs[0] = banReason; popupArgs[1] = banPeriod; } // Check password. if ((!bRejected) && (sConf.matchModeActivated) && (sConf.serverPassword != "") && (password != sConf.decode(sConf.serverPassword)) && (!client.hasRight(client.R_NeedsNoPW)) && (!client.bSpectator || sConf.spectatorsNeedPassword)) { bRejected = true; rejectType = RT_InvalidPassword; reason = lng.invalidPassMsg; allowSpecReconnect = !client.bSpectator && !sConf.spectatorsNeedPassword; popupWindowClass = "NexgenPasswordDialog"; popupArgs[0] = string(allowSpecReconnect); } // Check slots. if ((!bRejected) && !canGetSlot(client)) { bRejected = true; rejectType = RT_ServerFull; reason = lng.serverCapacityMsg; popupWindowClass = "NexgenServerFullDialog"; popupArgs[0] = string(sConf.playerSlots); popupArgs[1] = string(sConf.vipSlots); popupArgs[2] = string(sConf.adminSlots); } // Check play rights. if ((!bRejected) && (!client.bSpectator) && (!client.hasRight(client.R_MayPlay))) { bRejected = true; rejectType = RT_NoPlayRight; reason = lng.noPlayRightMsg; popupWindowClass = "NexgenNoPlayRightDialog"; } // Get player data object for this client. setPlayerData(client); // Check with plugins. while (!bRejected && index < arrayCount(plugins) && plugins[index] != none) { bRejected = !plugins[index].checkLogin(client, rejectType, reason, popupWindowClass, popupArgs); index++; } // Accept or reject player. if (bRejected) { // Allow plugins to modify the rejection of this player. index = 0; while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyLoginReject(client, rejectType, reason, popupWindowClass, popupArgs); index++; } // Reject the player. if (popupWindowClass != "") { client.showPopup(popupWindowClass, popupArgs[0], popupArgs[1], popupArgs[2], popupArgs[3]); } disconnectClient(client); nscLog(lng.format(lng.loginRejectedMsg, reason)); } else { // Send encryption paramters. Only players with an account receive the right paramters. if (client.bHasAccount) { sConf.getEncryptionParams(k, cs); } client.setEncryptionParams(k, cs); // Signal events. nscLog(lng.loginAcceptedMsg); playerJoined(client); } } /*************************************************************************************************** * * $DESCRIPTION Checks if the player ID of the specified client is unique. * $PARAM client The client whose player ID is to be checked. * $REQUIRE client != none * $RETURN True none of the other clients has the same player ID, false otherwise. * **************************************************************************************************/ function bool hasUniqueKey(NexgenClient client) { local bool bUnique; local NexgenClient currClient; // Compare each ID of the other clients with the ID of the specified client. bUnique = true; currClient = clientList; while (bUnique && currClient != none) { // Same ID? if (currClient != client && currClient.playerID ~= client.playerID) { bUnique = false; } // Compare ID with the next client. currClient = currClient.nextClient; } // Return result. return bUnique; } /*************************************************************************************************** * * $DESCRIPTION Checks if the specified client is banned on this server. * $PARAM client The client for which the ban is to be checked. * $PARAM banReason Description of why the client was banned. * $PARAM banPeriod Textual description indicating how long the player is banned. * $REQUIRE client != none * $RETURN True if the client is banned, false if not. * $ENSURE result == true ? new.banPeriod != "" : true * **************************************************************************************************/ function bool isBanned(NexgenClient client, out string banReason, out string banPeriod) { local int banIndex; local bool bBanned; // Get ban entry. banIndex = sConf.getBanIndex(client.playerName, client.ipAddress, client.playerID); // Check if player is banned and the ban hasn't expired. if (banIndex >= 0) { if (sConf.autoUpdateBans) { if (sConf.updateBan(banIndex, client.ipAddress, client.playerID)) { // Ban entry was changed, update dynamic config data checksum & notify clients. signalConfigUpdate(sConf.CT_BanList); } } banReason = sConf.banReason[banIndex]; banPeriod = lng.getBanPeriodDescription(sConf.banPeriod[banIndex]); bBanned = !sConf.isExpiredBan(banIndex); } // Return result. return bBanned; } /*************************************************************************************************** * * $DESCRIPTION Checks if the specified client can get a player slot on the server, i.e. the * server isn't full for this client. * $PARAM client The client for which is to be checked if a slot available. * $REQUIRE client != none * $RETURN True if there is an empty slot available that the specified client may occupy, * false if not. * **************************************************************************************************/ function bool canGetSlot(NexgenClient client) { local NexgenClient currClient; local bool hasVIPSlotAccess; local bool hasAdminSlotAccess; local int playerCount; local int vipCount; local int adminCount; local int vipAdminCount; local int specialSlotsUsed; // Spectators can always get a slot. if (client.bSpectator) { return true; } // Count used slots. currClient = clientList; while (currClient != none) { // Count all players, except the specified client and spectators. if (currClient != client && !currClient.bSpectator) { hasVIPSlotAccess = currClient.hasRight(client.R_VIPAccess); hasAdminSlotAccess = currClient.hasRight(client.R_AdminAccess); if (hasVIPSlotAccess && hasAdminSlotAccess) { vipAdminCount++; } else if (hasVIPSlotAccess) { vipCount++; } else if (hasAdminSlotAccess) { adminCount++; } else { playerCount++; } } // Next player. currClient = currClient.nextClient; } // Get slot access level for the specified client. hasVIPSlotAccess = client.hasRight(client.R_VIPAccess); hasAdminSlotAccess = client.hasRight(client.R_AdminAccess); // Check slot access. if (hasVIPSlotAccess && hasAdminSlotAccess) { return (playerCount + vipCount + adminCount + vipAdminCount) < (sConf.playerSlots + sConf.vipSlots + sConf.adminSlots); } else if (hasVIPSlotAccess) { specialSlotsUsed = max(0, vipAdminCount - (sConf.adminSlots - adminCount)) + vipCount; return (playerCount + specialSlotsUsed) < (sConf.playerSlots + sConf.vipSlots); } else if (hasAdminSlotAccess) { specialSlotsUsed = max(0, vipAdminCount - (sConf.vipSlots - vipCount)) + adminCount; return (playerCount + specialSlotsUsed) < (sConf.playerSlots + sConf.adminSlots); } else { return playerCount < sConf.playerSlots; } } /*************************************************************************************************** * * $DESCRIPTION Sets the player data object for the specified client. * $PARAM client The client for which the player data object has to be set. * $REQUIRE client != none * $ENSURE client.pDat != none && client.pDat.clientID ~= client.playerID * **************************************************************************************************/ function setPlayerData(NexgenClient client) { local NexgenPlayerData pDat; local bool bFound; // Search for saved player data object for this client. pDat = playerDataList; while (!bFound && pDat != none) { if (pDat.clientID ~= client.playerID) { bFound = true; } else { pDat = pDat.next; } } // Create player data object if necessary. if (!bFound) { // Create & initialize player data object. pDat = spawn(class'NexgenPlayerData', self); pDat.clientID = client.playerID; // Add player data object to the list pDat.next = playerDataList; playerDataList = pDat; } // Set player data object for the client. client.pDat = pDat; } /*************************************************************************************************** * * $DESCRIPTION Disconnects the specified client from this server. * $PARAM client The client that is to be disconnected from the server. * $REQUIRE client != none * **************************************************************************************************/ function disconnectClient(NexgenClient client) { // Remove client from the client list. removeClientHandler(client); // Close connection. client.player.destroy(); // Is this safe? At least thats how Epic does it. client.destroy(); } /*************************************************************************************************** * * $DESCRIPTION Removes the specified client from the client list. * $PARAM client The client that is to be removed. * $REQUIRE client != none * **************************************************************************************************/ function removeClientHandler(NexgenClient client) { local NexgenClient currClient; local bool bDone; // Remove the client from the linked client list. if (clientList == client) { // First element in the list. clientList = client.nextClient; } else { // Somewhere else in the list. currClient = clientList; while (!bDone && currClient != none) { if (currClient.nextClient == client) { bDone = true; currClient.nextClient = client.nextClient; } else { currClient = currClient.nextClient; } } } } /*************************************************************************************************** * * $DESCRIPTION Called when a player (re)spawns and allows us to modify the player. * $PARAM other The pawn/player that has (re)spawned. * $REQUIRE other != none * **************************************************************************************************/ function modifyPlayer(Pawn other) { local NexgenClient client; local int index; // Detect game start. if (!sConf.enableNexgenStartControl && DeathMatchPlus(level.game).bStartMatch && gInf.gameState == gInf.GS_Waiting) { gInf.gameState = gInf.GS_Playing; gameStarted(); } // Get client. client = getClient(other); // Signal event. if (client != none) { // Signal event on client. client.respawned(); // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerRespawned(client); index++; } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].modifyPlayer(other); index++; } // Let other mutators do their job. if (nextMutator != none) { nextMutator.modifyPlayer(other); } } /*************************************************************************************************** * * $DESCRIPTION Accepts the login request for the specified client. * $PARAM client The client whose login request has been accepted. * $REQUIRE client != none && !client.loginComplete * **************************************************************************************************/ function playerJoined(NexgenClient client) { local int index; local bool bFound; // Add client to the client list. client.nextClient = clientList; clientList = client; // Set player team if auto separate is enabled. if (!client.bSpectator && level.game.isA('TeamGamePlus') && sConf.matchModeActivated && sConf.matchAutoSeparate) { while (!bFound && index < arrayCount(sConf.tagsToSeparate) && index < TeamGamePlus(level.game).maxTeams) { // Check if player has a tag that separates him/her. if (instr(caps(client.playerName), caps(sConf.tagsToSeparate[index])) >= 0) { bFound = true; if (client.player.playerReplicationInfo.team != index) { client.setTeam(index); } } else { index++; } } } // Update client attributes. client.team = client.player.playerReplicationInfo.team; client.lastSwitchTime = timeSeconds; client.loginComplete = true; if (gInf.gameState == gInf.GS_Playing && !client.bSpectator) { client.spawnProtectionTimeX = sConf.spawnProtectionTime; } // Update game info. if (client.hasRight(client.R_MatchAdmin)) { gInf.matchAdminCount++; } // Notify plugins. index = 0; while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerJoined(client); index++; } // Notify other players. broadcastMsg(lng.playerJoinMsg, client.playerName, client.title, , , client.player.playerReplicationInfo); // Update tournament start status. if (sConf.enableNexgenStartControl && gInf.bTournamentMode && gInf.gameState == gInf.GS_Ready) { doTournamentModeReadySignalCheck(); } } /*************************************************************************************************** * * $DESCRIPTION Handles the events where a client has been initialized. * $PARAM client The client that has just been initialized. * $REQUIRE client != none && client.loginComplete * **************************************************************************************************/ function clientInitialized(NexgenClient client) { local int index; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].clientInitialized(client); index++; } // Show message if the server has crashed. if (bServerReloaded && gInf.gameState < gInf.GS_Playing) { if (bIsAdminReboot) { client.showMsg(lng.serverAdminRebootClientMsg); } else { client.showMsg(lng.serverCrashedClientMsg); } } // Send warning to the client in case the Nexgen boot sequence has failed. if (bBootSeqFailed) { client.showMsg(lng.bootFailedClientMsg); } // Show game is ready to launch message. if (gInf.gameState == gInf.GS_Ready) { showGameReadyToLaunchMessage(client); } } /*************************************************************************************************** * * $DESCRIPTION Handles the leaving of the specified client. * $PARAM client The client that has left the server. * $REQUIRE client.owner == none * **************************************************************************************************/ function playerLeft(NexgenClient client) { local int index; // Remove client from the client list. removeClientHandler(client); // Clear player data (data should be added by the plugins). client.pDat.clearData(); // Update game info. if (client.hasRight(client.R_MatchAdmin)) { gInf.matchAdminCount--; } lastPlayerLeftTime = timeSeconds; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerLeft(client); index++; } // Notify other players. broadcastMsg(lng.playerLeaveMsg, client.playerName, client.title); // Log event. nscLog(lng.format(lng.playerLeaveLogMsg, client.playerName)); // Pause the game? if (sConf.matchModeActivated && sConf.matchAutoPause && clientList != none && gInf.gameState == gInf.GS_Playing && gInf.matchAdminCount > 0 && !client.bSpectator) { level.pauser = serverPauserName; } // Unpause the game? if (level.pauser != "" && gInf.matchAdminCount == 0) { //level.pauser = ""; } // Update tournament start status. if (sConf.enableNexgenStartControl && gInf.bTournamentMode && gInf.gameState == gInf.GS_Ready) { clearReadySignals(); doTournamentModeReadySignalCheck(); } // No longer need the client handler. client.destroy(); } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has changed his/her name during the game. * $PARAM client The client that has changed his/her name. * $REQUIRE client.playerName != client.player.playerReplicationInfo.playerName * **************************************************************************************************/ function playerNameChanged(NexgenClient client) { local bool bAllowChange; local string reason; local string oldName; local int index; local bool bWasForcedChanged; // Check if the name change should be allowed. if (timeSeconds - client.nameChangeOverrideTime <= client.maxOverrideTime) { client.nameChangeOverrideTime = 0; // Clear admin override flag. bAllowChange = true; bWasForcedChanged = true; } else if (gInf.bNoNameChange) { reason = lng.nameChangeDisabled; } else { bAllowChange = true; } // Update name if allowed. if (bAllowChange) { // Update player name. oldName = client.playerName; client.playerName = client.player.playerReplicationInfo.playerName; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerNameChanged(client, oldName, bWasForcedChanged); index++; } // Notify other players. if (!bWasForcedChanged && oldName != client.player.playerReplicationInfo.playerName) { broadcastMsg(lng.playerNameChangeMsg, oldName, client.playerName, , , client.player.playerReplicationInfo); } } else { // Reset original name. client.changeName(client.playerName); sendMsg(client, lng.nameChangeFailMsg, reason); } } /*************************************************************************************************** * * $DESCRIPTION Deals with a client that has switched to another team. * $PARAM client The client that has changed team. * $REQUIRE client.team != client.player.playerReplicationInfo.team * **************************************************************************************************/ function playerTeamChanged(NexgenClient client) { local bool bAllowSwitch; local string reason; local int index; // Restore players score? if (sConf.restoreScoreOnTeamSwitch && timeSeconds - client.teamSwitchOverrideTime <= client.maxOverrideTime && client.player.playerReplicationInfo.score == client.scoreBeforeTeamSwitch - 1) { client.player.playerReplicationInfo.score = client.scoreBeforeTeamSwitch; } // Check if the teamswitch should be allowed. if (timeSeconds - client.teamSwitchOverrideTime <= client.maxOverrideTime) { client.teamSwitchOverrideTime = 0; // Clear admin override flag. bAllowSwitch = true; } else if (gInf.bNoTeamSwitch) { reason = lng.teamSwitchDisabledMsg; } else if (gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if (client.bNoTeamSwitch) { reason = lng.playerTeamSwitchDisabledMsg; } else { bAllowSwitch = true; } // Update team if allowed. if (bAllowSwitch) { client.team = client.player.playerReplicationInfo.team; client.lastSwitchTime = timeSeconds; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].playerTeamChanged(client); index++; } } else { // Switch back to original team. client.setTeam(client.team); sendMsg(client, lng.teamSwitchFailMsg, reason); } } /*************************************************************************************************** * * $DESCRIPTION Called when a pawn takes damage. * $PARAM actualDamage The amount of damage sustained by the pawn. * $PARAM victim Pawn that has become victim of the damage. * $PARAM instigatedBy The pawn that has instigated the damage to the victim. * $PARAM hitLocation Location where the damage was dealt. * $PARAM momentum Momentum of the damage that has been dealt. * $PARAM damageType Type of damage dealt to the victim. * $REQUIRE victim != none * $OVERRIDE * **************************************************************************************************/ function mutatorTakeDamage(out int actualDamage, Pawn victim, Pawn instigatedBy, out vector hitLocation, out vector momentum, name damageType) { local NexgenClient client; local byte bPreventDamage; local byte bResetPlayer; local int index; // Get client. client = getClient(victim); // Check if damage should be prevented. if (client != none && damageType != suicideDamageType) { checkPreventDamage(client, instigatedBy, damageType, actualDamage, bPreventDamage, bResetPlayer); // Prevent the damage. if (bool(bPreventDamage)) { actualDamage = 0; if (bool(bResetPlayer)) { level.game.restartPlayer(client.player); client.showMsg(lng.deathPreventedMsg); } } // Team kill? if (victim != instigatedBy && level.game.gameReplicationInfo.bTeamGame && instigatedBy != none && (instigatedBy.isA('PlayerPawn') || instigatedBy.isA('Bot')) && victim.playerReplicationInfo.team == instigatedBy.playerReplicationInfo.team) { // Yes, prevent damage & protect victim. client.tkPushProtectionTimeX = sConf.teamKillPushProtectionTime; if (!bool(bPreventDamage)) { // Damage hasn't been prevented yet. actualDamage = 0; client.tkDmgProtectionTimeX = sConf.teamKillDamageProtectionTime; // Notify players if desired. if (sConf.broadcastTeamKillAttempts) { broadcastMsg(lng.teamKillAttemptMsg, instigatedBy.playerReplicationInfo.playerName, victim.playerReplicationInfo.playerName, , , instigatedBy.playerReplicationInfo); } } } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutatorTakeDamage(actualDamage, victim, instigatedBy, hitLocation, momentum, damageType); index++; } // Let other mutators do their job. if (nextDamageMutator != none) { nextDamageMutator.mutatorTakeDamage(actualDamage, victim, instigatedBy, hitLocation, momentum, damageType); } } /*************************************************************************************************** * * $DESCRIPTION Called when the server wants to check if a players death should be prevented. * $PARAM victim The pawn that was killed. * $PARAM killer The pawn that has killed the victim. * $PARAM damageType Type of damage dealt to the victim. * $PARAM hitLocation Location where the damage was dealt. * $RETURN True if the players death should be prevented, false if not. * $OVERRIDE * **************************************************************************************************/ function bool preventDeath(Pawn victim, Pawn killer, name damageType, vector hitLocation) { local NexgenClient client; local byte bPreventDamage; local byte bResetPlayer; local int index; // Get client. client = getClient(victim); // Check if damage should be prevented. if (client != none && damageType != suicideDamageType) { checkPreventDamage(client, killer, damageType, 99999, bPreventDamage, bResetPlayer); // Prevent the damage. if (bool(bPreventDamage)) { client.player.health = 100; if (bool(bResetPlayer)) { level.game.restartPlayer(client.player); client.showMsg(lng.deathPreventedMsg); } return true; } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { if (plugins[index].preventDeath(victim, killer, damageType, hitLocation)) return true; index++; } // Let other mutators do their job. if (nextMutator == none) { return false; } else { return nextMutator.preventDeath(victim, killer, damageType, hitLocation); } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified damage to the client should be prevented. * $PARAM client The client for which the damage prevention check should be executed. * $PARAM instigator The pawn that has instigated the damage to the victim. * $PARAM damageType Type of damage the player has sustained. * $PARAM damage The amount of damage sustained by the client. * $PARAM bPreventDamage Whether the damage should be prevented or not. * $PARAM bResetPlayer Indicates if the player should restart if the damage is prevented. * $REQUIRE client != none * **************************************************************************************************/ function checkPreventDamage(NexgenClient client, Pawn instigator, name damageType, int damage, out byte bPreventDamage, out byte bResetPlayer) { // Check if player has switched to another team. if (client.team != client.player.playerReplicationInfo.team) { // Yes, don't prevent the damage. bPreventDamage = byte(false); bResetPlayer = byte(false); return; } // Spawn protection. if (client.spawnProtectionTimeX > 0) { bPreventDamage = byte(true); bResetPlayer = byte(client.player.playerReplicationInfo.hasFlag == none && (damageType == fallDamageType && client.player.health <= damage || damageType == burnDamageType || damageType == corrodeDamageType)); } // Team kill damage & push protection. if (!bool(bPreventDamage)) { bPreventDamage = byte(client.tkDmgProtectionTimeX > 0 || client.tkPushProtectionTimeX > 0 && (damageType == fallDamageType || damageType == burnDamageType || damageType == corrodeDamageType || instigator == none)); bResetPlayer = byte(client.tkPushProtectionTimeX > 0 && client.player.playerReplicationInfo.hasFlag == none && (damageType == fallDamageType && client.player.health <= damage || damageType == burnDamageType || damageType == corrodeDamageType)); } } /*************************************************************************************************** * * $DESCRIPTION Attempts to balance the current teams. * $RETURN True if the teams have been balanced, false if they are already balanced. * **************************************************************************************************/ function bool balanceTeams() { if (teamBalancer == none) { return false; } else { return teamBalancer.balanceTeams(); } } /*************************************************************************************************** * * $DESCRIPTION Starts the current game. * $REQUIRE gInf.gameState == gInf.GS_Ready || gInf.gameState == gInf.GS_Starting * $ENSURE gInf.gameState == gInf.GS_Playing; * **************************************************************************************************/ function startGame() { // Start the game immediately? if (!sConf.enableNexgenStartControl || gInf.gameState == gInf.GS_Ready && sConf.startTime <= 0 || gInf.gameState == gInf.GS_Starting && gInf.countDown <= 0) { // Yes. gInf.gameState = gInf.GS_Playing; DeathMatchPlus(level.game).bRequireReady = false; DeathMatchPlus(level.game).startMatch(); gameStarted(); } else if (gInf.gameState == gInf.GS_Ready && sConf.startTime > 0) { // No, delayed start. gInf.gameState = gInf.GS_Starting; gInf.countDown = sConf.startTime; } } /*************************************************************************************************** * * $DESCRIPTION Logs the given message. * $PARAM msg Message that should be written to the log. * $PARAM logType The type of log message. * **************************************************************************************************/ function nscLog(string msg, optional byte logType) { if (shouldLog(logType)) { // Log to console (stdout). if (sConf == none || sConf.logToConsole || logType == LT_System) { log(getLogTypeTag(logType) @ msg, logTag); } // Log to file. if (sConf == none || sConf.logToFile && logFile == none) { addLogBufferEntry(msg, logType); } else if (logFile != none) { logFile.addLog(msg, logType); } } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the log tag for the specified log type. * $PARAM logType The type of log message. * $RETURN The tag that should be prepended for log messages of the specified type. * **************************************************************************************************/ function string getLogTypeTag(byte logType) { switch (logType) { case LT_Event: return lng.eventLogTag; case LT_Message: return lng.messageLogTag; case LT_Say: return lng.chatMessageLogTag; case LT_TeamSay: return lng.teamSayMessageLogTag; case LT_PrivateMsg: return lng.privateMessageLogTag; case LT_AdminAction: return lng.adminActionLogTag; default: return lng.controllerSystemLogTag; } } /*************************************************************************************************** * * $DESCRIPTION Checks whether messages of the specified log type should be written to the log. * $PARAM logType The type of log message. * $RETURN True if the message of this type should be logged, false if not. * **************************************************************************************************/ function bool shouldLog(byte logType) { switch (logType) { case LT_Event: return sConf.logEvents; case LT_Message: return sConf.logSystemMessages; case LT_Say: return sConf.logChatMessages; case LT_TeamSay: return sConf.logChatMessages; case LT_PrivateMsg: return sConf.logPrivateMessages; case LT_AdminAction: return sConf.logAdminActions; default: return true; } } /*************************************************************************************************** * * $DESCRIPTION Adds the specified logs to the log buffer. * $PARAM msg Message that should be written to the log. * $PARAM logType The type of log message. * **************************************************************************************************/ function addLogBufferEntry(string msg, optional byte logType) { local NexgenLogEntry newLog; local NexgenLogEntry logEntry; // Create log entry. newLog = spawn(class'NexgenLogEntry', self); newLog.message = msg; newLog.type = logType; newLog.year = level.year; newLog.month = level.month; newLog.day = level.day; newLog.dayOfWeek = level.dayOfWeek; newLog.hour = level.hour; newLog.minute = level.minute; newLog.second = level.second; // Add new log entry to the linked list. if (logBuffer == none) { // First entry. logBuffer = newLog; } else { // Find last entry and append new log entry. logEntry = logBuffer; while (logEntry.nextLogEntry != none) logEntry = logEntry.nextLogEntry; logEntry.nextLogEntry = newLog; } } /*************************************************************************************************** * * $DESCRIPTION Clears the log buffer. * **************************************************************************************************/ function clearLogBuffer() { if (logBuffer != none) { logBuffer.destroy(); logBuffer = none; } } /*************************************************************************************************** * * $DESCRIPTION Logs the specified administrator action. * $PARAM admin The client that performed the action. * $PARAM msg Message that describes the action performed by the administrator. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM str4 Message specific content. * $PARAM pri Replication info of the player related to this message. * $PARAM bNoBroadcast Whether not to broadcast this administrator action. * $REQUIRE admin != none * **************************************************************************************************/ function logAdminAction(NexgenClient admin, string msg, optional string str1, optional string str2, optional string str3, optional string str4, optional PlayerReplicationInfo pri, optional bool bNoBroadcast) { local NexgenClient client; local bool bSendToAdminsOnly; // Format message. msg = class'NexgenUtil'.static.format(msg, str1, str2, str3, str4); // Log message. nscLog(class'NexgenUtil'.static.removeMessageColorTag(msg), LT_AdminAction); // Send message to all clients. bSendToAdminsOnly = bNoBroadcast || admin.hasRight(admin.R_HiddenAdmin) || !sConf.broadcastAdminActions; for (client = clientList; client != none; client = client.nextClient) { if (!bSendToAdminsOnly || client.bHasAccount) { client.showMsg(msg, pri); } } } /*************************************************************************************************** * * $DESCRIPTION Broadcasts the given message to all connected clients. * $PARAM msg Message that should send to all the clients. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM str4 Message specific content. * $PARAM pri Replication info of the player related to this message. * **************************************************************************************************/ function broadcastMsg(string msg, optional string str1, optional string str2, optional string str3, optional string str4, optional PlayerReplicationInfo pri) { local NexgenClient client; // Format message. msg = class'NexgenUtil'.static.format(msg, str1, str2, str3, str4); // Log message. nscLog(class'NexgenUtil'.static.removeMessageColorTag(msg), LT_Event); // Send message to all clients. for (client = clientList; client != none; client = client.nextClient) { client.showMsg(msg, pri); } } /*************************************************************************************************** * * $DESCRIPTION Broadcasts the given message to all connected clients. * $PARAM client The client to which the message should be send. * $PARAM msg Message that should send to all the clients. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM str4 Message specific content. * $PARAM pri Replication info of the player related to this message. * **************************************************************************************************/ function sendMsg(NexgenClient client, string msg, optional string str1, optional string str2, optional string str3, optional string str4, optional PlayerReplicationInfo pri) { msg = class'NexgenUtil'.static.format(msg, str1, str2, str3, str4); client.showMsg(msg, pri); } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given actor. * $PARAM a The actor for which the client handler instance is to be found. * $REQUIRE a != none * $RETURN The client handler for the given actor. * $ENSURE (!a.isA('PlayerPawn') ? result == none : true) && * (result != none ? result.owner == a : true) * **************************************************************************************************/ function NexgenClient getClient(Actor a) { local NexgenClient client; local bool bFound; // Search for NexgenClient owning this actor. client = clientList; while (!bFound && client != none) { if (client.owner == a) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given player code. * $PARAM playerNum The player code of the client handler instance that is to be found. * $REQUIRE playerNum >= 0 * $RETURN The client handler for the given player code. * $ENSURE (result != none ? result.playerNum == playerNum : true) * * **************************************************************************************************/ function NexgenClient getClientByNum(int playerNum) { local NexgenClient client; local bool bFound; // Search for NexgenClient with the specified player code. client = clientList; while (!bFound && client != none) { if (client.playerNum == playerNum) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Locates the NexgenClient instance for the given player client ID. * $PARAM clientID The client ID of the client handler instance that is to be found. * $REQUIRE clientID != "" * $RETURN The client handler for the given player code. * $ENSURE (result != none ? result.playerID ~= clientID : true) * * **************************************************************************************************/ function NexgenClient getClientByID(string clientID) { local NexgenClient client; local bool bFound; // Search for NexgenClient with the specified player code. client = clientList; while (!bFound && client != none) { if (client.playerID ~= clientID) { bFound = true; } else { client = client.nextClient; } } // Return result. if (bFound) { return client; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Handles a potential command message. * $PARAM sender PlayerPawn that has send the message in question. * $PARAM msg Message send by the player, which could be a command. * $REQUIRE sender != none * $RETURN True if the specified message is a command, false if not. * **************************************************************************************************/ function bool handleMsgCommand(PlayerPawn sender, string msg) { local string cmd; local bool bIsCommand; local int index; cmd = class'NexgenUtil'.static.trim(msg); bIsCommand = true; switch (cmd) { // Team commands. case "!r": case "!red": mutate(CMD_Prefix @ CMD_SwitchTeam @ 0, sender); break; case "!b": case "!blue": mutate(CMD_Prefix @ CMD_SwitchTeam @ 1, sender); break; case "!g": case "!green": mutate(CMD_Prefix @ CMD_SwitchTeam @ 2, sender); break; case "!y": case "!yellow": case "!gold": mutate(CMD_Prefix @ CMD_SwitchTeam @ 3, sender); break; case "!t": case "!team": case "!teams": mutate(CMD_Prefix @ CMD_BalanceTeams, sender); break; // Game commands. case "!p": case "!play": mutate(CMD_Prefix @ CMD_Play, sender); break; case "!s": case "!spec": mutate(CMD_Prefix @ CMD_Spectate, sender); break; case "!l": case "!start": mutate(CMD_Prefix @ CMD_StartGame, sender); break; case "!quit": case "!exit": mutate(CMD_Prefix @ CMD_Exit, sender); break; case "!leave": case "!bye": mutate(CMD_Prefix @ CMD_Disconnect, sender); break; // GUI commands. case "!o": case "!open": mutate(CMD_Prefix @ CMD_Open, sender); break; case "!v": case "!vote": mutate(CMD_Prefix @ CMD_OpenVote, sender); break; // Not a command. default: bIsCommand = false; } // Allow plugins to handle commands. index = 0; while (!bIsCommand && index < arrayCount(plugins) && plugins[index] != none) { bIsCommand = plugins[index].handleMsgCommand(sender, msg); index++; } return bIsCommand; } /*************************************************************************************************** * * $DESCRIPTION Called when the game executes its next 'game' tick. This function provides the * support for the gamespeed independent timing support. * $OVERRIDE * **************************************************************************************************/ function tick(float deltaTime) { local NexgenClient client; if (level.pauser == "") { timeSeconds += deltaTime / level.timeDilation; } for (client = clientList; client != none; client = client.nextClient) { if (client.player.player == none) { playerLeft(client); } } if (!bServerTravelDetected && level.nextURL != "" && level.nextSwitchCountdown - deltaTime <= 0) { bServerTravelDetected = true; notifyBeforeLevelChange(); } if (!bFirstTickPassed) { firstTick(); bFirstTickPassed = true; } } /*************************************************************************************************** * * $DESCRIPTION Executes one iteration of the server controller main loop. Various events are * detected and handled here, including: * - Players that change their name. * - Players that have switched to another team. * - When the game has ended. * - Game speed changes. * This function is also responsible for making our virtual 1 Hz timer tick. * $OVERRIDE * **************************************************************************************************/ function timer() { local NexgenClient client; // For each client... for (client = clientList; client != none; client = client.nextClient) { // Name changed? if (client.playerName != client.player.playerReplicationInfo.playerName) { playerNameChanged(client); } // Team changed? if (client.team != client.player.playerReplicationInfo.team) { playerTeamChanged(client); } } // Game ended? if (level.game.bGameEnded && gInf.gameState != gInf.GS_Ended) { gInf.gameState = gInf.GS_Ended; gameEnded(); } // Check speed changed? if (level.timeDilation != lastTimeDilation) { lastTimeDilation = level.timeDilation; gInf.gameSpeed = level.game.gameSpeed; gameSpeedChanged(); } // Simulated 1 Hz timer. virtualTimerCounter += timerRate; if (virtualTimerCounter > level.timeDilation) { virtualTimerCounter = 0; virtualTimer(); } } /*************************************************************************************************** * * $DESCRIPTION This is our home cooked timer, which ticks at a frequency of 1 Hz and is * independent of the game speed. * **************************************************************************************************/ function virtualTimer() { local int index; local NexgenClient client; // Update countdown. if (gInf.countDown > 0) { gInf.countDown--; } // Check if the game if ready to start if (gInf.countDown == 0 && gInf.gameState == gInf.GS_Waiting && sConf.enableNexgenStartControl) { gInf.gameState = gInf.GS_Ready; for (client = clientList; client != none; client = client.nextClient) { showGameReadyToLaunchMessage(client); } } if (gInf.countDown == 0 && gInf.gameState == gInf.GS_Starting) { startGame(); } // Automatically disable an inactive match? if (sConf.matchModeActivated && sConf.autoDisableMatchTime > 0 && clientList == none && timeSeconds - lastPlayerLeftTime > secondsPerMinute * sConf.autoDisableMatchTime) { // Yes. sConf.matchModeActivated = false; sConf.saveConfig(); signalConfigUpdate(sConf.CT_MatchSettings); if (sConf.matchAutoLockTeams) { gInf.bTeamsLocked = false; } } // Reboot server? if (gInf.rebootCountDown > 0) { gInf.rebootCountDown--; if (gInf.rebootCountDown == 0) { sConf.isAdminReboot = true; sConf.saveConfig(); consoleCommand(rebootCommand); } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].virtualTimer(); index++; } } /*************************************************************************************************** * * $DESCRIPTION Sends a message to the specified client that the game is ready to be started. * $PARAM client The client that should receive the 'ready to launch' message. * $REQUIRE client != none * **************************************************************************************************/ function showGameReadyToLaunchMessage(NexgenClient client) { if (sConf.matchModeActivated && gInf.bTournamentMode) { client.showMsg(lng.tournamentLaunchGameMsg); } else if (sConf.matchModeActivated && gInf.matchAdminCount > 0 && !client.hasRight(client.R_MatchAdmin)) { client.showMsg(lng.adminLaunchGameMsg); } else { client.showMsg(lng.launchGameMsg); } } /*************************************************************************************************** * * $DESCRIPTION Called when the game has started. * **************************************************************************************************/ function gameStarted() { local int index; // Record current time. gameStartTime = timeSeconds; // Log event. nscLog(lng.gameStartedMsg, LT_Event); // Lock teams? if (sConf.matchModeActivated && sConf.matchAutoLockTeams && !gInf.bTeamsLocked) { gInf.bTeamsLocked = true; signalGameInfoUpdate(gInf.IT_GlobalRights); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].gameStarted(); index++; } } /*************************************************************************************************** * * $DESCRIPTION Called when the game has ended. * $REQUIRE level.game.bGameEnded * **************************************************************************************************/ function gameEnded() { local int index; // Record current time. gameEndTime = timeSeconds; // Log event. nscLog(lng.format(lng.gameEndedMsg, int(gameEndTime - gameStartTime + 0.5)), LT_Event); // Update match settings. if (sConf.matchModeActivated) { sConf.currentMatch++; } // Unlock teams? if (sConf.matchModeActivated && sConf.matchAutoLockTeams && gInf.bTeamsLocked) { gInf.bTeamsLocked = false; signalGameInfoUpdate(gInf.IT_GlobalRights); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].gameEnded(); index++; } // Save configuration. sConf.saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Called when the server is about to perform a server travel. Note that the server * travel may fail to switch to the desired map. In that case the server will * continue running the current game or a second notifyBeforeLevelChange() call may * occur when trying to switch to another map. So be carefull what you do in this * function!!! * **************************************************************************************************/ function notifyBeforeLevelChange() { local int index; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].gameEnded(); index++; } // Close log file. if (logFile != none) { logFile.endLog(); logFile = none; } } /*************************************************************************************************** * * $DESCRIPTION Called when the game speed has changed. * **************************************************************************************************/ function gameSpeedChanged() { local int index; // Update timer rate. timerRate = 1.0 / timerFreq * level.timeDilation; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { //plugins[index].gameSpeedChanged(); index++; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so commands can be detected. This function * is called if a message is send to player. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM pri Player replication info of the sending player. * $PARAM s The message that is to be send. * $PARAM type Type of the message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorTeamMessage(Actor sender, Pawn receiver, PlayerReplicationInfo pri, coerce string s, name type, optional bool bBeep) { local bool bIsCommand; local NexgenClient client; local byte logType; local int index; local bool bSend; // Check for commands. if (sender != none && sender.isA('PlayerPawn') && sender == receiver && (type == 'Say' || type == 'TeamSay')) { bIsCommand = handleMsgCommand(PlayerPawn(sender), s); } // Check if player is muted. client = getClient(sender); if (client != none && client.isMuted()) { // Yeah he/she is, block the message. if (sender == receiver) { if (bIsCommand) { return true; } else { client.showMsg(lng.mutedReminderMsg); } } return false; } // Log message. if (sender == none && receiver != none && receiver.nextPawn == none) { nscLog(s, LT_Message); } else if (sender != none && sender == receiver && receiver.playerReplicationInfo != none) { if (type == 'Say') { logType = LT_Say; } else if (type == 'TeamSay') { logType = LT_TeamSay; } else { logType = LT_Message; } nscLog(receiver.playerReplicationInfo.playerName $ ": " $ s, logType); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { bSend = plugins[index].mutatorTeamMessage(sender, receiver, pri, s, type, bBeep); if (!bSend) return false; index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorTeamMessage(sender, receiver, pri, s, type, bBeep); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so commands can be detected. This function * is called if a message is send to player. Spectators that use say (not teamsay) * seem to be calling this function instead of mutatorTeamMessage. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM msg The message that is to be send. * $PARAM bBeep Whether or not to make a beep sound once received. * $PARAM type Type of the message that is to be send. * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorBroadcastMessage(Actor sender, Pawn receiver, out coerce string msg, optional bool bBeep, out optional name type) { local PlayerReplicationInfo senderPRI; local bool bIsCommand; local NexgenClient client; local bool bIsSpecMessage; local int index; local bool bSend; // Suppress default player join / leave messages. if (sender == level.game && right(msg, len(level.game.leftMessage)) ~= level.game.leftMessage || sender == level.game && right(msg, len(level.game.enteredMessage)) ~= level.game.enteredMessage) { return false; } // Get sender player replication info. if (sender != none && sender.isA('Pawn')) { senderPRI = Pawn(sender).playerReplicationInfo; } // Check if we're dealing with a spectator chat message. bIsSpecMessage = senderPRI != none && sender.isA('Spectator') && left(msg, len(senderPRI.playerName) + 1) ~= (senderPRI.playerName $ ":"); // Check for commands. if (bIsSpecMessage && sender == receiver) { bIsCommand = handleMsgCommand(PlayerPawn(sender), mid(msg, len(senderPRI.playerName) + 1)); } // Check if spectator is muted. if (bIsSpecMessage) { client = getClient(sender); if (client != none && client.isMuted() || sConf.matchModeActivated && sConf.muteSpectatorsDuringMatch && gInf.gameState == gInf.GS_Playing && !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate)) { // Spectator is muted, block the message. if (sender == receiver) { if (bIsCommand) { return true; } else { client.showMsg(lng.mutedReminderMsg); } } return false; } } // Write message to the log. if (bIsSpecMessage && sender == receiver) { nscLog(msg, LT_Say); } else if (!bIsSpecMessage && receiver != none && (receiver.nextPawn == none || receiver.nextPawn.isA('NexgenControllerPawn'))) { if (senderPRI == none) { nscLog(msg, LT_Message); } else { nscLog(senderPRI.playerName $ ": " $ msg, LT_Message); } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { bSend = plugins[index].mutatorBroadcastMessage(sender, receiver, msg, bBeep, type); if (!bSend) return false; index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorBroadcastMessage(sender, receiver, msg, bBeep, type); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the message mutator chain so messages can be logged. * $PARAM sender The actor that has send the message. * $PARAM receiver Pawn receiving the message. * $PARAM message The class of the localized message that is to be send. * $PARAM switch Optional message switch argument. * $PARAM relatedPRI_1 PlayerReplicationInfo of a player that is related to the message. * $PARAM relatedPRI_2 PlayerReplicationInfo of a player that is related to the message. * $PARAM optionalObject Optional object used to construct the message string. * $REQUIRE message != none * $RETURN True if the message should be send, false if it should be suppressed. * $OVERRIDE * **************************************************************************************************/ function bool mutatorBroadcastLocalizedMessage(Actor sender, Pawn receiver, out class message, out optional int switch, out optional PlayerReplicationInfo relatedPRI_1, out optional PlayerReplicationInfo relatedPRI_2, out optional Object optionalObject) { local PlayerReplicationInfo senderPRI; local string msg; local int index; local bool bSend; // Get sender player replication info. if (sender != none && sender.isA('Pawn')) { senderPRI = Pawn(sender).playerReplicationInfo; } // Prevent duplicate messages in the log. if (receiver != none && (receiver.nextPawn == none || receiver.nextPawn.isA('NexgenControllerPawn'))) { // Construct message. msg = message.static.getString(switch, relatedPRI_1, relatedPRI_2, optionalObject); // Log the message. if (senderPRI == none) { nscLog(msg, LT_Message); } else { nscLog(senderPRI.playerName $ ": " $ msg, LT_Message); } } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { bSend = plugins[index].mutatorBroadcastLocalizedMessage(sender, receiver, message, switch, relatedPRI_1, relatedPRI_2, optionalObject); if (!bSend) return false; index++; } // Allow other message mutators to do their job. if (nextMessageMutator != none) { return nextMessageMutator.mutatorBroadcastLocalizedMessage(sender, receiver, message, switch, relatedPRI_1, relatedPRI_2, optionalObject); } else { return true; } } /*************************************************************************************************** * * $DESCRIPTION Hooked into the mutator chain to detect Nexgen actions issued by the clients. If * a Nexgen command is detected it will be parsed and send to the execCommand() * function. * $PARAM mutateString Mutator specific string (indicates the action to perform). * $PARAM sender Player that has send the message. * $OVERRIDE * **************************************************************************************************/ function mutate(string mutateString, PlayerPawn sender) { local bool bIsNexgenCommand; local bool bIsLegacyCommand; local NexgenClient client; local string cmd; local string args[10]; local int index; // Get client handler for the sender. client = getClient(sender); // Parse command. bIsNexgenCommand = class'NexgenUtil'.static.parseCommandStr(mutateString, cmd, args); // Execute command. if (client != none) { if (bIsNexgenCommand) { cmdHandler.execCommand(client, cmd, args); } else if (mutateString ~= "asc#get#window" || mutateString ~= "hz0090") { // ASC/HUT legacy commands. cmdHandler.execCommand(client, CMD_Open, args); bIsLegacyCommand = true; } } // Let plugins and other mutators handle the string if this isn't a valid Nexgen command. if (!bIsNexgenCommand && !bIsLegacyCommand) { // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].mutate(mutateString, sender); index++; } // Allow other mutators to do their job. if (nextMutator != none) { nextMutator.mutate(mutateString, sender); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the clients that a certain part of the server configuration has changed. * $PARAM configType Type of settings that have been changed. * $ENSURE new.sConf.updateCounts[configType] = old.sConf.updateCounts[configType] + 1 * **************************************************************************************************/ function signalConfigUpdate(byte configType) { local NexgenClient client; local int index; // Set update counter. sConf.updateCounts[configType]++; // Update checksum. sConf.updateChecksum(configType); // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.configChanged(configType, sConf.updateCounts[configType], sConf.dynamicChecksums[configType]); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].configChanged(configType); index++; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the clients that a certain part of the server configuration has changed. * $PARAM configType Type of settings that have been changed. * $ENSURE new.sConf.updateCount = old.sConf.updateCount + 1 * **************************************************************************************************/ function signalGameInfoUpdate(byte infoType) { local NexgenClient client; // Set update counter. gInf.updateCount++; // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.gameInfoChanged(infoType, gInf.updateCount); } } /*************************************************************************************************** * * $DESCRIPTION Notifies all clients that a specified attribute of a player has changed. * $PARAM client The client of which an attribute has changed. * $REQUIRE client != none * **************************************************************************************************/ function announcePlayerAttrChange(NexgenClient client, string attributeName, coerce string value) { local NexgenClient c; local string args; // Get event arguments. class'NexgenUtil'.static.addProperty(args, attributeName, value); // Signal attribute change events. for (c = clientList; c != none; c = c.nextClient) { c.playerEvent(client.playerNum, client.PE_AttributeChanged, args); } } /*************************************************************************************************** * * $DESCRIPTION Forces the current game to end. * **************************************************************************************************/ function forceEndGame() { Local Actor a; local pawn aPawn; // Set end game comments. level.game.gameReplicationInfo.gameEndedComments = lng.forcedEndMsg; // Notify clients. for (aPawn = level.pawnList; aPawn != none; aPawn=aPawn.nextPawn) { if (aPawn.bIsPlayer) { aPawn.gotoState('GameEnded'); aPawn.clientGameEnded(); } } // Set game info end flags. level.game.bGameEnded = true; level.game.gameReplicationInfo.bStopCountDown = true; if (level.game.isA('DeathMatchPlus')) { DeathMatchPlus(level.game).endTime = level.timeSeconds + 3.0; } // Signal triggers. foreach level.game.allActors(class'Actor', a, 'EndGame') { a.trigger(level.game, none); } } /*************************************************************************************************** * * $DESCRIPTION Called when a player was killed by another player. * $PARAM killer The pawn that killed the other pawn. Might be none. * $PARAM victim Pawn that was the victim. * $OVERRIDE * **************************************************************************************************/ function scoreKill(Pawn killer, Pawn victim) { local int index; // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].scoreKill(killer, victim); index++; } // Let other mutators do their job. if (nextMutator != none) { nextMutator.scoreKill(killer, victim); } } /*************************************************************************************************** * * $DESCRIPTION Check for other mutators that might create compatibility issues with Nexgen. * **************************************************************************************************/ function doCompatibilityCheck() { local string serverActors; // Get server actor list. serverActors = caps(consoleCommand("get Engine.GameEngine ServerActors")); // Check for UTPure. bUTPureEnabled = instr(serverActors, ".UTPURESA\"") > 0; if (bUTPureEnabled) { nscLog(lng.format(lng.compatibilityModeMsg, "UTPure")); } } /*************************************************************************************************** * * $DESCRIPTION Signals a general event. The event is broadcasted to all clients and called on * all plugins. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ function signalEvent(string type, optional string arguments) { local NexgenClient client; local int index; // Notify clients. for (client = clientList; client != none; client = client.nextClient) { client.notifyEvent(type, arguments); } // Notify plugins. while (index < arrayCount(plugins) && plugins[index] != none) { plugins[index].notifyEvent(type, arguments); index++; } } /*************************************************************************************************** * * $DESCRIPTION Checks the number of players that have send a ready signal and the required * number of ready signals. Also once the required number of ready signals is met * the game will start automatically. * **************************************************************************************************/ function doTournamentModeReadySignalCheck() { local NexgenClient client; local byte numReady; local byte numRequiredReady; local byte numPlayers; // Count ready signals and number of players. for (client = clientList; client != none; client = client.nextClient) { if (!client.bSpectator) { numPlayers++; if (client.bIsReadyToPlay) { numReady++; } } } // Determine required ready count. numRequiredReady = max(2, numPlayers); // Update game info. gInf.numReady = numReady; gInf.numRequiredReady = numRequiredReady; // Start game if minimum required ready signals is reached. if (numReady >= numRequiredReady) { startGame(); } } /*************************************************************************************************** * * $DESCRIPTION Sets the bIsReadyToPlay flag for all clients to false. * **************************************************************************************************/ function clearReadySignals() { local NexgenClient client; for (client = clientList; client != none; client = client.nextClient) { client.bIsReadyToPlay = false; } } /*************************************************************************************************** * * $DESCRIPTION Called when the game is executing it's first tick. * **************************************************************************************************/ function firstTick() { if (!bSpecialMode) { sConf.setActiveMutatorList(); nscLog(lng.nexgenActiveMsg); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ O/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenContentPanel * $VERSION 1.08 (18-11-2007 21:51) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen control panel page with layout managing support. This basic * UWindowPageWindow provides functions to automatically setup the layout of the * window, so it is not necessary to position all components manually. * **************************************************************************************************/ class NexgenContentPanel extends UWindowPageWindow; struct LayoutRegion { // Structure for containing the definition of a region. var float x; // Horizontal offset. var float y; // Vertical offset. var float w; // Width of the region. var float h; // Height of the region. }; var LayoutRegion regions[64]; // Active regions for this panel. var int regionCount; // Number of regions created. var int currRegion; // Current selected region. var enum EPanelBackType { // Background type of the panel. PBT_Default, // Default background. PBT_Beveled, // Beveld background. PBT_Transparent // No background. } panelBGType; var NexgenContentPanel parentCP; // Parent NexgenContentPanel (may be none). const borderSize = 4.0; // Distance between the root region and the window borders. const minRegionSize = 4.0; // Minimum size of a region (in pixels). const AL_Center = 0; // Component is aligned in the center. const AL_Left = 1; // Component is aligned to the left border of the region. const AL_Top = 1; // Component is aligned to the upper border of the region. const AL_Right = 2; // Component is aligned to the right border of the region. const AL_Bottom = 2; // Component is aligned to the lower border of the region. const defaultComponentDist = 4.0; // Default distance between components. const defaultButtonHeight = 16.0; // Default height for UWindowSmallButton components. const defaultLabelHeight = 12.0; // Default height for UMenuLabelControl components. const defaultEditBoxHeight = 16.0; // Default height for UWindowEditControl components. const defaultCheckBoxHeight = 13.0; // Default height for UWindowCheckbox components. const defaultRaisedButtonHeight = 18.0; // Default height for UMenuRaisedButton components. const defaultlistComboHeight = 16.0; // Default height for UWindowComboControl components. const separator = ","; // Token used to seperate elements in a list. /*************************************************************************************************** * * $DESCRIPTION Adds a new region to the content panel. * $PARAM x Horizontal offset. * $PARAM y Vertical offset. * $PARAM w Width of the region. * $PARAM h Height of the region. * $REQUIRE w >= 0 && h >= 0 * $ENSURE result >= 0 ? new.regionCount = old.regionCount + 1 && * regions[result].x = x && * regions[result].y = y && * regions[result].w = w && * regions[result].h = h * : true * **************************************************************************************************/ function int addRegion(float x, float y, float w, float h) { local int index; // Check if there is room for another region. if (regionCount < arrayCount(regions)) { // There is, add the region. regions[regionCount].x = x; regions[regionCount].y = y; regions[regionCount].w = w; regions[regionCount].h = h; return regionCount++; } else { // There isn't, return failure. return -1; } } /*************************************************************************************************** * * $DESCRIPTION Adds the root region to the content panel. This region occupies the whole surface * of this content panel. * $REQUIRE regionCount == 0 * $ENSURE new.regionCount = old.regionCount + 1 * **************************************************************************************************/ function createWindowRootRegion() { currRegion = addRegion(borderSize + 2.0, borderSize + 2.0, winWidth - 2.0 * borderSize - 8.0, winHeight - 2.0 * borderSize - 10.0); } /*************************************************************************************************** * * $DESCRIPTION Adds the root region to the content panel. This region occupies the whole surface * of this content panel. Use this function instead of createRootRegion() if the * panel is added as a component on another panel. * $REQUIRE regionCount == 0 * $ENSURE new.regionCount = old.regionCount + 1 * **************************************************************************************************/ function createPanelRootRegion() { currRegion = addRegion(borderSize + 2.0, borderSize + 2.0, winWidth - 2.0 * borderSize - 4.0, winHeight - 2.0 * borderSize - 4.0); } /*************************************************************************************************** * * $DESCRIPTION Selects the region that is to be used. * $PARAM region Index of the region which should be selected. * $REQUIRE 0 <= region && region < regionCount * $ENSURE currRegion == region * **************************************************************************************************/ function selectRegion(int region) { currRegion = region; } /*************************************************************************************************** * * $DESCRIPTION Skips the current region and selects the next one. * $REQUIRE 0 <= currRegion && currRegion < regionCount * **************************************************************************************************/ function skipRegion() { currRegion++; } /*************************************************************************************************** * * $DESCRIPTION Splits the current region in two horizontal sub regions. Once this function has * been called it will automatically select the next region. * $PARAM height Determines the location of the point where the current region is to be * split. The exact semantics of this parameter depends on the bPercent and * bBottom parameter. * $PARAM dist Distance between the two sub regions (in pixels). * $PARAM bPercent Indicates whether height is an absolute or relative value. * $PARAM bBottom Whether the height parameter counts for the lower or upper sub region. * $REQUIRE (bPercent ? 0 < height && height < 100 : height > 0) && dist >= 0 && * 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1. * **************************************************************************************************/ function int splitRegionH(float height, optional int dist, optional bool bPercent, optional bool bBottom) { local float splitPoint; local float region1height; local float region2height; local float region1top; local float region2top; local int index; // Determine splitpoint. if (bPercent) { if (bBottom) { splitPoint = int(regions[currRegion].h * (100.0 - height) / 100.0); } else { splitPoint = int(regions[currRegion].h * height / 100.0); } } else { if (bBottom) { splitPoint = regions[currRegion].h - height; } else { splitPoint = height; } } // Determine region metrics. region1height = fMax(splitPoint - int(dist / 2.0), minRegionSize); region2height = fMax(regions[currRegion].h - region1height - dist, minRegionSize); region1top = regions[currRegion].y; region2top = region1top + region1height + dist; // Add regions. index = addRegion(regions[currRegion].x, region1top, regions[currRegion].w, region1height); addRegion(regions[currRegion].x, region2top, regions[currRegion].w, region2height); currRegion++; // Automatically select next region. // Return index of first created region. return index; } /*************************************************************************************************** * * $DESCRIPTION Splits the current region in two vertical sub regions. Once this function has been * called it will automatically select the next region. * $PARAM width Determines the location of the point where the current region is to be * split. The exact semantics of this parameter depends on the bPercent and * bRight parameter. * $PARAM dist Distance between the two sub regions (in pixels). * $PARAM bPercent Indicates whether width is an absolute or relative value. * $PARAM bRight Whether the width parameter counts for the left or right sub region. * $REQUIRE (bPercent ? 0 < width && width < 100 : width > 0) && dist >= 0 && * 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1. * **************************************************************************************************/ function int splitRegionV(float width, optional int dist, optional bool bPercent, optional bool bRight) { local float splitPoint; local float region1width; local float region2width; local float region1left; local float region2left; local int index; // Determine splitpoint. if (bPercent) { if (bRight) { splitPoint = int(regions[currRegion].w * (100.0 - width) / 100.0); } else { splitPoint = int(regions[currRegion].w * width / 100.0); } } else { if (bRight) { splitPoint = regions[currRegion].w - width; } else { splitPoint = width; } } // Determine region metrics. region1width = fMax(splitPoint - int(dist / 2.0), minRegionSize); region2width = fMax(regions[currRegion].w - region1width - dist, minRegionSize); region1left = regions[currRegion].x; region2left = region1left + region1width + dist; // Add regions. index = addRegion(region1left, regions[currRegion].y, region1width, regions[currRegion].h); addRegion(region2left, regions[currRegion].y, region2width, regions[currRegion].h); currRegion++; // Automatically select next region. // Return index of first created region. return index; } /*************************************************************************************************** * * $DESCRIPTION Divides the current region into a specified amount of equal sized parts. The * current region will be sliced using horizontal cuts. Automatically selects the * next region. * $PARAM amount Number of sub regions to create. * $PARAM dist Distance between each created sub region (in pixels). * $REQUIRE amount >= 0 && dist >= 0 && 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1, etc. * **************************************************************************************************/ function int divideRegionH(int amount, optional int dist) { local float totalRegionHeight; local float regionHeight; local float leftOver; local float carryOver; local float currHeight; local float currTop; local int index; local int result; local int count; // Determine region metrics. totalRegionHeight = regions[currRegion].h - (amount - 1) * dist; regionHeight = int(totalRegionHeight / amount); leftOver = (totalRegionHeight - regionHeight * amount) / amount; currTop = regions[currRegion].y; // Create regions. for (count = 0; count < amount; count++) { // Finish current region metrics. currHeight = regionHeight; carryOver += leftOver; if (carryOver >= 1) { carryOver = carryOver - 1.0; currHeight = currHeight + 1.0; } // Add the region. index = addRegion(regions[currRegion].x, currTop, regions[currRegion].w, currHeight); if (count == 0) { result = index; } // Update y-offset. currTop = currTop + currHeight + dist; } currRegion++; // Automatically select next region. // Return index of first created region. return result; } /*************************************************************************************************** * * $DESCRIPTION Divides the current region into a specified amount of equal sized parts. The * current region will be sliced using horizontal cuts. Automatically selects the * next region. * $PARAM amount Number of sub regions to create. * $PARAM dist Distance between each created sub region (in pixels). * $REQUIRE amount >= 0 && dist >= 0 && 0 <= currRegion && currRegion < regionCount * $RETURN The index of the first new sub region that was created or -1 if no region could * be created. The index of the second region is: result + 1, etc. * **************************************************************************************************/ function int divideRegionV(int amount, optional int dist) { local float totalRegionWidth; local float regionWidth; local float leftOver; local float carryOver; local float currWidth; local float currLeft; local int index; local int result; local int count; // Determine region metrics. totalRegionWidth = regions[currRegion].w - (amount - 1) * dist; regionWidth = int(totalRegionWidth / amount); leftOver = (totalRegionWidth - regionWidth * amount) / amount; currLeft = regions[currRegion].x; // Create regions. for (count = 0; count < amount; count++) { // Finish current region metrics. currWidth = regionWidth; carryOver += leftOver; if (carryOver >= 1) { carryOver = carryOver - 1.0; currWidth = currWidth + 1.0; } // Add the region. index = addRegion(currLeft, regions[currRegion].y, currWidth, regions[currRegion].h); if (count == 0) { result = index; } // Update y-offset. currLeft = currLeft + currWidth + dist; } currRegion++; // Automatically select next region. // Return index of first created region. return result; } /*************************************************************************************************** * * $DESCRIPTION Creates a new component and places it on the current region. Also automatically * selects the next region. * $PARAM wndClass Component type to create. * $PARAM width Preferred width of the component. Use 0 to use all space available. * $PARAM height Preferred height of the component. Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the component on the region. * $PARAM vAlign Vertical alignment of the component on the region. * $REQUIRE wndClass != none && width >= 0 && height >= 0 && * (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * (vAlign == AL_Top || vAlign == AL_Center || vAlign == AL_Bottom) && * 0 <= currRegion && currRegion < regionCount * $RETURN The component that has been created and added to the window. * $ENSURE result != none * **************************************************************************************************/ function UWindowWindow addComponent(class wndClass, optional float width, optional float height, optional byte hAlign, optional byte vAlign) { local float x; local float y; local float w; local float h; // Determine horizontal metrics. if (width <= 0.0) { x = regions[currRegion].x; w = regions[currRegion].w; } else { w = width; if (hAlign == AL_Left) { x = regions[currRegion].x; } else if (hAlign == AL_Right) { x = regions[currRegion].x + regions[currRegion].w - width; } else { x = int(regions[currRegion].x + (regions[currRegion].w - width) / 2.0); } } // Determine vertical metrics. if (height <= 0.0) { y = regions[currRegion].y; h = regions[currRegion].h; } else { h = height; if (vAlign == AL_Top) { y = regions[currRegion].y; } else if (vAlign == AL_Bottom) { y = regions[currRegion].y + regions[currRegion].h - height; } else { y = int(regions[currRegion].y + (regions[currRegion].h - height) / 2.0); } } // Select next region. currRegion++; // Create component. return createWindow(wndClass, x, y, w, h); } /*************************************************************************************************** * * $DESCRIPTION Adds a new button component to the current region. * $PARAM text Text to display on the button. * $PARAM width Width of the button (in pixels). Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the button on the current region. * $REQUIRE width >= 0 && (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * 0 <= currRegion && currRegion < regionCount * $RETURN The button that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowSmallButton addButton(optional string text, optional float width, optional byte hAlign) { local UWindowSmallButton button; button = UWindowSmallButton(addComponent(class'UWindowSmallButton', width, defaultButtonHeight, hAlign, AL_Center)); button.setText(text); button.register(self); return button; } /*************************************************************************************************** * * $DESCRIPTION Adds a new label component to the current region. * $PARAM text Text displayed on the label component. * $PARAM bBold Whether or not the text is displayed in a bold font. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The label that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UMenuLabelControl addLabel(optional coerce string text, optional bool bBold, optional TextAlign align) { local UMenuLabelControl label; label = UMenuLabelControl(addComponent(class'UMenuLabelControl', , defaultLabelHeight, , AL_Center)); label.setText(text); if (bBold) { label.setFont(F_Bold); } label.align = align; return label; } /*************************************************************************************************** * * $DESCRIPTION Adds a new edit box component to the current region. * $PARAM text Value of the edit box. * $PARAM width Width of the button (in pixels). Use 0 to use all space available. * $PARAM hAlign Horizontal alignment of the edit box on the current region. * $REQUIRE width >= 0 && (hAlign == AL_Left || hAlign == AL_Center || hAlign == AL_Right) && * 0 <= currRegion && currRegion < regionCount * $RETURN The edit box that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenEditControl addEditBox(optional string text, optional float width, optional byte hAlign) { local NexgenEditControl editBox; local float editBoxWidth; if (width > 0) { editBoxWidth = width; } else { editBoxWidth = regions[currRegion].w; } editBox = NexgenEditControl(addComponent(class'NexgenEditControl', width, defaultEditBoxHeight, hAlign, AL_Center)); editBox.editBoxWidth = editBoxWidth; editBox.setValue(text); return editBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new check box component to the current region. * $PARAM align Alignment of the check box. * $PARAM text The text to display on the check box. * $PARAM bBold Whether or not the text is displayed in a bold font. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The check box that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowCheckbox addCheckBox(optional TextAlign align, optional string text, optional bool bBold) { local UWindowCheckbox checkBox; checkBox = UWindowCheckbox(addComponent(class'UWindowCheckbox', , defaultCheckBoxHeight, , AL_Center)); checkBox.setText(text); checkBox.align = align; if (bBold) { checkBox.setFont(F_Bold); } return checkBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new raised button component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UMenuRaisedButton addRaisedButton() { local UMenuRaisedButton raisedButton; raisedButton = UMenuRaisedButton(addComponent(class'UMenuRaisedButton', , defaultRaisedButtonHeight, , AL_Center)); return raisedButton; } /*************************************************************************************************** * * $DESCRIPTION Adds a new image box component to the current region. * $PARAM image Texture to display on the component. * $PARAM bStretch Whether or not the image should be streched. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised button that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenImageControl addImageBox(optional Texture image, optional bool bStretch, optional float width, optional float height) { local NexgenImageControl imageBox; imageBox = NexgenImageControl(addComponent(class'NexgenImageControl', width, height, AL_Center, AL_Center)); imageBox.image = image; imageBox.bStretch = bStretch; return imageBox; } /*************************************************************************************************** * * $DESCRIPTION Adds a new component container panel to the current region. * $PARAM bgType Panel border/background style. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The raised that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function NexgenContentPanel addContentPanel(optional EPanelBackType bgType) { local NexgenContentPanel contentPanel; contentPanel = NexgenContentPanel(addComponent(class'NexgenContentPanel')); contentPanel.panelBGType = bgType; contentPanel.createPanelRootRegion(); contentPanel.parentCP = self; return contentPanel; } /*************************************************************************************************** * * $DESCRIPTION Adds a new combo control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The combo control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowComboControl addListCombo() { local UWindowComboControl listCombo; listCombo = UWindowComboControl(addComponent(class'UWindowComboControl', , defaultlistComboHeight, , AL_Center)); listCombo.editBoxWidth = listCombo.winWidth; listCombo.setEditable(false); return listCombo; } /*************************************************************************************************** * * $DESCRIPTION Adds a new dynamic text area control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The dynamic text area control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowDynamicTextArea addDynamicTextArea() { local UMenuMapListFrameCW frame; local UWindowDynamicTextArea textArea; frame = UMenuMapListFrameCW(addComponent(class'UMenuMapListFrameCW')); textArea = UWindowDynamicTextArea(CreateControl(class'UWindowDynamicTextArea', 0, 0, 100, 100)); textArea.setTextColor(lookAndFeel.editBoxTextColor); textArea.bTopCentric = false; frame.frame.setFrame(textArea); return textArea; } /*************************************************************************************************** * * $DESCRIPTION Adds a new dynamic text area control component to the current region. * $REQUIRE 0 <= currRegion && currRegion < regionCount * $RETURN The dynamic text area control that has been added to the panel. * $ENSURE result != none * **************************************************************************************************/ function UWindowListBox addListBox(class listBoxClass) { local UMenuMapListFrameCW frame; local UWindowListBox listBox; frame = UMenuMapListFrameCW(addComponent(class'UMenuMapListFrameCW')); listBox = UWindowListBox(CreateControl(listBoxClass, 0, 0, 100, 100)); frame.frame.setFrame(listBox); return listBox; } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Delegate events to parent. if (parentCP != none) { parentCP.notify(control, eventType); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of a key press event. * $PARAM key The number of the key that was pressed. * $PARAM x Unknown, x location of mouse cursor? * $PARAM y Unknown, x location of mouse cursor? * $OVERRIDE * **************************************************************************************************/ function keyDown(int key, float x, float y) { // Delegate events to parent. if (parentCP != none) { parentCP.keyDown(key, x, y); } } /*************************************************************************************************** * * $DESCRIPTION Paints the dialog area. * $PARAM c The canvas object which acts as a drawing surface for the dialog. * $PARAM x Unknown. * $PARAM y Unknown. * $OVERRIDE * **************************************************************************************************/ function paint(Canvas c, float x, float y){ switch (panelBGType) { case PBT_Beveled: drawUpBevel(c, 0, 0, winWidth, winHeight, getLookAndFeelTexture()); break; case PBT_Transparent: // Do nothing. break; default: super.paint(c, x, y); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ aFk(`^l~k,1l%|!k~!\|!kl~!kl},  M /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigSys * $VERSION 1.00 (27-1-2007 14:40) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class for configurations stored in the * servers configuration file. * **************************************************************************************************/ class NexgenConfigSys extends NexgenConfig config(system); oF "C"sF "B"vF "About"lFmL  dbp~mE"3p%@"mB"`@"mpB"mp}E"  D /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigExt * $VERSION 1.00 (27-1-2007 14:40) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class for external stored configuration * file (Nexgen.ini) * **************************************************************************************************/ class NexgenConfigExt extends NexgenConfig config(Nexgen); tF "A"uF "RunCount"~F "AutoSSMatch"GG "Plugins"qF` _"fTpppppppp` ,-` ,,-` , ,-` ,,-` ,,   e/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfigChecker * $VERSION 1.05 (9-8-2008 19:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Configuration checking class. This class contains code for checking the * configuration of the server controller. It will automatically try to repair any * errors or inconsitensties. The code has been place into a separate class to save * NexgenConfig from becoming too large (and therefore unreadable). * **************************************************************************************************/ class NexgenConfigChecker extends info; /*************************************************************************************************** * * $DESCRIPTION Validates the given Nexgen configuration. Any invalid settings will be * automatically adjusted to a proper setting. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True, if the configuration was valid, false otherwise. * $ENSURE !old.checkConfig(c) ? new.checkConfig(c) : true * **************************************************************************************************/ function bool checkConfig(NexgenConfig c) { local bool bInvalid; bInvalid = checkEncryption(c) || checkGlobalServerSettings(c) || checkExtraServerSettings(c) || checkBootControlSettings(c) || checkAccountSystemSettings(c) || checkBanList(c) || checkMatchSettings(c) || checkLogSettings(c); // Save repaired configuration if needed. if (bInvalid) { c.saveConfig(); } // Return result. return !bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the server encryption parameters. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkEncryption(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = c.configEncryptionKey == 0 || len(c.configCodeScheme) != 32; // Fix things if necessary. if (bInvalid) { c.resetEncryptionConfig(); c.globalServerPassword = c.encode(consoleCommand("get Engine.GameInfo GamePassword")); c.globalAdminPassword = c.encode(consoleCommand("get Engine.GameInfo AdminPassword")); c.serverPassword = ""; for (index = 0; index < arrayCount(c.atPassword); index++) { c.atPassword[index] = ""; } } // Return result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the global server settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkGlobalServerSettings(NexgenConfig c) { local bool bInvalid; // Check server info settings. if (c.serverName == "") { bInvalid = true; c.serverName = "[NEXGEN] Another UT Server"; } bInvalid = bInvalid || fixStrLen(c.serverName, 128); bInvalid = bInvalid || fixStrLen(c.shortName, 32); bInvalid = bInvalid || fixStrLen(c.adminName, 64); bInvalid = bInvalid || fixStrLen(c.adminEmail, 64); bInvalid = bInvalid || fixStrLen(c.MOTDLine[0], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[1], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[2], 192); bInvalid = bInvalid || fixStrLen(c.MOTDLine[3], 192); // Check slot settings. bInvalid = bInvalid || fixByteRange(c.playerSlots, 0, 32); bInvalid = bInvalid || fixByteRange(c.vipSlots, 0, 16); bInvalid = bInvalid || fixByteRange(c.adminSlots, 0, 16); bInvalid = bInvalid || fixByteRange(c.spectatorSlots, 0, 16); if (c.playerSlots + c.vipSlots + c.adminSlots <= 0) { bInvalid = true; c.playerSlots = 16; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the extra server settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkExtraServerSettings(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = bInvalid || fixByteRange(c.waitTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.startTime, 0, 30); bInvalid = bInvalid || fixByteRange(c.autoReconnectTime, 0, 60); bInvalid = bInvalid || fixIntRange(c.maxIdleTime, 0, 9999); bInvalid = bInvalid || fixIntRange(c.maxIdleTimeCP, 0, 9999); bInvalid = bInvalid || fixByteRange(c.spawnProtectionTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.teamKillDamageProtectionTime, 0, 30); bInvalid = bInvalid || fixByteRange(c.teamKillPushProtectionTime, 0, 60); bInvalid = bInvalid || fixByteRange(c.autoDisableMatchTime, 0, 120); for (index = 0; index < arrayCount(c.spawnProtectExcludeWeapons); index++) { bInvalid = bInvalid || fixStrLen(c.spawnProtectExcludeWeapons[index], 64); } for (index = 0; index < arrayCount(c.replacementClass); index++) { bInvalid = bInvalid || fixStrLen(c.replacementClass[index], 128); } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the boot control settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkBootControlSettings(NexgenConfig c) { local bool bInvalid; if (len(c.bootGameType) > 64) { bInvalid = true; c.bootGameType = ""; } bInvalid = bInvalid || fixStrLen(c.bootMapPrefix, 8); bInvalid = bInvalid || fixStrLen(c.bootOptions, 255); bInvalid = bInvalid || fixStrLen(c.bootCommands, 255); // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the account system settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkAccountSystemSettings(NexgenConfig c) { local bool bInvalid; local int index; // Check account types. while(index < arrayCount(c.atTypeName) && c.atTypeName[index] != "") { bInvalid = bInvalid || fixStrLen(c.atTypeName[index], 24); bInvalid = bInvalid || fixStrLen(c.atRights[index], 255); bInvalid = bInvalid || fixStrLen(c.atTitle[index], 24); if (len(c.atPassword[index]) > 128) { c.atPassword[index] = ""; bInvalid = true; } index++; } // Check user accounts. index = 0; while(index < arrayCount(c.paPlayerID) && c.paPlayerID[index] != "") { bInvalid = bInvalid || fixStrLen(c.paPlayerID[index], 32); bInvalid = bInvalid || fixStrLen(c.paPlayerName[index], 32); bInvalid = bInvalid || fixStrLen(c.paCustomRights[index], 255); bInvalid = bInvalid || fixStrLen(c.paCustomTitle[index], 24); if (c.get_paAccountType(index) < 0 && class'NexgenUtil'.static.trim(c.paCustomTitle[index]) == "") { bInvalid = true; c.paCustomTitle[index] = "Player*"; } index++; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the banlist. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkBanList(NexgenConfig c) { local bool bInvalid; local int index; while(index < arrayCount(c.bannedName) && c.bannedName[index] != "") { bInvalid = bInvalid || fixStrLen(c.bannedName[index], 32); bInvalid = bInvalid || fixStrLen(c.bannedIPs[index], 255); bInvalid = bInvalid || fixStrLen(c.bannedIDs[index], 255); bInvalid = bInvalid || fixStrLen(c.banReason[index], 255); bInvalid = bInvalid || fixStrLen(c.banPeriod[index], 32); index++; } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the match settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkMatchSettings(NexgenConfig c) { local bool bInvalid; local int index; bInvalid = bInvalid || fixByteRange(c.matchesToPlay, 1, 100); bInvalid = bInvalid || fixByteRange(c.currentMatch, 1, c.matchesToPlay); if (len(c.serverPassword) > 128) { c.serverPassword = ""; bInvalid = true; } for (index = 0; index < arrayCount(c.tagsToSeparate); index++) { bInvalid = bInvalid || fixStrLen(c.tagsToSeparate[index], 16); } // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Checks the log settings. * $PARAM c The configuration that is to be checked. * $REQUIRE c != none * $RETURN True if the configuration was valid, false otherwise. * **************************************************************************************************/ function bool checkLogSettings(NexgenConfig c) { local bool bInvalid; if (len(c.logPath) > 200) { c.logPath = ""; bInvalid = true; } bInvalid = bInvalid || fixStrLen(c.logFileExtension, 10); bInvalid = bInvalid || fixStrLen(c.logFileNameFormat, 100); bInvalid = bInvalid || fixStrLen(c.logFileTimeStampFormat, 100); // Return check result. return bInvalid; } /*************************************************************************************************** * * $DESCRIPTION Fixes the length of a string. This function makes sure the length of a given * string doesn't exceed the specified maximum length. * $PARAM str The string of which the length has to be checked. * $PARAM maxLen The maximum length of the string. * $REQUIRE maxLen >= 0 * $RETURN True if the length of the string was changed, false otherwise. * $ENSURE len(new.str) <= maxLen * **************************************************************************************************/ function bool fixStrLen(out string str, int maxLen) { if (len(str) > maxLen) { str = left(str, maxLen); return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Fixes the value of a given integer variable. Calling this function will ensure * that the value of the variable will be in the specified domain. * $PARAM intVar The integer variable whose value is to be checked. * $PARAM lowerBound Lower bound on the range of the variable. * $PARAM upperBound Upperbound bound on the range of the variable. * $RETURN True if value of the integer variable was changed, false otherwise. * $ENSURE lowerBound <= intVar && intVar <= upperBound * **************************************************************************************************/ function bool fixIntRange(out int intVar, int lowerBound, int upperBound) { if (intVar < lowerBound) { intVar = lowerBound; return true; } else if (intVar > upperBound) { intVar = upperBound; return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Fixes the value of a given byte variable. Calling this function will ensure * that the value of the variable will be in the specified domain. * $PARAM byteVar The byte variable whose value is to be checked. * $PARAM lowerBound Lower bound on the range of the variable. * $PARAM upperBound Upperbound bound on the range of the variable. * $RETURN True if value of the byte variable was changed, false otherwise. * $ENSURE lowerBound <= byteVar && byteVar <= upperBound * **************************************************************************************************/ function bool fixByteRange(out byte byteVar, byte lowerBound, byte upperBound) { if (byteVar < lowerBound) { byteVar = lowerBound; return true; } else if (byteVar > upperBound) { byteVar = upperBound; return true; } else { return false; } } @wFrf3vi;3|r,r,9r  e/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenConfig * $VERSION 1.35 (9-8-2008 19:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Nexgen configuration container/replication class. This class contains the settings * for the server controller and necessary code to gather a lot of required data. * WARNING: Any changes made to one of the variables during the game may prevent new * clients from initializing. MAKE SURE THE CHECKSUM IS ALWAYS UP TO DATE!!! * **************************************************************************************************/ class NexgenConfig extends ReplicationInfo; var bool bInitialized; // NexgenConfig instance has been initialized. var NexgenController control; // Server controller. const separator = ","; // Character used to seperate elements in a list. // Replication / data transfer control. var int staticChecksum; // Checksum for the static replicated variables. var int dynamicChecksums[10]; // Checksum for the dynamic replicated variables. var int dynamicChecksumModifiers[10]; // Dynamic data checksum salt. var int updateCounts[10]; // How many times the settings have been updated // during the current game. Used to detect setting // changes clientside. const CT_GlobalServerSettings = 0; // Global server settings config type. const CT_AccountTypes = 1; // Account types config type. const CT_UserAccounts = 2; // User account list config type. const CT_BanList = 3; // The player ban list config type. const CT_BootControl = 4; // Boot control settings config type. const CT_MatchSettings = 5; // Match settings config type. const CT_ExtraServerSettings = 6; // Extra server settings config type. const CT_ExclWeaponList = 7; // Excluded weapon list. const CT_HUDReplacementList = 8; // HUD class replacement list. const CT_LogSettings = 9; // The log settings. // Special settings. var config bool bInstalled; // Whether or not Nexgen has been installed. var config int lastInstalledVersion; // Last installed version of Nexgen. var private config string serverKey; // Unique server key. var config bool isNexgenBoot; // Whether the current map has been loaded by the // Nexgen boot controller. var config bool isAdminReboot; // Whether an admin has rebooted the server. // Data encryption support. var int encryptionKey; // Key used to encrypt data. var string codeScheme; // Code scheme used to encrypt data. var config int configEncryptionKey; // You should keep this private. var config string configCodeScheme; // You should keep this private. // Global server settings. var config string serverName; // Name of the server. var config string shortName; // Short server name. var config string adminName; // Name of the admin that controls the server. var config string adminEmail; // Email of the admin that controls the server. var string globalServerPassword; // Global password needed to enter the server. var string globalAdminPassword; // Global server administrator password. var config bool enableUplink; // Enable uplink to master server. var config byte playerSlots; // Maximum number of players. var config byte vipSlots; // Extra slots for VIPs if the server if full. var config byte adminSlots; // Extra slots for admins if the server if full. var config byte spectatorSlots; // Number of spectators allowed. var config string MOTDLine[4]; // Message of the day lines. // Extended server settings. var config bool autoUpdateBans; // Automatically update bans? var config bool removeExpiredBans; // Automatically remove expired bans at the // beginning of each game? var config byte waitTime; // Time to wait before the game can be started. var config byte startTime; // Time to wait before the game starts. var config byte autoReconnectTime; // Time to wait before automatically reconnecting. var config int maxIdleTime; // Maximum time a player can be idle. var config int maxIdleTimeCP; // Maximum idle time when the control panel is open. var config byte spawnProtectionTime; // Amount of time spawn protection stays activated. var config byte teamKillDamageProtectionTime; // Amount of time team kill damage protection // stays activated. var config byte teamKillPushProtectionTime; // Amount of time team kill push protection stays // activated. var config bool broadcastTeamKillAttempts; // Notify players of team kill attempts. var config bool allowTeamSwitch; // Whether team switching is allowed by default. var config bool allowTeamBalance; // Whether team balancing is allowed by default. var config bool allowNameChange; // Whether name changing is allowed by default. var config byte autoDisableMatchTime; // Automatically disable match mode if a game is // inactive for more then this amount of minutes. var config string spawnProtectExcludeWeapons[16]; // Weapons and fire modes excluded from cancelling // spawn protection. var config bool restoreScoreOnTeamSwitch; // Restore the players score when the player has // switched to another team. var config bool enableNexgenStartControl; // Whether to use the Nexgen game start control // feature. var config bool broadcastAdminActions; // Should administrator actions be broadcasted to // all players? var config bool autoRegisterServer; // Automatically register the server in the // Nexgen server database. // Logging. var config bool logEvents; // Write events to the log? var config bool logSystemMessages; // Write system messages to the log? var config bool logChatMessages; // Write chat messages to the log? var config bool logPrivateMessages; // Write private messages to the log? var config bool logAdminActions; // Write administrator actions to the log? var config bool logToConsole; // Write log entries to stdout? var config bool logToFile; // Write log entries to a special file? var config string logPath; // Path where should the log files be stored. var config string logFileExtension; // Extension of log files. var config string logFileNameFormat; // File name format for the log files. var config string logFileTimeStampFormat; // Time stamp format to use in log files. // Boot control. var config bool enableBootControl; // Whether the nexgen boot control is enabled. var config bool restartOnLastGame; // Whether to restart on the last game played. var config string bootGameType; // Game type to use when booting. var config string bootMapPrefix; // Map prefix to used to select the map to boot. var config string bootMutators; // Mutators to load for the nexgen server boot. var string bootMutatorIndices; // Indices in the mutatorInfo list of the mutators // to load for the nexgen server boot. var config string bootOptions; // Additional boot command line options. var config string bootCommands; // Pre map switch console commands. var config string lastServerURL; // Server commandline URL of last game. // Match setup. var config bool matchModeActivated; // Whether a match is in progress. var config byte matchesToPlay; // Number of games to play for the current match. var config byte currentMatch; // Number of games to play for the current match. var config string serverPassword; // Password (Nexgen) needed to enter the server. var config bool spectatorsNeedPassword; // Do spectators need to enter the password? var config bool muteSpectatorsDuringMatch; // Should spectators be muted during the match? var config bool enableMatchBootControl; // Enable boot control for matches. var config bool matchAutoLockTeams; // Automatically lock teams in match mode. var config bool matchAutoPause; // Automatically pause game when a player leaves. var config bool matchAutoSeparate; // Automatically separate players by tag. var config string tagsToSeparate[4]; // Name tags used to separate players in teams. // Extended HUD. var config bool useNexgenMessageHUD; // Enable the Nexgen message HUD replacement. var config string replacementClass[16]; // HUD class replacement instructions. var class HUDReplacementClass; // Nexgen replacement for the HUD class. // Account system. var config string atTypeName[10]; // Account type names. var config string atRights[10]; // Rights for the account types. var config string atTitle[10]; // Titles for the account types. var config string atPassword[10]; // Password for the account types. var config string paPlayerID[128]; // Player ID list. var config string paPlayerName[128]; // Names of the player accounts. var config int paAccountType[128]; // Associated account type. var config string paCustomRights[128]; // Custom rights if no account type is used. var config string paCustomTitle[128]; // Custom title if no account type is used. var string rightsDef[18]; // Rights definitions. var byte giveRightToRoot[18]; // Whether root admins recieve this right. // Ban system. var config string bannedName[128]; // Name of banned player. var config string bannedIPs[128]; // IP address(es) of the banned player. var config string bannedIDs[128]; // Client ID(s) of the banned player. var config string banReason[128]; // Reason why the player was banned. var config string banPeriod[128]; // Ban period; how long the player is banned. const maxBanIPAddresses = 8; // Maximum number of banned IP's per ban entry. const maxBanClientIDs = 6; // Maximum number of banned ID's per ban entry. const BP_Forever = 0; // Banned forever. const BP_Matches = 1; // Banned for 'x' matches. const BP_UntilDate = 2; // Banned until some 'date'. // Misc. var string serverID; // Public (unique) server identification code. // Should be the last value replicated, so the // client can check when the replication is // complete. Edit: this is now taken care of by // the checksum variable. var class serverInfoPanelClass; // Server info panel class. var class gameInfoPanelClass; // Game info panel class. var class matchControlPanelClass; // Match control panel class. var string gameTypeInfo[32]; // Game type description strings. var string mutatorInfo[128]; // Mutator description strings. var byte activeGameType; // Index in the gameTypeInfo list of the currently // active game type. var string activeMutatorIndices; // Indices in the mutatorInfo list of the mutators // currently loaded. const IW_Fire = "P"; // Ignore weapon primary fire tag. const IW_AltFire = "S"; // Ignore weapon alt fire tag. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Static. Doesn't change during the game. staticChecksum, serverID, rightsDef, HUDReplacementClass, gameTypeInfo, mutatorInfo, activeGameType, serverInfoPanelClass, gameInfoPanelClass, activeMutatorIndices, matchControlPanelClass, // Dynamic. May change during the game. dynamicChecksums, dynamicChecksumModifiers, updateCounts, // Global server settings config type. serverName, shortName, adminName, adminEmail, globalServerPassword, globalAdminPassword, playerSlots, vipSlots, adminSlots, spectatorSlots, enableUplink, MOTDLine, // Account types config type. atTypeName, atRights, atTitle, atPassword, paCustomTitle, // User account list config type. paPlayerID, paPlayerName, paAccountType, paCustomRights, // The player ban list config type. bannedName, bannedIPs, bannedIDs, banReason, banPeriod, // Boot control settings config type. enableBootControl, restartOnLastGame, bootGameType, bootMapPrefix, bootMutatorIndices, bootOptions, bootCommands, // Match settings config type. serverPassword, spectatorsNeedPassword, matchModeActivated, matchesToPlay, currentMatch, muteSpectatorsDuringMatch, enableMatchBootControl, matchAutoLockTeams, matchAutoPause, matchAutoSeparate, tagsToSeparate, // Extra server settings config type. autoReconnectTime, maxIdleTime, spawnProtectionTime, teamKillDamageProtectionTime, teamKillPushProtectionTime, broadcastTeamKillAttempts, autoUpdateBans, removeExpiredBans, useNexgenMessageHUD, waitTime, startTime, allowTeamSwitch, allowTeamBalance, allowNameChange, autoDisableMatchTime, maxIdleTimeCP, restoreScoreOnTeamSwitch, enableNexgenStartControl, broadcastAdminActions, autoRegisterServer, // Excluded weapon list. spawnProtectExcludeWeapons, // HUD class replacement list. replacementClass, // The log settings. logEvents, logSystemMessages, logChatMessages, logPrivateMessages, logAdminActions, logToConsole, logToFile, logPath, logFileExtension, logFileNameFormat, logFileTimeStampFormat; } /*************************************************************************************************** * * $DESCRIPTION Calculates a checksum of the replicated dynamic variables. * $PARAM configType The configuration type for which the checksum is to be calculated. * $REQUIRE configType == CT_GlobalServerSettings || * configType == CT_AccountTypes || * configType == CT_UserAccounts || * configType == CT_BanList || * configType == CT_BootControl || * configType == CT_MatchSettings || * configType == CT_ExtraServerSettings || * configType == CT_ExclWeaponList || * configType == CT_HUDReplacementList || * configType == CT_LogSettings * $RETURN The checksum of the replicated variables. * **************************************************************************************************/ simulated function int calcDynamicChecksum(byte configType) { local int checksum; local int index; checksum += dynamicChecksumModifiers[configType]; switch (configType) { case CT_GlobalServerSettings: // Global server settings config type. checksum += stringHash(serverName); checksum += stringHash(shortName); checksum += stringHash(adminName); checksum += stringHash(adminEmail); checksum += stringHash(globalServerPassword); checksum += stringHash(serverPassword); checksum += playerSlots; checksum += vipSlots; checksum += adminSlots; checksum += boolHash(enableUplink); for (index = 0; index < arrayCount(MOTDLine); index++) { checksum += stringHash(MOTDLine[index]); } break; case CT_AccountTypes: // Account types config type. for (index = 0; index < arrayCount(atTypeName); index++) { checksum += stringHash(atTypeName[index]); } for (index = 0; index < arrayCount(atRights); index++) { checksum += stringHash(atRights[index]); } for (index = 0; index < arrayCount(atTitle); index++) { checksum += stringHash(atTitle[index]); } for (index = 0; index < arrayCount(atPassword); index++) { checksum += stringHash(atPassword[index]); } break; case CT_UserAccounts: // User account list config type. for (index = 0; index < arrayCount(paPlayerID); index++) { checksum += stringHash(paPlayerID[index]); } for (index = 0; index < arrayCount(paPlayerName); index++) { checksum += stringHash(paPlayerName[index]); } for (index = 0; index < arrayCount(paAccountType); index++) { checksum += paAccountType[index]; } for (index = 0; index < arrayCount(paCustomRights); index++) { checksum += stringHash(paCustomRights[index]); } for (index = 0; index < arrayCount(paCustomTitle); index++) { checksum += stringHash(paCustomTitle[index]); } break; case CT_BanList: // The player ban list config type. for (index = 0; index < arrayCount(bannedName); index++) { checksum += stringHash(bannedName[index]); } for (index = 0; index < arrayCount(bannedIPs); index++) { checksum += stringHash(bannedIPs[index]); } for (index = 0; index < arrayCount(bannedIDs); index++) { checksum += stringHash(bannedIDs[index]); } for (index = 0; index < arrayCount(banReason); index++) { checksum += stringHash(banReason[index]); } for (index = 0; index < arrayCount(banPeriod); index++) { checksum += stringHash(banPeriod[index]); } break; case CT_BootControl: // Boot control settings config type. checksum += boolHash(enableBootControl); checksum += boolHash(restartOnLastGame); checksum += stringHash(bootGameType); checksum += stringHash(bootMapPrefix); checksum += stringHash(bootMutatorIndices); checksum += stringHash(bootOptions); checksum += stringHash(bootCommands); break; case CT_MatchSettings: // Match settings config type. checksum += boolHash(spectatorsNeedPassword); checksum += boolHash(matchModeActivated); checksum += matchesToPlay; checksum += currentMatch; checksum += boolHash(muteSpectatorsDuringMatch); checksum += boolHash(enableMatchBootControl); checksum += boolHash(matchAutoLockTeams); checksum += boolHash(matchAutoPause); checksum += boolHash(matchAutoSeparate); for (index = 0; index < arrayCount(tagsToSeparate); index++) { checksum += stringHash(tagsToSeparate[index]); } break; case CT_ExtraServerSettings: // Extra server settings config type. checksum += autoReconnectTime; checksum += maxIdleTime; checksum += spawnProtectionTime; checksum += teamKillDamageProtectionTime; checksum += teamKillPushProtectionTime; checksum += boolHash(broadcastTeamKillAttempts); checksum += boolHash(autoUpdateBans); checksum += boolHash(removeExpiredBans); checksum += boolHash(useNexgenMessageHUD); checksum += waitTime; checksum += startTime; checksum += boolHash(allowTeamSwitch); checksum += boolHash(allowTeamBalance); checksum += boolHash(allowNameChange); checksum += autoDisableMatchTime; checksum += maxIdleTimeCP; checksum += boolHash(restoreScoreOnTeamSwitch); checksum += boolHash(enableNexgenStartControl); checksum += boolHash(broadcastAdminActions); checksum += boolHash(autoRegisterServer); break; case CT_ExclWeaponList: // Excluded weapon list. for (index = 0; index < arrayCount(spawnProtectExcludeWeapons); index++) { checksum += stringHash(spawnProtectExcludeWeapons[index]); } break; case CT_HUDReplacementList: // HUD class replacement list. for (index = 0; index < arrayCount(replacementClass); index++) { checksum += stringHash(replacementClass[index]); } break; case CT_LogSettings: // The log settings. checksum += boolHash(logEvents); checksum += boolHash(logSystemMessages); checksum += boolHash(logChatMessages); checksum += boolHash(logPrivateMessages); checksum += boolHash(logAdminActions); checksum += boolHash(logToConsole); checksum += boolHash(logToFile); checksum += stringHash(logPath); checksum += stringHash(logFileExtension); checksum += stringHash(logFileNameFormat); checksum += stringHash(logFileTimeStampFormat); break; } return checksum; } /*************************************************************************************************** * * $DESCRIPTION Calculates a checksum of the replicated static variables. * $RETURN The checksum of the replicated variables. * **************************************************************************************************/ simulated function int calcStaticChecksum() { local int checksum; local int index; for (index = 0; index < arrayCount(rightsDef); index++) { checksum += stringHash(rightsDef[index]); } checksum += stringHash(HUDReplacementClass); for (index = 0; index < arrayCount(gameTypeInfo); index++) { checksum += stringHash(gameTypeInfo[index]); } for (index = 0; index < arrayCount(mutatorInfo); index++) { checksum += stringHash(mutatorInfo[index]); } checksum += stringHash(serverID); checksum += stringHash(serverInfoPanelClass); checksum += stringHash(gameInfoPanelClass); checksum += activeGameType; checksum += stringHash(activeMutatorIndices); checksum += stringHash(matchControlPanelClass); return checksum; } /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a string. * $PARAM str The string that is to be hashed. * $RETURN The hash value of the given string. * **************************************************************************************************/ static function int stringHash(coerce string str) { return len(str); } // Bah, this doesn't work when server and client have different character sets. /* static function int stringHash(coerce string str) { local int hash; if (str != "") { hash = 29789 * asc(left(str, 1)) + 953 * asc(mid(str, len(str) / 2, 1)) + 31 * asc(right(str, 1)) + len(str); } return hash; } */ // This version uses too much CPU processing power. /* static function int stringHash(coerce string str) { local int hash; local int index; for (index = 0; index < len(str); index++) { hash = 31 * hash + asc(mid(str, index, 1)); } return hash; } */ /*************************************************************************************************** * * $DESCRIPTION Gives the integer hash value of a boolean. * $PARAM value The boolean value. * $RETURN The integer hash value of the given boolean value. * $ENSURE result == value ? -1 : 0 * **************************************************************************************************/ static function int boolHash(bool value) { if (value) { return 1; } else { return 0; } } /*************************************************************************************************** * * $DESCRIPTION Updates the checksums for the current replication info. * $PARAM configType The configuration type for which the checksum is to be calculated. * $ENSURE dynamicChecksums[configType] == calcDynamicChecksum(configType) * **************************************************************************************************/ function updateChecksum(byte configType) { dynamicChecksumModifiers[configType] = rand(maxInt); dynamicChecksums[configType] = calcDynamicChecksum(configType); } /*************************************************************************************************** * * $DESCRIPTION Updates the checksum for the static replication info. * $ENSURE staticChecksum == calcStaticChecksum() * **************************************************************************************************/ function updateStaticChecksum() { staticChecksum = calcStaticChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Updates the checksums for the dynamic replication info. * $ENSURE foreach(type => dynamicChecksum in dynamicChecksums) * dynamicChecksum == calcDynamicChecksum(type) * **************************************************************************************************/ function updateDynamicChecksums() { local byte index; for (index = 0; index < arrayCount(dynamicChecksums); index++) { updateChecksum(index); } } /*************************************************************************************************** * * $DESCRIPTION Loads the server configuration. * $OVERRIDE * **************************************************************************************************/ function preBeginPlay() { // Check owner. if (owner == none || !owner.isA('NexgenController')) { destroy(); return; } control = NexgenController(owner); // Finish initialization. bInitialized = true; } /*************************************************************************************************** * * $DESCRIPTION Finalizes the initialization process. Calling this function makes sure that any * uninitialized variables are set. * $REQUIRE bInitialized && bInstalled * **************************************************************************************************/ function postInitialize() { local string mutatorClass; local string remaining; local int index; local string url; local string activeGameTypeClass; local string gameTypeClass; local bool bFound; serverID = class'MD5Hash'.static.MD5String(serverKey); for (index = 0; index < arrayCount(dynamicChecksums); index++) { updateCounts[index] = 1; } setEncryptionParams(configEncryptionKey, configCodeScheme); // Read settings from server config file. globalServerPassword = encode(consoleCommand("get Engine.GameInfo GamePassword")); globalAdminPassword = encode(consoleCommand("get Engine.GameInfo AdminPassword")); // Add default right definitions. addRightDefiniton("A", control.lng.allowedToPlayRightDesc); addRightDefiniton("B", control.lng.vipSlotAccessRightDesc); addRightDefiniton("C", control.lng.adminSlotAccessRightDesc); addRightDefiniton("D", control.lng.needsNoPWRightDesc); addRightDefiniton("E", control.lng.canBeIdleRightDesc); addRightDefiniton("F", control.lng.matchAdminRightDesc); addRightDefiniton("G", control.lng.moderatorRightDesc); addRightDefiniton("K", control.lng.matchSetRightDesc); addRightDefiniton("H", control.lng.banOpRightDesc); addRightDefiniton("I", control.lng.accountMngrRightDesc); addRightDefiniton("J", control.lng.serverAdminRightDesc); addRightDefiniton("L", control.lng.canBanAccountsRightDesc); addRightDefiniton("M", control.lng.hiddenAdminRightDesc, true); // Load game & mutator lists. loadGameTypeList(); loadMutatorList(); // Load mutator index list. remaining = bootMutators; while (remaining != "") { class'NexgenUtil'.static.split(remaining, mutatorClass, remaining); index = getMutatorIndex(mutatorClass); if (index >= 0) { if (bootMutatorIndices == "") { bootMutatorIndices = string(index); } else { bootMutatorIndices = bootMutatorIndices $ separator $ index; } } } // Get current server command line. url = level.getLocalURL(); url = mid(url, instr(url, "/") + 1); lastServerURL = url; // Get currently active game type. activeGameTypeClass = string(level.game.class); index = 0; while (!bFound && index < arrayCount(gameTypeInfo) && gameTypeInfo[index] != "") { // Get game type class. class'NexgenUtil'.static.split(gameTypeInfo[index], gameTypeClass, remaining); // Check if the classes match. if (activeGameTypeClass ~= gameTypeClass) { bFound = true; activeGameType = index; } else { index++; } } // Update match settings. if (matchModeActivated) { if (currentMatch > matchesToPlay) { currentMatch = 0; matchModeActivated = false; } } // Remove expired bans if desired. if (removeExpiredBans) { cleanExpiredBans(); } // Update ban periods. updateBanPeriods(); // Clear Nexgen boot controller flag. isNexgenBoot = false; // Save data. saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Loads a list of all game types available on this server. * **************************************************************************************************/ function loadGameTypeList() { local string game; local string description; local int index; local string gameInfoStr; local class gameClass; local string mapName; // For each game type... getNextIntDesc("TournamentGameInfo", 0, game, description); while(game != "" && index < arrayCount(gameTypeInfo)) { // Get game type info. gameClass = class(dynamicLoadObject(game, class'Class')); if (class'NexgenUtil'.static.trim(description) == "") { description = gameClass.default.gameName; } gameInfoStr = game $ separator $ gameClass.default.mapPrefix $ separator $ description; // Store game type info. gameTypeInfo[index] = gameInfoStr; // Continue with next game type. getNextIntDesc("TournamentGameInfo", ++index, game, description); } } /*************************************************************************************************** * * $DESCRIPTION Loads a list of all mutators available on this server. * **************************************************************************************************/ function loadMutatorList() { local string mutator; local string description; local int index; local string mutatorInfoStr; // For each mutator... getNextIntDesc("Mutator", 0, mutator, description); while (mutator != "" && index < arrayCount(mutatorInfo)) { // Get mutator info. if (instr(description, ",") >= 0) { description = left(description, instr(description, ",")); } mutatorInfoStr = mutator $ separator $ description; // Store mutator info. mutatorInfo[index] = mutatorInfoStr; // Continue with next mutator. getNextIntDesc("Mutator", ++index, mutator, description); } } /*************************************************************************************************** * * $DESCRIPTION Generates new encryption parameters for the server. * **************************************************************************************************/ function resetEncryptionConfig() { local byte cb; local bool bValidChar; configEncryptionKey = (rand(0x10000) << 16) | rand(0x10000); configCodeScheme = ""; while (len(configCodeScheme) < 32) { do { cb = rand(93) + 33; bValidChar = (cb != 34) && (instr(configCodeScheme, chr(cb)) < 0); } until (bValidChar); configCodeScheme = configCodeScheme $ chr(cb); } setEncryptionParams(configEncryptionKey, configCodeScheme); } /*************************************************************************************************** * * $DESCRIPTION Performs a default installation of the Nexgen Server Controller. Calling this * function will automatically configure the server for first use. * $REQUIRE bInitialized && !bInstalled * **************************************************************************************************/ function install() { local string packageName; packageName = class'NexgenUtil'.default.packageName; // Create unique key for this server. serverKey = class'NexgenUtil'.static.makeKey(); // Set encryption parameters for this server. resetEncryptionConfig(); // Add default account types. addAccountType("", "A", "Player"); // No account. addAccountType("VIP", "A,B"); addAccountType("Level 3 Admin", "A,B,C,D,F", "L3 Admin"); addAccountType("Level 4 Admin", "A,B,C,D,F,G", "L4 Admin"); addAccountType("Level 5 Admin", "A,B,C,D,E,F,G,H,K", "L5 Admin"); addAccountType("Level 6 Admin", "A,B,C,D,E,F,G,H,I,K,L", "L6 Admin"); addAccountType("Level 7 Admin", "A,B,C,D,E,F,G,H,I,J,K,L", "L7 Admin"); // Set default settings. serverName = level.game.gameReplicationInfo.serverName; shortName = level.game.gameReplicationInfo.shortName; adminName = level.game.gameReplicationInfo.adminName; adminEmail = level.game.gameReplicationInfo.adminEmail; //globalServerPassword = encode(consoleCommand("get Engine.GameInfo GamePassword")); -- No longer config. //globalAdminPassword = encode(consoleCommand("get Engine.GameInfo AdminPassword")); -- No longer config. serverPassword = ""; enableUplink = consoleCommand("get IpServer.UdpServerUplink DoUplink") ~= "True"; spectatorsNeedPassword = false; playerSlots = level.game.maxPlayers; vipSlots = 0; adminSlots = 0; spectatorSlots = level.game.maxSpectators; autoUpdateBans = true; waitTime = 10; startTime = 5; autoReconnectTime = 10; maxIdleTime = 40; maxIdleTimeCP = 120; spawnProtectionTime = 10; teamKillDamageProtectionTime = 0; teamKillPushProtectionTime = 8; broadcastTeamKillAttempts = false; MOTDLine[0] = level.game.gameReplicationInfo.MOTDLine1; MOTDLine[1] = level.game.gameReplicationInfo.MOTDLine2; MOTDLine[2] = level.game.gameReplicationInfo.MOTDLine3; MOTDLine[3] = level.game.gameReplicationInfo.MOTDLine4; spawnProtectExcludeWeapons[0]="Botpack.Translocator,PS"; spawnProtectExcludeWeapons[1]="Botpack.SniperRifle,S"; enableBootControl = false; lastServerURL = ""; restartOnLastGame = true; matchModeActivated = false; matchesToPlay = 5; currentMatch = 1; serverPassword = ""; spectatorsNeedPassword = true; muteSpectatorsDuringMatch = true; enableMatchBootControl = true; matchAutoLockTeams = true; matchAutoPause = false; matchAutoSeparate = false; allowTeamSwitch = true; allowTeamBalance = true; allowNameChange = true; autoDisableMatchTime = 5; // Message HUD. useNexgenMessageHUD = true; replacementClass[0] = "BotPack.ChallengeHUD=" $ packageName $ ".NexgenHUDxDM"; replacementClass[1] = "BotPack.ChallengeTeamHUD=" $ packageName $ ".NexgenHUDxTDM"; replacementClass[2] = "BotPack.ChallengeCTFHUD=" $ packageName $ ".NexgenHUDxCTF"; replacementClass[3] = "BotPack.AssaultHUD=" $ packageName $ ".NexgenHUDxAS"; replacementClass[4] = "BotPack.ChallengeDominationHUD=" $ packageName $ ".NexgenHUDxDOM"; // Server controller has been successfully installed. bInstalled = true; saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Updates the configuration to the current Nexgen version. * $ENSURE lastInstalledVersion >= class'NexgenUtil'.default.versionCode * **************************************************************************************************/ function updateConfig() { if (lastInstalledVersion < 106) installVersion106(); if (lastInstalledVersion < 107) installVersion107(); if (lastInstalledVersion < 108) installVersion108(); //if (lastInstalledVersion < 109) installVersion109(); // etc. lastInstalledVersion = class'NexgenUtil'.default.versionCode; saveConfig(); } /*************************************************************************************************** * * $DESCRIPTION Automatically installs version 1.06 of the Nexgen Server Controller. * **************************************************************************************************/ function installVersion106() { local bool bFound; local int index; local string packageName; local string newReplacementRule; packageName = class'NexgenUtil'.default.packageName; newReplacementRule = "*=" $ packageName $ ".NexgenHUDWrapper"; // Add new HUD class replacement rule to support custom game types. while (!bFound && index < arrayCount(replacementClass)) { if (replacementClass[index] ~= newReplacementRule) { bFound = true; } else if (replacementClass[index] == "") { replacementClass[index] = newReplacementRule; bFound = true; } else { index++; } } } /*************************************************************************************************** * * $DESCRIPTION Automatically installs version 1.07 of the Nexgen Server Controller. * **************************************************************************************************/ function installVersion107() { enableNexgenStartControl = true; addAccountType("Hidden Admin", "A,B,C,D,E,F,G,H,K,M"); logAdminActions = logEvents; broadcastAdminActions = true; logToConsole = true; logToFile = false; logPath = "../Logs"; logFileExtension = "log"; logFileNameFormat = control.lng.defaultLogFileNameFormat; logFileTimeStampFormat = control.lng.defaultLogFileTimeStampFormat; } /*************************************************************************************************** * * $DESCRIPTION Automatically installs version 1.08 of the Nexgen Server Controller. * **************************************************************************************************/ function installVersion108() { autoRegisterServer = true; } /*************************************************************************************************** * * $DESCRIPTION Adds the specified account type to the server controller. * $PARAM typeName Name of the account type to add. * $PARAM rights The rights for this account type. * $PARAM title Title for players that have this account name. * $REQUIRE typeName != "" * **************************************************************************************************/ function addAccountType(string typeName, string rights, optional string title) { local int index; local bool bFound; // Find an empty account type slot. while (!bFound && index < arrayCount(atTypeName)) { if (atTypeName[index] == "") { // This slot is empty. bFound = true; atTypeName[index] = typeName; atRights[index] = rights; atTitle[index] = title; } else { // This one is used, continue search. index++; } } } /*************************************************************************************************** * * $DESCRIPTION Defines a new type of client right. Note that defining a right may fail if there * isn't a free slot available for the definition. * $PARAM rightID String identifier of the client right. * $PARAM description Description of the right. * $REQUIRE bInitialized && rightID != "" && description != "" * $RETURN True, if the right definition was added, false not. * **************************************************************************************************/ function bool addRightDefiniton(string rightID, string description, optional bool bNotForRoot) { local bool bFound; local int index; // Find empty slot. while (!bFound && index < arrayCount(rightsDef)) { if (rightsDef[index] == "") { // Empty slot found. bFound = true; rightsDef[index] = rightID $ separator $ description; giveRightToRoot[index] = byte(!bNotForRoot); } else { // Slot is in use, continue with the next one. index++; } } return bFound; } /*************************************************************************************************** * * $DESCRIPTION Validates the current configuration. Any invalid settings will be automatically * adjusted to a proper setting. * $REQUIRE bInitialized * $RETURN True, if the configuration was valid, false otherwise. * $ENSURE !old.checkConfig() ? new.checkConfig() : true * **************************************************************************************************/ function bool checkConfig() { local bool bConfigOk; local NexgenConfigChecker cCheck; cCheck = spawn(class'NexgenConfigChecker'); bConfigOk = cCheck.checkConfig(self); cCheck.destroy(); return bConfigOk; } /*************************************************************************************************** * * $DESCRIPTION Returns the index in the ban list for the given player info. * $PARAM playerName Name of the player for which the entry in the ban list is to be found. * $PARAM playerIP IP address of the player. * $PARAM playerID ID code of the player. * $RETURN The index in the ban list for the specified player if banned, -1 if the player is * not banned on the server. * $ENSURE 0 <= result && result <= arrayCount(bannedName) || result == -1 * **************************************************************************************************/ function int getBanIndex(string playerName, string playerIP, string playerID) { local int index; local bool bFound; local bool bNameMatch; local bool bIPMatch; local bool bIDMatch; // Lookup player in the ban list. while (!bFound && index < arrayCount(bannedName) && bannedName[index] != "") { bNameMatch = bannedName[index] ~= playerName; bIPMatch = instr(bannedIPs[index], playerIP) >= 0; bIDMatch = instr(bannedIDs[index], playerID) >= 0; // Match? if (bNameMatch || bIPMatch || bIDMatch) { // Oh yeah. bFound = true; } else { // Nope, maybe next. index++; } } // Return index in the ban list. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Updates the specified ban entry. If a new IP or ID for the specified entry is * detected it will be added. * $PARAM index Location in the banlist. * $PARAM playerIP IP address of the player. * $PARAM playerID ID code of the player. * $REQUIRE 0 <= index && index <= arrayCount(bannedName) && bannedName[index] != "" * $RETURN True if the specified ban entry was updated, false if no changes were made. * $ENSURE instr(bannedIPs[index], playerIP) >= 0 && instr(bannedIDs[index], playerID) >= 0 * **************************************************************************************************/ function bool updateBan(int index, string playerIP, string playerID) { local bool bIPMatch; local bool bIDMatch; local string remaining; local string currIP; local string currID; local int ipCount; local int idCount; // Compare & count IP address. remaining = bannedIPs[index]; while (!bIPMatch && remaining != "") { class'NexgenUtil'.static.split(remaining, currIP, remaining); currIP = class'NexgenUtil'.static.trim(currIP); if (currIP ~= playerIP) { bIPMatch = true; } else { ipCount++; } } // Add IP address if not already in the list and the list isn't full. if (!bIPMatch && ipCount < maxBanIPAddresses) { if (bannedIPs[index] == "") { bannedIPs[index] = playerIP; } else { bannedIPs[index] = bannedIPs[index] $ separator $ playerIP; } } // Compare & count client ID's. remaining = bannedIDs[index]; while (!bIDMatch && remaining != "") { class'NexgenUtil'.static.split(remaining, currID, remaining); currID = class'NexgenUtil'.static.trim(currID); if (currID ~= playerID) { bIDMatch = true; } else { idCount++; } } // Add client ID if not already in the list and the list isn't full. if (!bIDMatch && idCount < maxBanClientIDs) { if (bannedIDs[index] == "") { bannedIDs[index] = playerID; } else { bannedIDs[index] = bannedIDs[index] $ separator $ playerID; } } // Save changes. if (!bIPMatch || !bIDMatch) { saveConfig(); return true; } else { return false; } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified ban entry has expired. * $PARAM index Location in the banlist. * $REQUIRE 0 <= index && index <= arrayCount(bannedName) && bannedName[index] != "" * $RETURN True if the specified ban entry has expired, false if not. * **************************************************************************************************/ function bool isExpiredBan(int index) { local bool bExpired; local byte banPeriodType; local string banPeriodArgs; local int year, month, day, hour, minute; // Get period type. getBanPeriodType(banPeriod[index], banPeriodType, banPeriodArgs); // Check for expiration. if (banPeriodType == BP_Matches) { // Banned for some matches. bExpired = (int(banPeriodArgs) <= 0); } else if (banPeriodType == BP_UntilDate) { // Banned until some date. class'NexgenUtil'.static.readDate(banPeriodArgs, year, month, day, hour, minute); bExpired = level.year > year || level.year == year && (level.month > month || level.month == month && (level.day > day || level.day == day && (level.hour > hour || level.hour == hour && level.minute >= minute))); } else { // Banned forever, never expires. bExpired = false; } // Return result. return bExpired; } /*************************************************************************************************** * * $DESCRIPTION Removes all expired bans from the banlist. * $RETURN True if one ore more bans were removed from the banlist. * **************************************************************************************************/ function bool cleanExpiredBans() { local int currBan; local bool bBanDeleted; // Check each ban entry. while (currBan < arrayCount(bannedName) && bannedName[currBan] != "") { if (isExpiredBan(currBan)) { removeBan(currBan); bBanDeleted = true; } else { currBan++; } } // Return result. return bBanDeleted; } /*************************************************************************************************** * * $DESCRIPTION Removes the specified entry from the banlist. * $PARAM entryNum Location in the banlist. * $REQUIRE 0 <= entryNum && entryNum <= arrayCount(bannedName) && bannedName[entryNum] != "" * $ENSURE new.bannedName[entryNum] != old.bannedName[entryNum] * **************************************************************************************************/ function removeBan(int entryNum) { local int index; for (index = entryNum; index < arrayCount(bannedName); index++) { // Last entry? if (index + 1 == arrayCount(bannedName)) { // Yes, clear fields. bannedName[index] = ""; bannedIPs[index] = ""; bannedIDs[index] = ""; banReason[index] = ""; banPeriod[index] = ""; } else { // No, copy fields from next entry. bannedName[index] = bannedName[index + 1]; bannedIPs[index] = bannedIPs[index + 1]; bannedIDs[index] = bannedIDs[index + 1]; banReason[index] = banReason[index + 1]; banPeriod[index] = banPeriod[index + 1]; } } } /*************************************************************************************************** * * $DESCRIPTION Updates the ban period strings. Note this function should only be called once * during the game, preferrably at the beginning of the game as it might cause a * checksum mismatch for the dynamic config data. * **************************************************************************************************/ function updateBanPeriods() { local int currBan; local byte banPeriodType; local string banPeriodArgs; // Check each ban entry. while (currBan < arrayCount(bannedName) && bannedName[currBan] != "") { getBanPeriodType(banPeriod[currBan], banPeriodType, banPeriodArgs); if (banPeriodType == BP_Matches) { banPeriod[currBan] = "M" $ max(0, int(banPeriodArgs) - 1); } currBan++; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the title for the specified user account. * $PARAM accountNum Index of the user account which is to be used. * $REQUIRE 0 <= accountNum && accountNum <= arrayCount(paPlayerID) * $RETURN The title assigned to the specified account. * $ENSURE result != "" * **************************************************************************************************/ simulated function string getUserAccountTitle(byte accountNum) { if (paAccountType[accountNum] < 0) { return paCustomTitle[accountNum]; } else { return getAccountTypeTitle(paAccountType[accountNum]); } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the title for the specified account type. * $PARAM accountType Index of the account type which is to be used. * $REQUIRE 0 <= accountType && accountType <= arrayCount(atTypeName) * $RETURN The title assigned to the specified account type. * $ENSURE result != "" * **************************************************************************************************/ simulated function string getAccountTypeTitle(byte accountType) { if (atTitle[accountType] == "") { return atTypeName[accountType]; } else { return atTitle[accountType]; } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account index for the specified clientID. * $PARAM clientID The client identification code of the player whose account index is to * be returned. * $REQUIRE clientID != "" * $RETURN The index in the user account list for the specified player. In case there is no * account for the player, -1 will be returned. * $ENSURE result == -1 || 0 <= result && result <= arrayCount(paPlayerID) && * result >= 0 ? paPlayerID[result] ~= clientID : true * **************************************************************************************************/ simulated function int getUserAccountIndex(string clientID) { local int index; local bool bFound; // Locate account. while (!bFound && index < arrayCount(paPlayerID) && paPlayerID[index] != "") { // Check entry... if (paPlayerID[index] ~= clientID) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Adds a new user account to the server. Note that if there already is an existing * account for the specified clientID, that account will be overwritten. * $PARAM clientID Identification code of the player for which an account has to be * added. * $PARAM accountName Name of the player for which the account is to be made. * $PARAM accountType Type of account to use. Use -1 to indicate a custom account. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle User title for a custom account type. * $REQUIRE clientID != "" && accountName != "" && * -1 <= accountType && accountType <= arrayCount(atTypeName) && * (accountType == -1 ? customTitle != "" : true) * $RETURN The position in the user accountlist where the account was stored. If all user * account slots are occupied -1 will be returned and the account isn't saved. * $ENSURE -1 <= result && result <= arrayCount(paPlayerID) && * (result >= 0 ? paPlayerID[result] == clientID && * paPlayerName[result] == accountName && * paAccountType[result] == accountType && * paCustomRights[result] == customRights && * paCustomTitle[result] == customTitle * : true) * **************************************************************************************************/ function int addUserAccount(string clientID, string accountName, int accountType, optional string customRights, optional string customTitle) { local int index; local bool bFound; // Check if player already has an account. index = getUserAccountIndex(clientID); if (index >= 0) { // Yeah, update existing account. bFound = true; } else { // Nope find an empty slot. index = 0; while (!bFound && index < arrayCount(paPlayerID)) { if (paPlayerID[index] == "") { // Empty slot found, use it! bFound = true; } else { // Slot is used, check next one. index++; } } } // Store account. if (bFound) { paPlayerID[index] = clientID; paPlayerName[index] = accountName; paAccountType[index] = accountType; paCustomRights[index] = customRights; paCustomTitle[index] = customTitle; } // Return index of created account. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Creates a rights string containing all available rights. * $PARAM bForRootAdmin Whether to include only the rights that are for root admins. * $RETURN A rights string with all rights that are defined on this server. * $ENSURE result != "" * **************************************************************************************************/ function string getAllRights(optional bool bForRootAdmin) { local int index; local string rights; while (index < arrayCount(rightsDef) && rightsDef[index] != "") { if (!bForRootAdmin || bool(giveRightToRoot[index])) { if (index > 0) { rights = rights $ separator $ left(rightsDef[index], instr(rightsDef[index], separator)); } else { rights = left(rightsDef[index], instr(rightsDef[index], separator)); } } index++; } return rights; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the ban period type and its argument from the specified ban period * string. * $PARAM banPeriodStr The ban period description string. * $PARAM banPeriodType Ban period type number. * $PARAM args Optional arguments of the ban period type. * $ENSURE banPeriodType == BP_Forever || * banPeriodType == BP_Matches || * banPeriodType == BP_UntilDate * **************************************************************************************************/ static function getBanPeriodType(string banPeriodStr, out byte banPeriodType, out string args) { if (banPeriodStr == "") { banPeriodType = BP_Forever; } else if (left(banPeriodStr, 1) ~= "M") { banPeriodType = BP_Matches; args = mid(banPeriodStr, 1); } else if (left(banPeriodStr, 1) ~= "U") { banPeriodType = BP_UntilDate; args = mid(banPeriodStr, 1); } else { banPeriodType = BP_Forever; } } /*************************************************************************************************** * * $DESCRIPTION Locates the position of the specified mutator in the mutator list. * $PARAM mutatorClass The class of the mutator that is to be located. * $RETURN The index of the specified mutator in the mutator list, or -1 if the mutator isn't * found. * **************************************************************************************************/ simulated function int getMutatorIndex(string mutatorClass) { local int index; local bool bFound; local string currClass; local string remaining; // Attempt to locate mutator. while (!bFound && index < arrayCount(mutatorInfo) && mutatorInfo[index] != "") { remaining = mutatorInfo[index]; class'NexgenUtil'.static.split(remaining, currClass, remaining); if (currClass ~= mutatorClass) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Locates the position of the specified game in the game list. * $PARAM gameClass The class of the game that is to be located. * $RETURN The index of the specified gane in the game list, or -1 if the game isn't found. * **************************************************************************************************/ simulated function int getGameIndex(string gameClass) { local int index; local bool bFound; local string currClass; local string remaining; // Attempt to locate mutator. while (!bFound && index < arrayCount(gameTypeInfo) && gameTypeInfo[index] != "") { remaining = gameTypeInfo[index]; class'NexgenUtil'.static.split(remaining, currClass, remaining); if (currClass ~= gameClass) { bFound = true; } else { index++; } } // Return result. if (bFound) { return index; } else { return -1; } } /*************************************************************************************************** * * $DESCRIPTION Sets the list of active mutators. * **************************************************************************************************/ function setActiveMutatorList() { local Mutator m; local int index; // Reset list. activeMutatorIndices = ""; // Get name for each mutator instance. foreach allActors(class'Mutator', m) { index = getMutatorIndex(string(m.class)); if (index >= 0) { if (activeMutatorIndices == "") { activeMutatorIndices = string(index); } else { activeMutatorIndices = activeMutatorIndices $ separator $ string(index); } } } // Update checksum. updateStaticChecksum(); } /*************************************************************************************************** * * $DESCRIPTION Sets the current encryption parameters. * **************************************************************************************************/ simulated function setEncryptionParams(int k, string cs) { default.encryptionKey = k; default.codeScheme = cs; } /*************************************************************************************************** * * $DESCRIPTION Retrieves the current encryption parameters. * **************************************************************************************************/ function int getEncryptionParams(out int k, out string cs) { k = default.encryptionKey; cs = default.codeScheme; } /*************************************************************************************************** * * $DESCRIPTION Encodes the given string. * $PARAM str The string that is to be encoded. * $RETURN The encoded string. * $ENSURE decode(result) == str * **************************************************************************************************/ simulated function string encode(string str) { local string output; local int index; local byte ck[4]; local byte cb; local byte nb; if (len(default.codeScheme) != 32 || str == "") return ""; ck[0] = default.encryptionKey & 0xFF; ck[1] = (default.encryptionKey >>> 8) & 0xFF; ck[2] = (default.encryptionKey >>> 16) & 0xFF; ck[3] = (default.encryptionKey >>> 24) & 0xFF; for (index = 0; index <= len(str); index++) { if (index == len(str)) { cb = nb; nb = 0; } else if (index == len(str) - 1) { cb = asc(mid(str, index, 1)); nb = rand(256); } else { cb = asc(mid(str, index, 1)); nb = asc(mid(str, index + 1, 1)); } cb = cb ^ ck[index % 4] ^ nb; output = output $ mid(default.codeScheme, (cb >>> 4) & 0x0F, 1) $ mid(default.codeScheme, cb & 0x0F, 1); } return output; } /*************************************************************************************************** * * $DESCRIPTION Decodes the given string. * $PARAM str The string that is to be decoded. * $RETURN The decoded string. * **************************************************************************************************/ simulated function string decode(string str) { local string output; local int index; local int ci; local byte ck[4]; local byte cb; local byte lb; if (len(default.codeScheme) != 32 || str == "" || len(str) % 2 == 1) return ""; ck[0] = default.encryptionKey & 0xFF; ck[1] = (default.encryptionKey >>> 8) & 0xFF; ck[2] = (default.encryptionKey >>> 16) & 0xFF; ck[3] = (default.encryptionKey >>> 24) & 0xFF; for (index = len(str) / 2 - 1; index >= 0; index--) { ci = instr(default.codeScheme, mid(str, index * 2 + 1, 1)); if (ci < 0) return ""; else cb = ci; ci = instr(default.codeScheme, mid(str, index * 2, 1)); if (ci < 0) return ""; else cb = cb | (ci << 4); if (index == len(str) / 2 - 1) { lb = cb ^ ck[index % 4]; } else { cb = cb ^ lb ^ ck[index % 4]; output = chr(cb) $ output; lb = cb; } } return output; } /*************************************************************************************************** * * $DESCRIPTION Wrapper function for retrieving an element from the paAccountType array. * This function is required because the array can't be accessed outside this class * once its size exceeds 63 entries. Increasing the size beyond 63 will make the * compiler go mad. * $PARAM index The index of the element that is to be retrieved. * $RETURN The value of the element stored at the specified index. * **************************************************************************************************/ simulated function int get_paAccountType(int index) { return paAccountType[index]; } /*************************************************************************************************** * * $DESCRIPTION Wrapper function for storing an element to the paAccountType array. * This function is required because the array can't be accessed outside this class * once its size exceeds 63 entries. Increasing the size beyond 63 will make the * compiler go mad. * $PARAM index The index of the element that is to be stored. * $PARAM value The value that is to be stored. * **************************************************************************************************/ simulated function set_paAccountType(int index, int value) { paAccountType[index] = value; } @zFHkEk>6|H,JH,,<  AG "AutoSSNormalGame"}FF"YUnJs%Bs}F"F,FF"s&sF  CG "PlayPMSound"FVG "ShowPlayerLoc"x/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenCommandHandler * $VERSION 1.08 (14-6-2008 12:42) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Handles the nexgen commands issued via the mutate command in the console. The * command handling has been placed into this class because it gets rather lengthy. * This saves the NexgenController class from becoming one huge unreadable monster. * **************************************************************************************************/ class NexgenCommandHandler extends info; var NexgenController control; // Server controller. var NexgenLang lng; // Language instance to support localization. const openMapVoteCommand = "BDBMAPVOTE VOTEMENU"; // Mutate command to open the mapvote window. /*************************************************************************************************** * * $DESCRIPTION Initializes the command handler. * $REQUIRE owner != none && owner.isA('NexgenController') * $ENSURE control != none && lng != none * **************************************************************************************************/ function preBeginPlay() { control = NexgenController(owner); lng = control.lng; } /*************************************************************************************************** * * $DESCRIPTION Executes the given command. * $PARAM client The client that has issued the command. * $PARAM cmd Name of the command to execute. * $PARAM args Arguments for the command to execute. * $REQUIRE sender != none * **************************************************************************************************/ function execCommand(NexgenClient client, string cmd, string args[10]) { local bool bInvalidCommand; local bool bSuccess; local string failMessage; local string reason; // Determine action. failMessage = lng.commandFailedMsg; reason = lng.internalErrorMsg; switch (caps(cmd)) { case control.CMD_SwitchTeam: bSuccess = execSwitchTeam(client, int(args[0]), reason); failMessage = lng.teamSwitchFailedMsg; break; case control.CMD_BalanceTeams: bSuccess = execBalanceTeams(client, reason); failMessage = lng.teamBalanceFailedMsg; break; case control.CMD_Play: bSuccess = execJoinAsPlayer(client, reason); break; case control.CMD_Spectate: bSuccess = execJoinAsSpec(client, reason); break; case control.CMD_StartGame: bSuccess = execStartGame(client, reason); break; case control.CMD_Pause: bSuccess = execPauseGame(client, reason); break; case control.CMD_Exit: bSuccess = true; client.clientCommand(client.exitCommand); break; case control.CMD_Disconnect: bSuccess = true; client.clientCommand(client.disconnectCommand); break; case control.CMD_Open: bSuccess = true; client.showPanel(); break; case control.CMD_OpenVote: bSuccess = true; level.game.baseMutator.mutate(openMapVoteCommand, client.player); break; default: bInvalidCommand = true; } // Check outcome. if (bInvalidCommand) { client.showMsg(lng.invalidCommandMsg); } else if (!bSuccess) { client.showMsg(lng.format(failMessage, reason)); } } /*************************************************************************************************** * * $DESCRIPTION Executes a team switch command. * $PARAM client The client that has issued the command. * $PARAM newTeam The team where the player wants to switch to. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execSwitchTeam(NexgenClient client, int newTeam, out string reason) { local bool bAllowed; // Check if the player is allowed to switch. if (client.bSpectator) { reason = lng.specTeamSwitchMsg; } else if (control.gInf.bNoTeamSwitch) { reason = lng.teamSwitchDisabledMsg; /* } else if ('player has switched too much') { reason = lng.switchLimitReachedMsg; */ } else if (client.bNoTeamSwitch) { reason = lng.playerTeamSwitchDisabledMsg; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if ((newTeam < 0) || (level.game.isA('TeamGamePlus') && newTeam >= TeamGamePlus(level.game).maxTeams) || (newTeam > 3)) { reason = lng.invalidTeamMsg; } else if (newTeam == client.player.playerReplicationInfo.team) { reason = lng.format(lng.sameTeamMsg, lng.getTeamName(newTeam)); } else { // All seems to be ok. bAllowed = true; } // Switch team if allowed. if (bAllowed) { client.setTeam(newTeam); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a reconnect as spectator command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execJoinAsSpec(NexgenClient client, out string reason) { local bool bAllowed; local int numSpecs; local NexgenClient currClient; // Count spectators. currClient = control.clientList; while (currClient != none) { if (currClient.bSpectator) { numSpecs++; } currClient = currClient.nextClient; } // Check if the player is allowed to join as a spectator. if (control.sConf.spectatorSlots == 0) { reason = lng.noSpecsAllowedMsg; } else if (numSpecs >= control.sConf.spectatorSlots && !client.bSpectator) { reason = lng.noMoreSpecSlotsMsg; } else { bAllowed = true; } // Reconnect as spectator if allowed. if (bAllowed) { client.reconnect(client.RCN_ReconnectAsSpec); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a reconnect as player command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execJoinAsPlayer(NexgenClient client, out string reason) { local bool bAllowed; // Check if the player is allowed to join as a player. if (!client.hasRight(client.R_MayPlay)) { reason = lng.noPlayingRightsMsg; } else if (client.bSpectator && !control.canGetSlot(client)) { reason = lng.noMorePlayerSlotsMsg; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else { bAllowed = true; } // Reconnect as player if allowed. if (bAllowed) { client.reconnect(client.RCN_ReconnectAsPlayer); } return bAllowed; } /*************************************************************************************************** * * $DESCRIPTION Executes a balance team command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execBalanceTeams(NexgenClient client, out string reason) { local bool bAllowed; local bool bSuccess; // Check if the player is allowed to balance the teams. if (!level.game.isA('TeamGamePlus')) { reason = lng.notATeamGameMsg; } else if (client.hasRight(client.R_MatchAdmin)) { bAllowed = true; } else if (control.gInf.bTeamsLocked) { reason = lng.teamsLockedMsg; } else if (control.gInf.bNoTeamBalance) { reason = lng.teamBalanceDisabledMsg; } else { bAllowed = true; } // Balance teams if allowed. if (bAllowed) { bSuccess = control.balanceTeams(); if (bSuccess) { control.broadcastMsg(lng.balanceMsg, client.playerName, , , , client.player.playerReplicationInfo); } else { reason = lng.teamsAlreadyBalancedMsg; } } return bAllowed && bSuccess; } /*************************************************************************************************** * * $DESCRIPTION Executes a start game command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execStartGame(NexgenClient client, out string reason) { local bool bAllowed; if (!control.sConf.enableNexgenStartControl) { // bAllowed = false; } else if (control.gInf.gameState != control.gInf.GS_Ready) { // bAllowed = false; } else if (control.sConf.matchModeActivated && control.gInf.matchAdminCount > 0 && !client.hasRight(client.R_MatchAdmin) && !control.gInf.bTournamentMode) { // bAllowed = false; } else { bAllowed = true; } if (bAllowed) { if (control.gInf.bTournamentMode) { if (!client.bSpectator && !client.bIsReadyToPlay) { client.bIsReadyToPlay = true; control.broadcastMsg(lng.playerReadyMsg, client.playerName, , , , client.player.playerReplicationInfo); control.doTournamentModeReadySignalCheck(); } } else { control.startGame(); control.broadcastMsg(lng.launchMsg, client.playerName, , , , client.player.playerReplicationInfo); } } return true; } /*************************************************************************************************** * * $DESCRIPTION Executes a pause game command. * $PARAM reason Reason why the command has failed to execute. * $REQUIRE client != none * $RETURN True if the command has been executed, false if it failed to execute. * **************************************************************************************************/ function bool execPauseGame(NexgenClient client, out string reason) { NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)).pauseGame(); return true; } @Gjmn9qLo"[~L%port%%LL%port%SjO~L%name%%wj*LL%name%js~L%admin%%wj*LL%admin%j[v~L%serverid%%wj*LL%serverid%jO Lj ZLL  n5/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientLoginHandler * $VERSION 1.01 (2-8-2008 14:12) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Default login handler for the Nexgen Server Controller. * **************************************************************************************************/ class NexgenClientLoginHandler extends Object abstract; /*************************************************************************************************** * * $DESCRIPTION Retrieves the login parameters for the specified client. * $PARAM client The Nexgen client whose login parameters are to be retrieved. * $PARAM clientID Unique client identifier GUID. * $PARAM loginOptions Extra login parameters. * **************************************************************************************************/ static function getLoginParameters(NexgenClient client, out string clientID, out string loginOptions) { local string clientKey; local string password; // Load client key. clientKey = client.gc.get(client.SSTR_ClientKey); clientID = client.gc.get(client.SSTR_ClientID); if (clientKey == "") { // Client has no key. Create a new one. clientKey = class'NexgenUtil'.static.makeKey(); clientID = class'MD5Hash'.static.MD5String(clientKey); client.gc.set(client.SSTR_ClientKey, clientKey); client.gc.set(client.SSTR_ClientID, clientID); client.gc.saveConfig(); } else if (class'MD5Hash'.static.MD5String(clientKey) != clientID) { // KEY/ID mismatch. Reset key. clientKey = class'NexgenUtil'.static.makeKey(); clientID = class'MD5Hash'.static.MD5String(clientKey); client.gc.set(client.SSTR_ClientKey, clientKey); client.gc.set(client.SSTR_ClientID, clientID); client.gc.saveConfig(); } // Load login info. password = client.sc.get(client.serverID, client.SSTR_ServerPassword); // Set login options. if (password != "") class'NexgenUtil'.static.addProperty(loginOptions, client.SSTR_ServerPassword, password); class'NexgenUtil'.static.addProperty(loginOptions, client.SSTR_ClientKey, clientKey); } /*************************************************************************************************** * * $DESCRIPTION Checks the login parameters send by the client. * $PARAM client The Nexgen client whose login parameters are to be checked. * **************************************************************************************************/ static function bool checkLoginParameters(NexgenClient client) { local bool bLoginParametersValid; local string clientKey; // Check client ID. bLoginParametersValid = class'NexgenUtil'.static.isValidClientID(client.playerID); // Check client key. if (bLoginParametersValid) { clientKey = class'NexgenUtil'.static.getProperty(client.loginOptions, client.SSTR_ClientKey); bLoginParametersValid = class'NexgenUtil'.static.isValidKey(clientKey); } // Check client ID / key match. if (bLoginParametersValid) { bLoginParametersValid = (class'MD5Hash'.static.MD5String(clientKey) == client.playerID); } // Return result. return bLoginParametersValid; } KG "Boot control"L@B /*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientCore * $VERSION 1.32 (9-8-2008 19:28) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Core client controller class that takes care of the basic Nexgen functionality. * **************************************************************************************************/ class NexgenClientCore extends NexgenClientController; var string blockedPlayers[32]; // List containing all clientIDs of blocked players. var bool bBlockAll; // Whether the client doesn't want to receive PMs at all. const openURLCommand = "open"; // Console command for opening an URL. const restartURL = "?restart"; // Server URL for restarting the server. const separator = ","; // Character used to seperate elements in a list. const rebootDelay = 10; // Delay in seconds before the server will be rebooted. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... receivePM, receivePassword; reliable if (role == ROLE_SimulatedProxy) // Replicate to server... setServerSettings, sendPM, addAccountType, updateAccountType ,deleteAccountType, moveAccountType, deleteAccount, updateAccount, addAccount, pauseGame, endGame, restartGame, setPlayerTeam, toggleTeamSwitch, reconnectPlayer, sendPlayerToURL, toggleGlobalTeamSwitch, toggleGlobalTeamBalance, toggleLockedTeams, adminLogin, deleteBan, addBan, updateBan, updateBootControl, separatePlayers, sendPassword, updateMatchSettings, toggleMatchMode, togglePlayerMute, setPlayerName, kickPlayer, showAdminMessage, toggleGlobalMute, toggleGlobalNameChange, banPlayer, setServerSettingsExt1, setServerSettingsExt2, rebootServer, delIgnoredWeapon, saveIgnoredWeapon, delHUDReplacementClass, saveHUDReplacementClass, toggleGlobalTournamentMode, setLogSettings; } /*************************************************************************************************** * * $DESCRIPTION Checks whether the specified client is blocked by this player. Blocked players * will not be able to send private messages to this client. * $PARAM clientID The identification string of the player which is to be checked. * $REQUIRE clientID != "" * $RETURN True if the specified client is blocked, false if not. * **************************************************************************************************/ simulated function bool isBlocked(string clientID) { local int index; local bool bBlocked; while (!bBlocked && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] ~= clientID) { bBlocked = true; } else { index++; } } return bBlocked; } /*************************************************************************************************** * * $DESCRIPTION Blocks the specified player. * $PARAM clientID The identification string of the player which is to be blocked. * $REQUIRE clientID != "" * **************************************************************************************************/ simulated function blockPlayer(string clientID) { local int index; local bool bFound; // Cancel if player is blocked already. if (isBlocked(clientID)) { return; } // Find a free slot. while (!bFound && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] == "") { bFound = true; blockedPlayers[index] = clientID; } else { index++; } } } /*************************************************************************************************** * * $DESCRIPTION Unblocks the specified player. * $PARAM clientID The identification string of the player which is to be unblocked. * $REQUIRE clientID != "" * **************************************************************************************************/ simulated function unblockPlayer(string clientID) { local int index; local bool bFound; // Find a free slot. while (!bFound && index < arrayCount(blockedPlayers)) { if (blockedPlayers[index] ~= clientID) { bFound = true; blockedPlayers[index] = ""; } else { index++; } } } /*************************************************************************************************** * * $DESCRIPTION Modifies the setup of the Nexgen remote control panel. * $OVERRIDE * **************************************************************************************************/ simulated function setupControlPanel() { // Client. client.mainWindow.mainPanel.addPanel(client.lng.clientTabTxt, class'NexgenPanelContainer', "client"); client.mainWindow.mainPanel.addPanel(client.lng.homeTabTxt, class'NexgenRCPHome', , "client"); client.addPluginClientConfigPanel(class'NexgenRCPClientConfig'); client.mainWindow.mainPanel.addPanel(client.lng.privateMessageTabTxt, class'NexgenRCPPrivateMsg', , "client"); // Match. client.mainWindow.mainPanel.addPanel(client.lng.gameTabTxt, class'NexgenPanelContainer', "game"); if (client.sConf.gameInfoPanelClass != none) { client.mainWindow.mainPanel.addPanel(client.lng.infoTabTxt, client.sConf.gameInfoPanelClass, , "game"); } if (client.hasRight(client.R_Moderate)) { client.mainWindow.mainPanel.addPanel(client.lng.moderatorTabTxt, class'NexgenRCPModerate', , "game"); } if (client.hasRight(client.R_MatchAdmin) && client.sConf.matchControlPanelClass != none) { client.mainWindow.mainPanel.addPanel(client.lng.matchControlTabTxt, client.sConf.matchControlPanelClass, , "game"); } if (client.hasRight(client.R_MatchSet)) { client.mainWindow.mainPanel.addPanel(client.lng.matchSetupTabTxt, class'NexgenRCPMatchSet', , "game"); } // Server. client.mainWindow.mainPanel.addPanel(client.lng.serverTabTxt, class'NexgenPanelContainer', "server"); if (client.sConf.serverInfoPanelClass != none) { client.mainWindow.mainPanel.addPanel(client.lng.infoTabTxt, client.sConf.serverInfoPanelClass, , "server"); } if (client.hasRight(client.R_BanOperator)) { client.mainWindow.mainPanel.addPanel(client.lng.banControlTabTxt, class'NexgenRCPBanControl', , "server"); } if (client.hasRight(client.R_AccountManager)) { client.mainWindow.mainPanel.addPanel(client.lng.accountsTabTxt, class'NexgenRCPUserAccounts', , "server"); } if (client.hasRight(client.R_ServerAdmin)) { client.mainWindow.mainPanel.addPanel(client.lng.accountTypesTabTxt, class'NexgenRCPAccountTypes', , "server"); client.mainWindow.mainPanel.addPanel(client.lng.settingsTabTxt, class'NexgenPanelContainer', "serversettings", "server"); client.mainWindow.mainPanel.addPanel(client.lng.basicSettingsTabTxt, class'NexgenRCPServerSettings', , "server,serversettings"); client.mainWindow.mainPanel.addPanel(client.lng.nexgenSettingsTabTxt, class'NexgenScrollPanelContainer', "nexgensettings", "server,serversettings"); client.mainWindow.mainPanel.addPanel("", class'NexgenRCPMiscNexgenSettings', , "server,serversettings,nexgensettings"); client.mainWindow.mainPanel.addPanel("", class'NexgenRCPLogSettings', , "server,serversettings,nexgensettings"); client.mainWindow.mainPanel.addPanel("", class'NexgenRCPIgnoredWeaponsSettings', , "server,serversettings,nexgensettings"); client.mainWindow.mainPanel.addPanel("", class'NexgenRCPReplacementHUDSettings', , "server,serversettings,nexgensettings"); client.mainWindow.mainPanel.addPanel(client.lng.bootTabTxt, class'NexgenRCPBootControl', , "server,serversettings"); } // About. client.mainWindow.mainPanel.addPanel(client.lng.aboutTabTxt, class'NexgenRCPAbout'); } /*************************************************************************************************** * * $DESCRIPTION Updates the global server settings & notifies the other clients. * $PARAM serverName Name of the server. * $PARAM shortServerName Short name of the server. * $PARAM MOTD1 Message of the day line 1. * $PARAM MOTD2 Message of the day line 2. * $PARAM MOTD3 Message of the day line 3. * $PARAM MOTD4 Message of the day line 4. * $PARAM adminName Name of the server administrator. * $PARAM adminEmail E-Mail address of the server administrator. * $PARAM serverPassword Password for entering the server. * $PARAM adminPassword Password needed to login as server administrator. * $PARAM playerSlots Number of players allowed. * $PARAM vipSlots Amount of extra slots available reserved for VIPs. * $PARAM adminSlots Amount of extra slots available reserved for admins. * $PARAM specSlots How many spectators are allowed during the game. * $PARAM bEnableUplink Indicates if the server should connect to the master server. * **************************************************************************************************/ function setServerSettings(string serverName, string shortServerName, string MOTD1, string MOTD2, string MOTD3, string MOTD4, string adminName, string adminEmail, string serverPassword, string adminPassword, int playerSlots, int vipSlots, int adminSlots, int specSlots, bool bEnableUplink) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Check values. if (serverName == "") serverName = control.sConf.serverName; if (playerSlots < 0) playerSlots = 0; if (vipSlots < 0) vipSlots = 0; if (adminSlots < 0) adminSlots = 0; if (specSlots < 0) specSlots = 0; if (playerSlots > 32) playerSlots = 32; if (vipSlots > 16) vipSlots = 16; if (adminSlots > 16) adminSlots = 16; if (specSlots > 16) specSlots = 16; if (playerSlots + vipSlots + adminSlots <= 0) playerSlots = 16; // Save settings. control.sConf.serverName = serverName; control.sConf.shortName = shortServerName; control.sConf.MOTDLine[0] = MOTD1; control.sConf.MOTDLine[1] = MOTD2; control.sConf.MOTDLine[2] = MOTD3; control.sConf.MOTDLine[3] = MOTD4; control.sConf.adminName = adminName; control.sConf.adminEmail = adminEmail; control.sConf.globalServerPassword = control.sConf.encode(serverPassword); control.sConf.globalAdminPassword = control.sConf.encode(adminPassword); control.sConf.playerSlots = playerSlots; control.sConf.vipSlots = vipSlots; control.sConf.adminSlots = adminSlots; control.sConf.spectatorSlots = specSlots; control.sConf.enableUplink = bEnableUplink; control.sConf.saveConfig(); // Apply settings. level.game.gameReplicationInfo.serverName = serverName; level.game.gameReplicationInfo.shortName = shortServerName; level.game.gameReplicationInfo.MOTDLine1 = MOTD1; level.game.gameReplicationInfo.MOTDLine2 = MOTD2; level.game.gameReplicationInfo.MOTDLine3 = MOTD3; level.game.gameReplicationInfo.MOTDLine4 = MOTD4; level.game.gameReplicationInfo.adminName = adminName; level.game.gameReplicationInfo.adminEmail = adminEmail; consoleCommand("set Engine.GameInfo GamePassword" @ serverPassword); consoleCommand("set Engine.GameInfo AdminPassword" @ adminPassword); level.game.maxPlayers = playerSlots + vipSlots + adminSlots; level.game.maxSpectators = specSlots; if (bEnableUplink) { consoleCommand("set IpServer.UdpServerUplink DoUplink True"); } else { consoleCommand("set IpServer.UdpServerUplink DoUplink False"); } // Notify clients. control.signalConfigUpdate(control.sConf.CT_GlobalServerSettings); client.showMsg(control.lng.settingsSavedMsg); // Log action. logAdminAction(control.lng.adminUpdateGlobalServerSettings); } /*************************************************************************************************** * * $DESCRIPTION Send a private message to the player with the specified player code. Note that the * message may not be send if the client isn't allowed to, e.g. he/she is muted. * $PARAM playerNum Player code of the player that should receive the message. * $PARAM msg The message to send. * $PARAM bWindowed Whether the message to send should popup in a window. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ function sendPM(int playerNum, string msg, optional bool bWindowed) { local NexgenClient target; local NexgenClientCore targetCtrl; local string senderTitle; // Check for abuse. if (!client.hasRight(client.R_Moderate) && (client.isMuted() || bWindowed)) { return; } // Locate target player. target = control.getClientByNum(playerNum); // Check if player is allowed to send message when in match mode. Not allowed if: // - match mode is activated and spectators are muted during the match... if (control.sConf.matchModeActivated && control.sConf.muteSpectatorsDuringMatch && // - AND the match is in progress... control.gInf.gameState == control.gInf.GS_Playing && // - AND the player is a spectator and the target player isn't.. client.bSpectator && !target.bSpectator && // - AND the player isn't a moderator or match admin. !client.hasRight(client.R_MatchAdmin) && !client.hasRight(client.R_Moderate)) { return; } // Get sender title. if (client.bHasAccount) { senderTitle = client.title; } // Send private message. if (target != none) { targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePM(client.playerID, client.player.playerReplicationInfo, msg, bWindowed, client.hasRight(client.R_Moderate), senderTitle); control.nscLog(client.playerName $ " -> " $ target.playerName $ ": " $ msg, control.LT_PrivateMsg); } } /*************************************************************************************************** * * $DESCRIPTION Send a private message to the player with the specified player code. Note that the * message may not be send if the client isn't allowed to, e.g. he/she is muted. * $PARAM senderID Client ID of the player that has send the message. * $PARAM pri The player replication info actor of the sending player. * $PARAM msg The message that was received. * $PARAM bWindowed Whether the received message should popup in a window. * $PARAM bForced Indicates if the message should be forced (i.e. can't be blocked). * $PARAM senderTitle Title of the player that has send the message. * $REQUIRE senderID != "" && pri != none * **************************************************************************************************/ simulated function receivePM(string senderID, PlayerReplicationInfo pri, string msg, optional bool bWindowed, optional bool bForced, optional string senderTitle) { local NexgenRCPPrivateMsg pmPanel; // Check if message is blocked. if (!bForced && (bBlockAll || isBlocked(senderID))) { return; } // Play PM receive sound. if (client.gc.get(client.SSTR_PlayPMSound, "true") ~= "true") { //client.player.playSound(sound'UnrealShare.TransA3', SLOT_Misc); client.player.clientPlaySound(sound'UnrealShare.TransA3', , true); } // Display message. pmPanel = NexgenRCPPrivateMsg(client.mainWindow.mainPanel.getPanel(class'NexgenRCPPrivateMsg'.default.panelIdentifier)); if (pmPanel != none) { pmPanel.receiveMessage(msg, pri); } if (bWindowed) { client.showPopup("NexgenPrivateMsgDialog", msg, pri.playerName); } else { client.showMsg(client.lng.format(client.lng.receivedPMMsg, pri.playerName, msg, senderTitle), pri); } } /*************************************************************************************************** * * $DESCRIPTION Adds the specified account type to the list. * $PARAM atTypeName Name of the account type to add. * $PARAM atRights Rights assigned to the account type. * $PARAM atTitle Display title of the account type. * $PARAM atPassword Password for the account type. * **************************************************************************************************/ function addAccountType(string atTypeName, string atRights, string atTitle, string atPassword) { local int index; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || control.sConf.atTypeName[arrayCount(control.sConf.atTypeName) - 1] != "") { return; } // Locate a free entry. while (control.sConf.atTypeName[index] != "") { index++; } // Store the account type. if (atTypeName == "") { control.sConf.atTypeName[index] = control.lng.format(control.lng.accountTypeNameStr, index + 1); } else { control.sConf.atTypeName[index] = class'NexgenUtil'.static.trim(atTypeName); } control.sConf.atRights[index] = atRights; control.sConf.atTitle[index] = class'NexgenUtil'.static.trim(atTitle); control.sConf.atPassword[index] = control.sConf.encode(atPassword); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); // Log action. logAdminAction(control.lng.adminAddAccountType, atTypeName, , , true); } /*************************************************************************************************** * * $DESCRIPTION Updates the account type info for the specified account type. * $PARAM accountTypeNum The index of the account type that is to be updated. * $PARAM atTypeName Name of the account type to update. * $PARAM atRights Rights assigned to the account type. * $PARAM atTitle Display title of the account type. * $PARAM atPassword Password for the account type. * **************************************************************************************************/ function updateAccountType(byte accountTypeNum, string atTypeName, string atRights, string atTitle, string atPassword) { // Preliminary checks. if (!client.hasRight(client.R_AccountManager)) { return; } // Store the account type. if (atTypeName != "") { control.sConf.atTypeName[accountTypeNum] = class'NexgenUtil'.static.trim(atTypeName); } control.sConf.atRights[accountTypeNum] = atRights; control.sConf.atTitle[accountTypeNum] = class'NexgenUtil'.static.trim(atTitle); control.sConf.atPassword[accountTypeNum] = control.sConf.encode(atPassword); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); updatePlayerTitles(); // Log action. logAdminAction(control.lng.adminUpdateAccountType, atTypeName, , , true); } /*************************************************************************************************** * * $DESCRIPTION Updates the titles of each player. Iterates over the player list and checks * whether the title of a player has changed. In case it has changed all players * will be notified. * **************************************************************************************************/ function updatePlayerTitles() { local NexgenClient c; local string newTitle; local int accountNum; // For each client... for (c = control.clientList; c != none; c = c.nextClient) { // Get new title... accountNum = control.sConf.getUserAccountIndex(c.playerID); if (accountNum < 0) { if (c.bSpectator) { newTitle = control.lng.specTitle; } else { newTitle = control.sConf.getAccountTypeTitle(0); } } else { newTitle = control.sConf.getUserAccountTitle(accountNum); } // Compare with old title & update if necessary. if (c.title != newTitle) { c.title = newTitle; control.announcePlayerAttrChange(c, c.PA_Title, newTitle); } } } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified account type from the list. * $PARAM accountTypeNum The index of the account type that is to be deleted. * **************************************************************************************************/ function deleteAccountType(byte accountTypeNum) { local bool userAccountsChanged; local int index; local string removedAccountType; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || accountTypeNum >= arrayCount(control.sConf.atTypeName) || accountTypeNum < 1 || control.sConf.atTypeName[accountTypeNum] == "") { return; } // Update user account entries. while (index < arrayCount(control.sConf.paPlayerID) && control.sConf.paPlayerID[index] != "") { // Update account info? if (control.sConf.get_paAccountType(index) == accountTypeNum) { // User has the deleted account type, give him/her the default account. control.sConf.set_paAccountType(index, 0); userAccountsChanged = true; } else if (control.sConf.get_paAccountType(index) >= accountTypeNum) { // User has an account type num above the deleted account, update account type num. control.sConf.set_paAccountType(index, control.sConf.get_paAccountType(index) - 1); userAccountsChanged = true; } // Continue with next account. index++; } // Copy name of account type to be deleted. removedAccountType = control.sConf.atTypeName[accountTypeNum]; // Update account type entries. index = accountTypeNum; while (index < arrayCount(control.sConf.atTypeName) && control.sConf.atTypeName[index] != "") { // Last entry? if (index + 1 == arrayCount(control.sConf.atTypeName)) { // Yes, clear all fields. control.sConf.atTypeName[index] = ""; control.sConf.atRights[index] = ""; control.sConf.atTitle[index] = ""; control.sConf.atPassword[index] = ""; } else { // No, copy from next entry. control.sConf.atTypeName[index] = control.sConf.atTypeName[index + 1]; control.sConf.atRights[index] = control.sConf.atRights[index + 1]; control.sConf.atTitle[index] = control.sConf.atTitle[index + 1]; control.sConf.atPassword[index] = control.sConf.atPassword[index + 1]; } // Continue with next account type. index++; } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); if (userAccountsChanged) { control.signalConfigUpdate(control.sConf.CT_UserAccounts); } // Log action. logAdminAction(control.lng.adminRemoveAccountType, removedAccountType, , , true); } /*************************************************************************************************** * * $DESCRIPTION Moves the specified account type one position in the list. * $PARAM accountTypeNum The index of the account type that is to be moved. * $PARAM bDown Whether the item is to be moved up or down. * **************************************************************************************************/ function moveAccountType(byte accountTypeNum, bool bDown) { local int index; local bool userAccountsChanged; local int oldPosition; local int newPosition; local string temp; local string movedAccountType; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_ServerAdmin) || // - accountTypeNum higher then the size of the list accountTypeNum >= arrayCount(control.sConf.atTypeName) || // - There is no account type stored at the specified index. control.sConf.atTypeName[accountTypeNum] == "" || // - The account type is moved down, but it already is the last used entry. bDown && (control.sConf.atTypeName[accountTypeNum + 1] == "" || accountTypeNum + 1 >= arrayCount(control.sConf.atTypeName)) || // - The account type is moved up, but it is either the first or second entry. !bDown && accountTypeNum < 2) { return; } // Determine positions. oldPosition = accountTypeNum; if (bDown) { newPosition = accountTypeNum + 1; } else { newPosition = accountTypeNum - 1; } // Update user account entries. while (index < arrayCount(control.sConf.paPlayerID) && control.sConf.paPlayerID[index] != "") { // Check if account type number should be changed. if (control.sConf.get_paAccountType(index) == oldPosition) { control.sConf.set_paAccountType(index, newPosition); userAccountsChanged = true; } else if (control.sConf.get_paAccountType(index) == newPosition) { control.sConf.set_paAccountType(index, oldPosition); userAccountsChanged = true; } // Continue with next account. index++; } // Copy name of account type to move. movedAccountType = control.sConf.atTypeName[accountTypeNum]; // Update account type entries. temp = control.sConf.atTypeName[oldPosition]; control.sConf.atTypeName[oldPosition] = control.sConf.atTypeName[newPosition]; control.sConf.atTypeName[newPosition] = temp; temp = control.sConf.atRights[oldPosition]; control.sConf.atRights[oldPosition] = control.sConf.atRights[newPosition]; control.sConf.atRights[newPosition] = temp; temp = control.sConf.atTitle[oldPosition]; control.sConf.atTitle[oldPosition] = control.sConf.atTitle[newPosition]; control.sConf.atTitle[newPosition] = temp; temp = control.sConf.atPassword[oldPosition]; control.sConf.atPassword[oldPosition] = control.sConf.atPassword[newPosition]; control.sConf.atPassword[newPosition] = temp; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_AccountTypes); if (userAccountsChanged) { control.signalConfigUpdate(control.sConf.CT_UserAccounts); } // Log action. logAdminAction(control.lng.adminMoveAccountType, movedAccountType, , , true); } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified account. * $PARAM accountNum The index number of the account to delete. * **************************************************************************************************/ function deleteAccount(byte accountNum) { local int index; local NexgenClient c; local string removedAccount; local string removedAccountTitle; // Preliminary checks. if (!client.hasRight(client.R_AccountManager) || accountNum < 0 || accountNum >= arrayCount(control.sConf.paPlayerID) || control.sConf.paPlayerID[accountNum] == "") { return; } // Kill the players client if he/she is online. c = control.getClientByID(control.sConf.paPlayerID[accountNum]); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Backup account info. removedAccount = control.sConf.paPlayerName[accountNum]; removedAccountTitle = control.sConf.getUserAccountTitle(accountNum); // Update user account entries. for (index = accountNum; index < arrayCount(control.sConf.paPlayerID); index++) { // Last entry? if (index + 1 == arrayCount(control.sConf.paPlayerID)) { // Yes, clear all fields. control.sConf.paPlayerID[index] = ""; control.sConf.paPlayerName[index] = ""; control.sConf.set_paAccountType(index, 0); control.sConf.paCustomRights[index] = ""; control.sConf.paCustomTitle[index] = ""; } else { control.sConf.paPlayerID[index] = control.sConf.paPlayerID[index + 1]; control.sConf.paPlayerName[index] = control.sConf.paPlayerName[index + 1]; control.sConf.set_paAccountType(index, control.sConf.get_paAccountType(index + 1)); control.sConf.paCustomRights[index] = control.sConf.paCustomRights[index + 1]; control.sConf.paCustomTitle[index] = control.sConf.paCustomTitle[index + 1]; } } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); // Log action. logAdminAction(control.lng.adminRemoveAccount, removedAccount, removedAccountTitle, , true); } /*************************************************************************************************** * * $DESCRIPTION Updates the specified account. * $PARAM accountNum The index number of the account to update. * $PARAM accountName Name of the account user. * $PARAM accountType The type of account assigned to the user. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle Custom account title. * **************************************************************************************************/ function updateAccount(byte accountNum, string accountName, int accountType, string customRights, string customTitle) { local NexgenClient c; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_AccountManager) || // - An invalid account number is specified. accountNum < 0 || accountNum >= arrayCount(control.sConf.paPlayerID) || // - The account doesn't exist. control.sConf.paPlayerID[accountNum] == "" || // - No account name was specified. class'NexgenUtil'.static.trim(accountName) == "" || // - An invalid account type number is specified. accountType < -1 || accountType >= arrayCount(control.sConf.atTypeName) || // - The account type isn't used. accountType != -1 && control.sConf.atTypeName[accountType] == "" || // - A custom account is used, but no custom title was set. accountType == -1 && class'NexgenUtil'.static.trim(customTitle) == "") { return; } // Kill the players client if he/she is online. c = control.getClientByID(control.sConf.paPlayerID[accountNum]); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Update account. control.sConf.paPlayerName[accountNum] = class'NexgenUtil'.static.trim(accountName); control.sConf.set_paAccountType(accountNum, accountType); if (accountType < 0) { control.sConf.paCustomRights[accountNum] = customRights; control.sConf.paCustomTitle[accountNum] = customTitle; } else { control.sConf.paCustomRights[accountNum] = ""; control.sConf.paCustomTitle[accountNum] = ""; } // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); // Log action. logAdminAction(control.lng.adminUpdateAccount, accountName, , , true); } /*************************************************************************************************** * * $DESCRIPTION Creates a new user account. * $PARAM accountNum The index number of the account to update. * $PARAM accountName Name of the account user. * $PARAM accountType The type of account assigned to the user. * $PARAM customRights Rights string for a custom account type. * $PARAM customTitle Custom account title. * **************************************************************************************************/ function addAccount(string clientID, string accountName, int accountType, string customRights, string customTitle) { local NexgenClient c; local int index; local bool bFound; local bool bAlreadyHasAccount; local string accountTitle; // Preliminary checks. Fails if: // - The client doesn't have the rights to manage to accounts. if (!client.hasRight(client.R_AccountManager) || // - No account name was specified. class'NexgenUtil'.static.trim(accountName) == "" || // - An invalid account type number is specified. accountType < -1 || accountType >= arrayCount(control.sConf.atTypeName) || // - The account type isn't used. accountType != -1 && control.sConf.atTypeName[accountType] == "" || // - A custom account is used, but no custom title was set. accountType == -1 && class'NexgenUtil'.static.trim(customTitle) == "") { return; } // Get free user account slot. while (!bFound && !bAlreadyHasAccount && index < arrayCount(control.sConf.paPlayerID)) { if (control.sConf.paPlayerID[index] == "") { bFound = true; } else if (control.sConf.paPlayerID[index] ~= clientID) { bAlreadyHasAccount = true; } else { index++; } } // Cancel on error. if (!bFound || bAlreadyHasAccount) { return; } // Kill the players client if he/she is online. c = control.getClientByID(clientID); if (c != none) { c.showPopup("NexgenAccountUpdatedDialog"); c.player.destroy(); } // Add account. control.sConf.paPlayerID[index] = clientID; control.sConf.paPlayerName[index] = accountName; control.sConf.set_paAccountType(index, accountType); if (accountType < 0) { control.sConf.paCustomRights[index] = customRights; control.sConf.paCustomTitle[index] = customTitle; } accountTitle = control.sConf.getUserAccountTitle(index); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); // Log action. logAdminAction(control.lng.adminAddAccount, accountName, accountTitle, , true); } /*************************************************************************************************** * * $DESCRIPTION Pauses the game. If the game is already paused when this function is called the * game will be resumed. * **************************************************************************************************/ function pauseGame() { local bool bGameIsPaused; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || control.gInf.gameState != control.gInf.GS_Playing) { return; } // Check if game is currently paused. bGameIsPaused = level.pauser != ""; // Pause / resume game. if (bGameIsPaused) { level.pauser = ""; } else { level.pauser = client.playerName; } bGameIsPaused = !bGameIsPaused; // Announce event. if (bGameIsPaused) { logAdminAction(control.lng.adminPauseGameMsg); } else { logAdminAction(control.lng.adminResumeGameMsg); } } /*************************************************************************************************** * * $DESCRIPTION Ends the current game. * **************************************************************************************************/ function endGame() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || control.gInf.gameState != control.gInf.GS_Playing) { return; } // End the game. level.pauser = ""; // Make sure the game doesn't remain paused. control.forceEndGame(); // Announce event. logAdminAction(control.lng.adminStopGameMsg); } /*************************************************************************************************** * * $DESCRIPTION Restarts the current game. * **************************************************************************************************/ function restartGame() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Restart. level.serverTravel(restartURL, false); // Announce event. logAdminAction(control.lng.adminRestartGameMsg); } /*************************************************************************************************** * * $DESCRIPTION Changes the team of the specified player. * $PARAM playerNum The player that is to be switched to another team. * $PARAM newTeam The number of the team where to player should be switched to. * **************************************************************************************************/ function setPlayerTeam(int playerNum, int newTeam) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. Fails if: // - The player doesn't have match admin rights. if (!client.hasRight(client.R_MatchAdmin) || // - An invalid player number was specified. target == none || // - The game isn't a team game. !level.game.gameReplicationInfo.bTeamGame || // - An invalid team number was specified. newTeam < 0 || newTeam >= 4 || // - A non existing team was specified. level.game.isA('TeamGamePlus') && newTeam >= TeamGamePlus(level.game).maxTeams || // - The target player is already on the specified team. target.team == newTeam) { return; } // Switch player. target.setTeam(newTeam); // Announce event. logAdminAction(control.lng.adminTeamSwitchMsg, target.playerName, control.lng.getTeamName(newTeam)); } /*************************************************************************************************** * * $DESCRIPTION Enables or disables team switching for the specified player. * $PARAM playerNum The player code of the player whose team switch right is to be changed. * **************************************************************************************************/ function toggleTeamSwitch(int playerNum) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none) { return; } // Toggle team switch on or off. target.bNoTeamSwitch = !target.bNoTeamSwitch; // Announce event. if (target.bNoTeamSwitch) { logAdminAction(control.lng.adminPlayerTeamSwitchDisableMsg, target.playerName); } else { logAdminAction(control.lng.adminPlayerTeamSwitchEnableMsg, target.playerName); } } /*************************************************************************************************** * * $DESCRIPTION Reconnects the specified player. * $PARAM playerNum The player code of the player that is to be reconnected. * $PARAM bSpec Whether the player should be reconnected as spectator. * **************************************************************************************************/ function reconnectPlayer(int playerNum, bool bSpec) { local NexgenClient target; // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none || target.bSpectator == bSpec) { return; } // Reconnect player. if (bSpec) { target.reconnect(target.RCN_ReconnectAsSpec); } else { control.giveJoinOverrideCode(target); target.reconnect(target.RCN_ReconnectAsPlayer); } // Announce event. if (bSpec) { logAdminAction(control.lng.adminReconnectAsSpecMsg, target.playerName); } else { logAdminAction(control.lng.adminReconnectAsPlayerMsg, target.playerName); } } /*************************************************************************************************** * * $DESCRIPTION Sends the specified player to the target URL. * $PARAM playerNum The player which is to be send to the specified URL. * $PARAM url The target url string. * **************************************************************************************************/ function sendPlayerToURL(int playerNum, string url) { local NexgenClient target; local string fURL; // Format parameter. fURL = class'NexgenUtil'.static.trim(url); // Get target client handler. target = control.getClientByNum(playerNum); // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || target == none || fURL == "") { return; } // Send to url. target.clientCommand(openURLCommand @ fURL); // Announce event. logAdminAction(control.lng.adminSendToURLMsg, target.playerName, fURL); } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalTeamSwitch() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bNoTeamSwitch = !control.gInf.bNoTeamSwitch; // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); if (control.gInf.bNoTeamSwitch) { logAdminAction(control.lng.adminDisableTeamSwitchMsg); } else { logAdminAction(control.lng.adminEnableTeamSwitchMsg); } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team balancing for all players in the current game. * **************************************************************************************************/ function toggleGlobalTeamBalance() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bNoTeamBalance = !control.gInf.bNoTeamBalance; // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); if (control.gInf.bNoTeamBalance) { logAdminAction(control.lng.adminDisableTeamBalanceMsg); } else { logAdminAction(control.lng.adminEnableTeamBalanceMsg); } } /*************************************************************************************************** * * $DESCRIPTION Locks / unlocks the teams for the current game. * **************************************************************************************************/ function toggleLockedTeams() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bTeamsLocked = !control.gInf.bTeamsLocked; // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); if (control.gInf.bTeamsLocked) { logAdminAction(control.lng.adminLockTeamsMsg); } else { logAdminAction(control.lng.adminUnlockTeamsMsg); } } /*************************************************************************************************** * * $DESCRIPTION Attempts to give the player an account. The account given to the player depends on * the password that was specified. It can either be the server admin password * (Engine.GameInfo.AdminPassword) or one of the nexgen account type passwords. * $PARAM password The administrator password. * **************************************************************************************************/ function adminLogin(string password) { local bool bValidPassword; local bool bRootAdmin; local int index; local byte accountType; // Check password. if (password == "") { // You wish it was that easy! } else if (password == control.sConf.decode(control.sConf.globalAdminPassword)) { // Server root admin password. bValidPassword = true; bRootAdmin = true; } else { // Check with account types. while (!bValidPassword && index < arrayCount(control.sConf.atTypeName) && control.sConf.atTypeName[index] != "") { if (control.sConf.atPassword[index] != "" && control.sConf.decode(control.sConf.atPassword[index]) == password) { // Account type password match. bValidPassword = true; accountType = index; } else { // Passwords do not match, continue with next account type. index++; } } } // Valid password entered? if (bValidPassword) { // Yes, add / update user account. if (bRootAdmin) { index = control.sConf.addUserAccount(client.playerID, client.playerName, -1, control.sConf.getAllRights(true), control.lng.rootAdminTitle); } else { index = control.sConf.addUserAccount(client.playerID, client.playerName, accountType); } // Account successfully created / updated? if (index >= 0) { // Yes. // Announce event. control.broadcastMsg(control.lng.adminLoginMsg, client.playerName, control.sConf.getUserAccountTitle(index), , , client.player.playerReplicationInfo); // Disconnect client. client.showPopup("NexgenAccountUpdatedDialog"); client.player.destroy(); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_UserAccounts); } } else { // No, notify client. client.showMsg(control.lng.invalidPasswordMsg); } } /*************************************************************************************************** * * $DESCRIPTION Deletes the specified entry from the banlist. * $PARAM entryNum Entry number of the ban to delete. * **************************************************************************************************/ function deleteBan(byte entryNum) { local string deletedBan; local int index; // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || entryNum >= arrayCount(control.sConf.bannedName) || control.sConf.bannedName[entryNum] == "") { return; } // Remove ban. deletedBan = control.sConf.bannedName[entryNum]; control.sConf.removeBan(entryNum); // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Log action. logAdminAction(control.lng.adminDeleteBanMsg, deletedBan, , , true); } /*************************************************************************************************** * * $DESCRIPTION Adds specified ban to the banlist. * $PARAM playerName Name of the player that is banned. * $PARAM ipList List of banned ip addresses. * $PARAM idList List of banned client id's. * $PARAM banReason The reason why this player was banned. * $PARAM banPeriod Describes the period for which the player is banned. * **************************************************************************************************/ function addBan(string playerName, string ipList, string idList, string banReason, string banPeriod) { local byte entryNum; local bool bFound; // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || class'NexgenUtil'.static.trim(playerName) == "") { return; } // Find a free slot. while (!bFound && entryNum < arrayCount(control.sConf.bannedName)) { if (control.sConf.bannedName[entryNum] == "") { bFound = true; } else { entryNum++; } } // Cancel on error. if (!bFound) { return; } // Store ban. control.sConf.bannedName[entryNum] = playerName; control.sConf.bannedIPs[entryNum] = ipList; control.sConf.bannedIDs[entryNum] = idList; control.sConf.banReason[entryNum] = banReason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Log action. logAdminAction(control.lng.adminAddBanMsg, playerName, , , true); } /*************************************************************************************************** * * $DESCRIPTION Updates the specified ban entry. * $PARAM entryNum The entry in the banlist that is to be updated. * $PARAM playerName Name of the player that is banned. * $PARAM ipList List of banned ip addresses. * $PARAM idList List of banned client id's. * $PARAM banReason The reason why this player was banned. * $PARAM banPeriod Describes the period for which the player is banned. * **************************************************************************************************/ function updateBan(byte entryNum, string playerName, string ipList, string idList, string banReason, string banPeriod) { // Preliminary checks. if (!client.hasRight(client.R_BanOperator) || class'NexgenUtil'.static.trim(playerName) == "" || entryNum >= arrayCount(control.sConf.bannedName) || control.sConf.bannedName[entryNum] == "") { return; } // Store ban. control.sConf.bannedName[entryNum] = playerName; control.sConf.bannedIPs[entryNum] = ipList; control.sConf.bannedIDs[entryNum] = idList; control.sConf.banReason[entryNum] = banReason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); // Log action. logAdminAction(control.lng.adminUpdateBanMsg, playerName, , , true); } /*************************************************************************************************** * * $DESCRIPTION Updates the boot control settings. * $PARAM bEnable Whether nexgen boot control should be used. * $PARAM bLoadLastMap Indicates if the game before the crash/reboot is to be loaded. * $PARAM gameType The class of the game type used to boot the server with. * $PARAM mutators List of mutator classes that are loaded when the server gets booted. * $PARAM mapPrefix Map prefix of the levels to load. * $PARAM extraOptions Extra command line options for the nexgen server boot. * $PARAM bootCommands Console commands to execute before the server is booted. * **************************************************************************************************/ function updateBootControl(bool bEnable, bool bLoadLastMap, int gameType, string mutators, string mapPrefix, string extraOptions, string bootCommands) { local string gameClass; local string mutatorClass; local string remaining; local string indexStr; local string temp; local int index; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Store new settings. control.sConf.enableBootControl = bEnable; control.sConf.restartOnLastGame = bLoadLastMap; if (0 <= gameType && gameType < arrayCount(client.sConf.gameTypeInfo)) { class'NexgenUtil'.static.split(client.sConf.gameTypeInfo[gameType], gameClass, remaining); control.sConf.bootGameType = gameClass; } else { control.sConf.bootGameType = ""; } control.sConf.bootMapPrefix = mapPrefix; control.sConf.bootMutatorIndices = mutators; control.sConf.bootMutators = ""; remaining = mutators; while (remaining != "") { class'NexgenUtil'.static.split(remaining, indexStr, remaining); index = int(indexStr); if (0 <= index && index < arrayCount(client.sConf.mutatorInfo)) { class'NexgenUtil'.static.split(client.sConf.mutatorInfo[index], mutatorClass, temp); if (control.sConf.bootMutators == "") { control.sConf.bootMutators = mutatorClass; } else { control.sConf.bootMutators = control.sConf.bootMutators $ separator $ mutatorClass; } } } control.sConf.bootOptions = extraOptions; control.sConf.bootCommands = bootCommands; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BootControl); client.showMsg(control.lng.settingsSavedMsg); // Log action. logAdminAction(control.lng.adminUpdateBootControl, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Separates players by the specified tags. * $PARAM teamTags The tags used for each team. * **************************************************************************************************/ function separatePlayers(string teamTags[4]) { local NexgenClient c; local int index; local bool bFound; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin) || !level.game.isA('TeamGamePlus')) { return; } // Switch players. for (c = control.clientList; c != none; c = c.nextClient) { if (!c.bSpectator) { index = 0; bFound = false; while (!bFound && index < arrayCount(control.sConf.tagsToSeparate) && index < TeamGamePlus(level.game).maxTeams) { // Check if player has a tag that separates him/her. if (instr(caps(c.playerName), caps(control.sConf.tagsToSeparate[index])) >= 0) { bFound = true; if (c.player.playerReplicationInfo.team != index) { c.setTeam(index); } } else { index++; } } } } // Log action. logAdminAction(control.lng.adminSeparateByTag, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Sends the match password to the specified player. * $PARAM targetPlayer Player code of the player that is to receive the password. In case * -1 is passed the password will be send to all players. * $PARAM password The match password that is to be send. * **************************************************************************************************/ function sendPassword(int targetPlayer, optional string password) { local string pwToSend; local NexgenClient target; local NexgenClientCore targetCtrl; local string pwReceivedMsg; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Get password. if (password == "") { pwToSend = control.sConf.decode(control.sConf.serverPassword); } else { pwToSend = password; } // Send password. if (targetPlayer >= 0) { target = control.getClientByNum(targetPlayer); targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePassword(pwToSend); } else { for (target = control.clientList; target != none; target = target.nextClient) { targetCtrl = NexgenClientCore(target.getController(ctrlID)); targetCtrl.receivePassword(pwToSend); } } // Send feedback to client. client.showMsg(control.lng.passwordSendMsg); } /*************************************************************************************************** * * $DESCRIPTION Send the specified match password to the client. * $PARAM password The password to send. * **************************************************************************************************/ simulated function receivePassword(string password) { client.sc.visitServer(client.serverID); client.sc.set(client.serverID, client.SSTR_ServerPassword, password); client.sc.saveConfig(); client.showMsg(client.lng.format(client.lng.receivedPWMsg, password)); } /*************************************************************************************************** * * $DESCRIPTION Updates the match settings * $PARAM matchesToPlay Number of matches to play. * $PARAM currentMatch Current match number. * $PARAM serverPassword The password needed to enter the match. * $PARAM spectatorsNeedPassword Whether spectators need to enter the password. * $PARAM muteSpectatorsDuringMatch Are spectators allowed to chat during the match? * $PARAM enableMatchBootControl Use Nexgen boot control during the match. * $PARAM matchAutoLockTeams Whether the teams are automatically locked when the * match begins. * $PARAM matchAutoPause Automatically pause the game when a player leaves? * $PARAM matchAutoSeparate Automatically separate players in teams? * $PARAM teamTags The tags used to separate players in teams. * **************************************************************************************************/ function updateMatchSettings(int matchesToPlay, int currentMatch, string serverPassword, bool spectatorsNeedPassword, bool muteSpectatorsDuringMatch, bool enableMatchBootControl, bool matchAutoLockTeams, bool matchAutoPause, bool matchAutoSeparate, string teamTag0, string teamTag1, string teamTag2, string teamTag3) { local int index; // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Validate parameters. matchesToPlay = max(1, matchesToPlay); currentMatch = clamp(currentMatch, 1, matchesToPlay); // Save settings. control.sConf.matchesToPlay = matchesToPlay; control.sConf.currentMatch = currentMatch; control.sConf.serverPassword = control.sConf.encode(serverPassword); control.sConf.spectatorsNeedPassword = spectatorsNeedPassword; control.sConf.muteSpectatorsDuringMatch = muteSpectatorsDuringMatch; control.sConf.enableMatchBootControl = enableMatchBootControl; control.sConf.matchAutoLockTeams = matchAutoLockTeams; control.sConf.matchAutoPause = matchAutoPause; control.sConf.matchAutoSeparate = matchAutoSeparate; /* lame... looks like another replication issue for (index = 0; index < arrayCount(teamTags); index++) { control.sConf.tagsToSeparate[index] = teamTags[index]; } */ control.sConf.tagsToSeparate[0] = teamTag0; control.sConf.tagsToSeparate[1] = teamTag1; control.sConf.tagsToSeparate[2] = teamTag2; control.sConf.tagsToSeparate[3] = teamTag3; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_MatchSettings); client.showMsg(control.lng.settingsSavedMsg); // Log action. logAdminAction(control.lng.adminUpdateMatchSettings, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Turns the match mode on or off. * **************************************************************************************************/ function toggleMatchMode() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Save settings. control.sConf.matchModeActivated = !control.sConf.matchModeActivated; control.sConf.saveConfig(); // (Un)lock teams? if (control.sConf.matchAutoLockTeams && control.gInf.gameState == control.gInf.GS_Playing && control.gInf.bTeamsLocked != control.sConf.matchModeActivated) { control.gInf.bTeamsLocked = control.sConf.matchModeActivated; control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); } // Notify clients. control.signalConfigUpdate(control.sConf.CT_MatchSettings); if (control.sConf.matchModeActivated) { logAdminAction(control.lng.adminEnableMatchModeMsg); } else { logAdminAction(control.lng.adminDisableMatchModeMsg); } } /*************************************************************************************************** * * $DESCRIPTION Mutes / unmutes the specified player. * $PARAM playerNum The player code of the player that is to be (un)muted. * **************************************************************************************************/ function togglePlayerMute(int playerNum) { local NexgenClient target; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Mute target player. target.bMuted = !target.bMuted; // Announce event. if (target.bMuted) { logAdminAction(control.lng.adminMutePlayerMsg, target.playerName); } else { logAdminAction(control.lng.adminUnmutePlayerMsg, target.playerName); } } /*************************************************************************************************** * * $DESCRIPTION Changes the name of the specified player. * $PARAM playerNum The player code of the player whose name is to be changed. * $PARAM newName New name for the player. * **************************************************************************************************/ function setPlayerName(int playerNum, string newName) { local NexgenClient target; local string oldName; newName = class'NexgenUtil'.static.trim(newName); // Preliminary checks. if (!client.hasRight(client.R_Moderate) || newName == "") { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Change name. oldName = target.playerName; target.changeName(newName); // Announce event. logAdminAction(control.lng.adminSetNameMsg, oldName, newName); } /*************************************************************************************************** * * $DESCRIPTION Kicks the specified player from the server. * $PARAM playerNum The player code of the player the player that is to be kicked. * $PARAM reason Description of why the player was kicked. * **************************************************************************************************/ function kickPlayer(int playerNum, string reason) { local NexgenClient target; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Check if player can kick/ban players that have an account on the server. if (target.bHasAccount && !client.hasRight(client.R_BanAccounts)) { client.showMsg(control.lng.noBanAccountRightMsg); return; } // Kick player. target.showPopup("NexgenJustBannedDialog", reason, "-"); target.player.destroy(); // Announce event. logAdminAction(control.lng.adminKickPlayerMsg, target.playerName); } /*************************************************************************************************** * * $DESCRIPTION Bans the specified player from the server. * $PARAM playerNum The player code of the player the player that is to be banned. * $PARAM banPeriodType The type of period for which the player is banned. 1 means x * matches and 2 means x days, where x is specified by the * banPeriodArgs argument. Any other value means the player is banned * forever. * $PARAM banPeriodArgs Optional argument for the ban period type. * $PARAM reason Description of why the player was banned. * **************************************************************************************************/ function banPlayer(int playerNum, byte banPeriodType, int banPeriodArgs, string reason) { local NexgenClient target; local string banPeriod; local string banPeriodDesc; local int year, month, day, hour, minute; local int entryNum; local bool bFound; local bool bHasExistingBanEntry; // Preliminary checks. if (!client.hasRight(client.R_Moderate) || !client.hasRight(client.R_BanOperator)) { return; } // Get target client. target = control.getClientByNum(playerNum); if (target == none) return; // Check if player can kick/ban players that have an account on the server. if (target.bHasAccount && !client.hasRight(client.R_BanAccounts)) { client.showMsg(control.lng.noBanAccountRightMsg); return; } // Get ban period. if (banPeriodType == control.sConf.BP_Matches) { banPeriod = "M" $ max(1, banPeriodArgs); } else if (banPeriodType == control.sConf.BP_UntilDate) { year = level.year; month = level.month; day = level.day; hour = level.hour; minute = level.minute; class'NexgenUtil'.static.computeDate(max(1, banPeriodArgs), year, month, day); banPeriod = "U" $ class'NexgenUtil'.static.serializeDate(year, month, day, hour, minute); } banPeriodDesc = control.lng.getBanPeriodDescription(banPeriod); // Kick player from the server. target.showPopup("NexgenJustBannedDialog", reason, banPeriodDesc); target.player.destroy(); // Announce event. logAdminAction(control.lng.adminBanPlayerMsg, target.playerName); // Check if player already has an entry in the banlist. entryNum = control.sConf.getBanIndex("", target.ipAddress, target.playerID); if (entryNum >= 0) { bFound = true; bHasExistingBanEntry = true; } else { entryNum = 0; } // Find a free slot in the ban list. while (!bFound && entryNum < arrayCount(control.sConf.bannedName)) { if (control.sConf.bannedName[entryNum] == "") { bFound = true; } else { entryNum++; } } // Cancel on error. if (!bFound) { return; } // Store ban. control.sConf.bannedName[entryNum] = target.playerName; if (bHasExistingBanEntry) { control.sConf.updateBan(entryNum, target.ipAddress, target.playerID); } else { control.sConf.bannedIPs[entryNum] = target.ipAddress; control.sConf.bannedIDs[entryNum] = target.playerID; } control.sConf.banReason[entryNum] = reason; control.sConf.banPeriod[entryNum] = banPeriod; // Save changes. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_BanList); } /*************************************************************************************************** * * $DESCRIPTION Shows a special administrator message. * $PARAM msg The message to display. * **************************************************************************************************/ function showAdminMessage(string msg) { local Pawn p; // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Show message. for (p = level.pawnList; p != none; p = p.nextPawn) { if(p.isA('PlayerPawn')) { PlayerPawn(p).clearProgressMessages(); PlayerPawn(p).setProgressTime(6); PlayerPawn(p).setProgressMessage(msg, 0); } } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalMute() { // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Update setting. control.gInf.bMuteAll = !control.gInf.bMuteAll; // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); if (control.gInf.bMuteAll) { logAdminAction(control.lng.adminMuteAllMsg); } else { logAdminAction(control.lng.adminUnmuteAllMsg); } } /*************************************************************************************************** * * $DESCRIPTION Enables / disables team switching for all players in the current game. * **************************************************************************************************/ function toggleGlobalNameChange() { // Preliminary checks. if (!client.hasRight(client.R_Moderate)) { return; } // Update setting. control.gInf.bNoNameChange = !control.gInf.bNoNameChange; // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GlobalRights); if (control.gInf.bNoNameChange) { logAdminAction(control.lng.adminDisableNameChangeMsg); } else { logAdminAction(control.lng.adminEnableNameChangeMsg); } } /*************************************************************************************************** * * $DESCRIPTION Updates the first part of the extra server settings. * $PARAM autoUpdateBans Automatically update ban entries? * $PARAM removeExpiredBans Automatically remove expired ban entries? * $PARAM broadcastAdminActions Show the actions of administrators to all players? * $PARAM broadcastTeamKillAttempts Whether or not to show team kill messages. * $PARAM useNexgenMessageHUD Enable the Nexgen message HUD on this server? * $PARAM enableNexgenStartControl Allow Nexgen to control the game start. * $PARAM restoreScoreOnTeamSwitch Restore a players score when swichted to another team? * $PARAM allowTeamSwitch Whether team switching is allowed by default. * $PARAM allowTeamBalance Whether team balancing is allowed by default. * $PARAM allowNameChange Whether name changing is allowed by default. * $PARAM autoRegisterServer Automatically register server in Nexgen database? * **************************************************************************************************/ function setServerSettingsExt1(bool autoUpdateBans, bool removeExpiredBans, bool broadcastAdminActions, bool broadcastTeamKillAttempts, bool useNexgenMessageHUD, bool enableNexgenStartControl, bool restoreScoreOnTeamSwitch, bool allowTeamSwitch, bool allowTeamBalance, bool allowNameChange, bool autoRegisterServer) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Save settings. control.sConf.autoUpdateBans = autoUpdateBans; control.sConf.removeExpiredBans = removeExpiredBans; control.sConf.broadcastAdminActions = broadcastAdminActions; control.sConf.broadcastTeamKillAttempts = broadcastTeamKillAttempts; control.sConf.useNexgenMessageHUD = useNexgenMessageHUD; control.sConf.enableNexgenStartControl = enableNexgenStartControl; control.sConf.restoreScoreOnTeamSwitch = restoreScoreOnTeamSwitch; control.sConf.allowTeamSwitch = allowTeamSwitch; control.sConf.allowTeamBalance = allowTeamBalance; control.sConf.allowNameChange = allowNameChange; control.sConf.autoRegisterServer = autoRegisterServer; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExtraServerSettings); } /*************************************************************************************************** * * $DESCRIPTION Updates the second part of the extra server settings. * $PARAM waitTime Time to wait before the game can be started. * $PARAM startTime Game starting countdown time. * $PARAM autoReconnectTime Time to wait before automatically reconnecting. * $PARAM maxIdleTime Maximum time a player can be idle on the server. * $PARAM spawnProtectionTime How long the player remains protected after spawning. * $PARAM teamKillDamageProtectionTime How long the player remains damage protected after a tk. * $PARAM teamKillPushProtectionTime How long the player remains push protected after a tk. * $PARAM autoDisableMatchTime Time to wait before disabling match mode. * **************************************************************************************************/ function setServerSettingsExt2(byte waitTime, byte startTime, byte autoReconnectTime, int maxIdleTime, int maxIdleTimeCP, byte spawnProtectionTime, byte teamKillDamageProtectionTime, byte teamKillPushProtectionTime, byte autoDisableMatchTime) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Check values. waitTime = clamp(waitTime, 0, 60); startTime = clamp(startTime, 0, 30); autoReconnectTime = clamp(autoReconnectTime, 0, 60); maxIdleTime = clamp(maxIdleTime, 0, 9999); maxIdleTimeCP = clamp(maxIdleTimeCP, 0, 9999); spawnProtectionTime = clamp(spawnProtectionTime, 0, 60); teamKillDamageProtectionTime = clamp(teamKillDamageProtectionTime, 0, 30); teamKillPushProtectionTime = clamp(teamKillPushProtectionTime, 0, 60); autoDisableMatchTime = clamp(autoDisableMatchTime, 0, 120); // Save settings. control.sConf.waitTime = waitTime; control.sConf.startTime = startTime; control.sConf.autoReconnectTime = autoReconnectTime; control.sConf.maxIdleTime = maxIdleTime; control.sConf.maxIdleTimeCP = maxIdleTimeCP; control.sConf.spawnProtectionTime = spawnProtectionTime; control.sConf.teamKillDamageProtectionTime = teamKillDamageProtectionTime; control.sConf.teamKillPushProtectionTime = teamKillPushProtectionTime; control.sConf.autoDisableMatchTime = autoDisableMatchTime; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExtraServerSettings); client.showMsg(control.lng.settingsSavedMsg); // Log action. logAdminAction(control.lng.adminUpdateMiscNexgenSettings); } /*************************************************************************************************** * * $DESCRIPTION Reboots the server. * **************************************************************************************************/ function rebootServer() { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Announce event. logAdminAction(control.lng.adminRebootServerMsg); // Reboot the server. control.gInf.rebootCountDown = rebootDelay; //consoleCommand(rebootCommand); } /*************************************************************************************************** * * $DESCRIPTION Deletes the weapon in the spawn protect ignored weapon list at the specified index. * $PARAM index Index of the weapon that is to be deleted. * **************************************************************************************************/ function delIgnoredWeapon(byte index) { local int currIndex; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index < 0 || index >= arrayCount(control.sConf.spawnProtectExcludeWeapons)) { return; } // Delete weapon. for (currIndex = index; currIndex < arrayCount(control.sConf.spawnProtectExcludeWeapons) - 1; currIndex++) { control.sConf.spawnProtectExcludeWeapons[currIndex] = control.sConf.spawnProtectExcludeWeapons[currIndex + 1]; } control.sConf.spawnProtectExcludeWeapons[arrayCount(control.sConf.spawnProtectExcludeWeapons) - 1] = ""; // Save settings. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExclWeaponList); // Log action. logAdminAction(control.lng.adminUpdateIgnoredWeaponList, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Saves the spawn protect ignore settings for the specified weapon. * $PARAM index Position in the list where the settings are to be stored. A * value below zero indicates that a free slot is to be used. * $PARAM weaponClass Class of the weapon type that is to be added / saved. * $PARAM ignorePrimaryFire Whether primary fire is ignored by the spawn protector. * $PARAM ignoreAltFire Whether alternate fire is ignored by the spawn protector. * **************************************************************************************************/ function saveIgnoredWeapon(int index, string weaponClass, bool ignorePrimaryFire, bool ignoreAltFire) { local string weaponConfigStr; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index >= arrayCount(control.sConf.spawnProtectExcludeWeapons) || class'NexgenUtil'.static.trim(weaponClass) == "") { return; } // Find index if weapon is to be added. if (index < 0) { index = 0; while (index < arrayCount(control.sConf.spawnProtectExcludeWeapons) && control.sConf.spawnProtectExcludeWeapons[index] != "") { index++; } // Valid index? if (index >= arrayCount(control.sConf.spawnProtectExcludeWeapons)) { // Nope, cancel action. return; } } // Save settings. weaponConfigStr = class'NexgenUtil'.static.trim(weaponClass) $ separator; if (ignorePrimaryFire) weaponConfigStr = weaponConfigStr $ control.sConf.IW_Fire; if (ignoreAltFire) weaponConfigStr = weaponConfigStr $ control.sConf.IW_AltFire; control.sConf.spawnProtectExcludeWeapons[index] = weaponConfigStr; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_ExclWeaponList); // Log action. logAdminAction(control.lng.adminUpdateIgnoredWeaponList, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Deletes the hud replacement class in the list at the specified index. * $PARAM index Index of the hud replacement class that is to be deleted. * **************************************************************************************************/ function delHUDReplacementClass(byte index) { local int currIndex; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index < 0 || index >= arrayCount(control.sConf.replacementClass)) { return; } // Delete weapon. for (currIndex = index; currIndex < arrayCount(control.sConf.replacementClass) - 1; currIndex++) { control.sConf.replacementClass[currIndex] = control.sConf.replacementClass[currIndex + 1]; } control.sConf.replacementClass[arrayCount(control.sConf.replacementClass) - 1] = ""; // Save settings. control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_HUDReplacementList); // Log action. logAdminAction(control.lng.adminUpdateHUDReplacementList, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Saves the HUD replacement class settings at the specified position in the list. * $PARAM index Position in the list where the settings are to be stored. A * value below zero indicates that a free slot is to be used. * $PARAM originalClass Original HUD class that is to be replaced. * $PARAM replacementClass Replacement HUD class. * **************************************************************************************************/ function saveHUDReplacementClass(int index, string originalClass, string replacementClass) { local string replacementConfigStr; // Preliminary checks. if (!client.hasRight(client.R_ServerAdmin) || index >= arrayCount(control.sConf.replacementClass) || class'NexgenUtil'.static.trim(originalClass) == "" || class'NexgenUtil'.static.trim(replacementClass) == "") { return; } // Find index if weapon is to be added. if (index < 0) { index = 0; while (index < arrayCount(control.sConf.replacementClass) && control.sConf.replacementClass[index] != "") { index++; } // Valid index? if (index >= arrayCount(control.sConf.replacementClass)) { // Nope, cancel action. return; } } // Save settings. control.sConf.replacementClass[index] = class'NexgenUtil'.static.trim(originalClass) $ "=" $ class'NexgenUtil'.static.trim(replacementClass); control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_HUDReplacementList); // Log action. logAdminAction(control.lng.adminUpdateHUDReplacementList, , , , true); } /*************************************************************************************************** * * $DESCRIPTION Enables / disables tournament mode for the current game. * **************************************************************************************************/ function toggleGlobalTournamentMode() { // Preliminary checks. if (!client.hasRight(client.R_MatchAdmin)) { return; } // Update setting. control.gInf.bTournamentMode = !control.gInf.bTournamentMode; DeathMatchPlus(level.game).bTournament = control.gInf.bTournamentMode; consoleCommand("set Botpack.DeathMatchPlus bTournament" @ control.gInf.bTournamentMode); if (control.sConf.enableNexgenStartControl && control.gInf.bTournamentMode && control.gInf.gameState == control.gInf.GS_Ready) { control.clearReadySignals(); control.doTournamentModeReadySignalCheck(); } else if (!control.sConf.enableNexgenStartControl) { DeathMatchPlus(level.game).bNetReady = !control.gInf.bTournamentMode; } // Announce event. control.signalGameInfoUpdate(control.gInf.IT_GameSettings); if (control.gInf.bTournamentMode) { logAdminAction(control.lng.adminEnableTournamentModeMsg); } else { logAdminAction(control.lng.adminDisableTournamentModeMsg); } } /*************************************************************************************************** * * $DESCRIPTION Wrapper function for NexgenController.logAdminAction(). * $PARAM msg Message that describes the action performed by the administrator. * $PARAM str1 Message specific content. * $PARAM str2 Message specific content. * $PARAM str3 Message specific content. * $PARAM bNoBroadcast Whether not to broadcast this administrator action. * **************************************************************************************************/ function logAdminAction(string msg, optional string str1, optional string str2, optional string str3, optional bool bNoBroadcast) { control.logAdminAction(client, msg, client.playerName, str1, str2, str3, client.player.playerReplicationInfo, bNoBroadcast); } /*************************************************************************************************** * * $DESCRIPTION Updates the log and notifies the other clients. * $PARAM logEvents Include events in the log. * $PARAM logSystemMessages Include system messages in the log. * $PARAM logChatMessages Include chat messages in the log. * $PARAM logPrivateMessages Include private messages in the log. * $PARAM logAdminActions Include the actions of administrators in the log. * $PARAM logToConsole Write log messages to the console. * $PARAM logToFile Whether or not to create log files. * $PARAM logPath Path where the log files should be stored. * $PARAM logFileExtension Extension of the log files. * $PARAM logFileNameFormat Log file name format. * $PARAM logFileTimeStampFormat Format of the time stamps in the log file. * **************************************************************************************************/ function setLogSettings(bool logEvents, bool logSystemMessages, bool logChatMessages, bool logPrivateMessages, bool logAdminActions, bool logToConsole, bool logToFile, string logPath, string logFileExtension, string logFileNameFormat, string logFileTimeStampFormat) { // Check rights. if (!client.hasRight(client.R_ServerAdmin)) { return; } // Check values. if (class'NexgenUtil'.static.trim(logFileNameFormat) == "") { logFileNameFormat = control.lng.defaultLogFileNameFormat; } // Save settings. control.sConf.logEvents = logEvents; control.sConf.logSystemMessages = logSystemMessages; control.sConf.logChatMessages = logChatMessages; control.sConf.logPrivateMessages = logPrivateMessages; control.sConf.logAdminActions = logAdminActions; control.sConf.logToConsole = logToConsole; control.sConf.logToFile = logToFile; control.sConf.logPath = logPath; control.sConf.logFileExtension = logFileExtension; control.sConf.logFileNameFormat = logFileNameFormat; control.sConf.logFileTimeStampFormat = logFileTimeStampFormat; control.sConf.saveConfig(); // Notify clients. control.signalConfigUpdate(control.sConf.CT_LogSettings); client.showMsg(control.lng.settingsSavedMsg); // Log action. logAdminAction(control.lng.adminUpdateLogServerSettings); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ EGn"oRu3s Vn"s ps ~s ..unrs   TG "Nexgen"s @C/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClientController * $VERSION 1.06 (9-8-2008 20:49) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Client controller class. Multiple client controller can be hooked onto a * NexgenClient instance in order to have client side control. This class is used to * support plugins for the Nexgen server controller system. * **************************************************************************************************/ class NexgenClientController extends Info abstract; var string ctrlID; // Identifier for this controller. var NexgenClient client; // The client to which this controller is linked. var NexgenController control; // Nexgen controller. var bool bCanModifyHUDStatePanel; // Whether it can modify the client and server state panels // in the Nexgen HUD. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... client; } /*************************************************************************************************** * * $DESCRIPTION Initializes the client controller. * $OVERRIDE * **************************************************************************************************/ simulated event postNetBeginPlay() { // Check if we're the net owner. The client controller appears to be replicated to other players // that view the player owning this controller, which makes sense if you think about it. However // this is not desirable as the client controller is meant for only one client, so kill this // instance if we're not the owner (i.e. we're viewing another player). if (bNetOwner) { super.postNetBeginPlay(); } else { destroy(); } } /*************************************************************************************************** * * $DESCRIPTION Initializes the client controller. This function is automatically called after * the critical variables have been set, such as the client variable. * $PARAM creator The Actor that has added the controller to the client. * **************************************************************************************************/ function initialize(optional Actor creator) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when the NexgenClient has received its initial replication info is has * been initialized. At this point it's safe to use all functions of the client. * **************************************************************************************************/ simulated function clientInitialized() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the setup of the Nexgen remote control panel. * **************************************************************************************************/ simulated function setupControlPanel() { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the server configuration has been updated. * $PARAM configType Type of settings that have been changed. * **************************************************************************************************/ simulated function configChanged(byte configType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies this panel that the extended game info has been updated. * $PARAM infoType Type of information that has been changed. * **************************************************************************************************/ simulated function gameInfoChanged(byte infoType) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ simulated function playerEvent(int playerNum, string eventType, optional string args) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the client state panel on the Nexgen HUD. * $PARAM stateType State type identifier. * $PARAM text Text to display on the state panel. * $PARAM textColor Color of the text to display. * $PARAM icon State icon. The icon is displayed in front of the text. * $PARAM solidIcon Solid version of the icon (masked, no transparency). * $PARAM bBlink Whether the text on the panel should blink. * **************************************************************************************************/ simulated function modifyClientState(out name stateType, out string text, out Color textColor, out Texture icon, out Texture solidIcon, out byte bBlink) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Modifies the client state panel on the Nexgen HUD. * $PARAM stateType State type identifier. * $PARAM text Text to display on the state panel. * $PARAM textColor Color of the text to display. * $PARAM icon State icon. The icon is displayed in front of the text. * $PARAM solidIcon Solid version of the icon (masked, no transparency). * $PARAM bBlink Whether the text on the panel should blink. * **************************************************************************************************/ simulated function modifyServerState(out name stateType, out string text, out Color textColor, out Texture icon, out Texture solidIcon, out byte bBlink) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Called when a general event has occurred in the system. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ simulated function notifyEvent(string type, optional string arguments) { // To implement in subclass. } /*************************************************************************************************** * * $DESCRIPTION Checks whether the client is ready to initialize. * $RETURN True if the client is ready to initialize, false if not. * **************************************************************************************************/ simulated function bool isReadyToInitialize() { return true; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ JGJ,pKxbxJ,w%Zw}\/*?:<>"|xx\/*?:<>"|w&_wx  K,$ "Log file created, logging to: %1"w@G/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenClient * $VERSION 1.54 (9-8-2008 21:34) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Client information class. An instance of this class keeps track of the information * of a specific player. It will also provide a the necessary means to allow * communication between the client and server, via replication. * **************************************************************************************************/ class NexgenClient extends Info; #exec AUDIO IMPORT NAME=timer_tick FILE=Resources\TimerTick.wav // General info. var NexgenClient nextClient; // Next client (linked list). var NexgenController control; // The Nexgen server controller instance. var NexgenConfig sConf; // Server configuration. var NexgenGameInfo gInf; // Extended game info. var GeneralConfig gc; // General client configuration. var ServerConfig sc; // Server specific client configuration. var PlayerPawn player; // The PlayerPawn object for this client. var NexgenLang lng; // Language instance to support localization. var NexgenPlayerData pDat; // Player data actor for this client. var class loginHandler; // Client login handler. var int loginHandlerChecksum; // Login handler class checksum. // Login support. var string serverID; // ID of the server. var bool bNetLogin; // Client is ready to login (waiting for server). var bool bNetWait; // Client is waiting for initial replication. var bool loginComplete; // Login procedure has been completed? var string ipAddress; // IP address of the client. var string playerID; // Player identification code. var int playerNum; // Client number. var bool bHasAccount; // Indicates if this client has an account on the // server. var string loginOptions; // Extra login parameters. const loginTimerFreq = 5.0; // Rate of the main timer when loggin in. const waitTimeOut = 20.0; // Login timeout (in seconds). // Player info. var string playerName; // Name of the player in the current game. var int team; // Current team. var string rights; // Rights owned by the client. var string title; // Title of the client. var string country; // Country based on IP address. var bool bSpectator; // The client is a spectator. var bool bFirePressed; // Player has the fire button pressed. // Dynamic control info. var float badConnectionSince; // Bad connection has been detected at this time. var bool bBadConnectionDetected; // Whether the bad connection alert has been detected. var vector oldLocation; // Last known 'camp/idle' location. var bool bMuted; // Indicates if this client has been muted. var bool bNoTeamSwitch; // Whether team switching for this player is disabled. var float teamSwitchOverrideTime; // Time at which the team switch override flag was set. var float nameChangeOverrideTime; // Time at which the name change override flag was set. var float lastRespawnTime; // Last time at which the player has respawned. var float lastSwitchTime; // Last time the player has switched to another team. var float spawnProtectionTimeX; // Spawn protection time remaining (server only). var byte spawnProtectionTime; // Spawn protection time remaining (replicated). var float tkDmgProtectionTimeX; // Team kill damage protection time remaining (server only). var byte tkDmgProtectionTime; // Team kill damage protection time remaining (replicated). var float tkPushProtectionTimeX; // Team kill push protection time remaining (server only). var byte tkPushProtectionTime; // Team kill push protection time remaining (replicated). var bool bScreenShotTaken; // Whether a screenshot has been taken. var bool bReconnecting; // Client is reconnecting to the server. var HUD originalHUD; // Original HUD used by the player. var HUD newHUD; // The NexgenHUD. var float gameEndTime; // Time at which the game has ended (local). var bool bEncryptionParamsSet; // Set to true when the client has received the // encryption parameters. var float idleTime; // Number of seconds the player has been idle. var float idleTimeCP; // Same but when the control panel is open. var int idleTimeRemaining; // Time remaining before the player will be kicked. var bool bUsingNexgenMessageHUD; // Whether the extended Nexgen message HUD is used. var float scoreBeforeTeamSwitch; // The player's score just before the team switch. var bool bIsReadyToPlay; // Ready signal for this client. // Game speed independent timer support. var float timeSeconds; // Time elpased since the creation of this client. // Controller extensions. var NexgenClientController clientCtrl[16]; // Client controller extensions. // GUI data. var NexgenHUD nscHUD; // Nexgen HUD extension mutator. var WindowConsole consoleWindow; // Shortcut to the console of the player. var NexgenPopupFrame popupWindow; // The popup window. var NexgenMainFrame mainWindow; // The main window. // Config change events. var int nextDynamicUpdateCount[10]; // Last received config update count per config type. var int nextDynamicChecksum[10]; // Checksum to wait for. var byte bWaitingForNewConfig[10]; // Whether the client is waiting for the configuration. // Game info change events var int gameInfoUpdate[2]; // Game info update check per type. // Client side settings. const SSTR_ClientKey = "ClientKey"; // Private client key setting. const SSTR_ClientID = "ClientID"; // Public client ID setting. const SSTR_ServerPassword = "Password"; // Server join password setting. const SSTR_OverrideClass = "OverrideClass"; // Override class setting string. const SSTR_UseNexgenHUD = "UseNexgenHUD"; // Whether or not to use the Nexgen HUD. const SSTR_FlashMessages = "FlashMessages"; // Flash new messages on the Nexgen HUD. const SSTR_ShowPlayerLocation = "ShowPlayerLoc"; // Show player location on teamsay messages. const SSTR_PlayPMSound = "PlayPMSound"; // Play a sound when a PM is received. const SSTR_AutoSSNormalGame = "AutoSSNormalGame"; // Automatically take scrshot at end of normal games. const SSTR_AutoSSMatch = "AutoSSMatch"; // Automatically take scrshot at end of matches. const SSTR_RunCount = "RunCount"; // Number of times Nexgen has been used. // Client rights. const R_MayPlay = "A"; // Allowed to play on the server. const R_VIPAccess = "B"; // Has access to VIP slots. const R_AdminAccess = "C"; // Has access to admin slots. const R_NeedsNoPW = "D"; // Doesn't need a password to enter the server. const R_CanBeIdle = "E"; // Whether the client can be idle on the server. const R_MatchAdmin = "F"; // Controls the current game. const R_Moderate = "G"; // Player can moderate the game. const R_BanOperator = "H"; // Player can ban and unban players. const R_AccountManager = "I"; // Is allowed to change the player accounts. const R_ServerAdmin = "J"; // Can edit the global server settings. const R_MatchSet = "K"; // Can setup clan matches. const R_BanAccounts = "L"; // Can kick or ban players with an account. const R_HiddenAdmin = "M"; // Whether the admin and his/her actions are // invisible. // Reconnect options. const RCN_ReconnectOnly = 0; // Just reconnect, nothing more. const RCN_ReconnectAsPlayer = 1; // Set OverrideClass to "" and reconnect. const RCN_ReconnectAsSpec = 2; // Set OverrideClass to 'spec' and reconnect. // General constants. const reconnectCommand = "Reconnect"; // Console command for reconnecting. const disconnectCommand = "Disconnect"; // Console command for disconnecting. const exitCommand = "Exit"; // Console command for quitting UT. const startCommand = "Mutate NSC START"; // Console command for starting the game. const spectatorClass = "Botpack.CHSpectator"; // Override class to use for spectators. const separator = ","; // Character used to seperate elements in a list. // Player events. const PE_PlayerJoined = "pj"; // A new player has joined the server. const PE_PlayerLeft = "pl"; // Somebody left the server. const PE_AttributeChanged = "ac"; // An attribute of the player has changed. // Player attributes. const PA_ClientID = "id"; // The client identification code. const PA_IPAddress = "ip"; // IP address of the client. const PA_Name = "name"; // Nickname of the player const PA_Title = "title"; // The players title/role on the server. const PA_Team = "team"; // Team to which the player belongs. const PA_Country = "country"; // Country where the player is from. // Settings. const maxIdleRadius = 192; // Radius around oldLocation that counts as idle. const idleCountDelay = 4; // Delay before player is marked as idle. const idleTimeWarning = 15; // Idle time remaining when alerting the player. const maxOverrideTime = 0.50; // Max elapsed time since override flag was set (in sec). const cancelSpawnProtectDelay = 1.25; // Delay in seconds before checking the spawn // protection cancelling conditions. const normalModeTimerFreq = 4.0; // Frequency of timer when the client is fully // initialized (in Hz). const autoScreenShotDelay = 2.0; // Seconds to wait before automatically taking a // a screenshot at the end of the game. const autoSSMinGameTime = 30.0; // Number of seconds the player has to be at least // in the game if a screenshot is to be taken. // This prevents players entering an ended game to // take a screenshot. const welcomeMsgCount = 10; // How many times should the welcome message be displayed? const maxClientCtrlInitWaitTime = 10; // How long should the client wait for client // controllers to become ready before // initializing. /*************************************************************************************************** * * $DESCRIPTION Replication block. * **************************************************************************************************/ replication { reliable if (role == ROLE_Authority) // Replicate to client... // Variables. clientCtrl, serverID, loginHandler, loginHandlerChecksum, sConf, gInf, loginComplete, ipAddress, playerName, rights, title, bSpectator, playerNum, bHasAccount, country, bMuted, spawnProtectionTime, tkDmgProtectionTime, tkPushProtectionTime, // Functions. showPopup, showPanel, showMsg, reconnect, clientCommand, configChanged, gameInfoChanged, playerEvent, updateLoginOption, setEncryptionParams, notifyEvent; reliable if (role < ROLE_Authority) // Replicate to server... // Variables. // Functions. login, clientInitialized; } /*************************************************************************************************** * * $DESCRIPTION Initializes the client handler. If called it will initiate the login procedure. * $OVERRIDE * **************************************************************************************************/ simulated function preBeginPlay() { // Execute server side actions. if (role == ROLE_Authority) { // Set player. player = PlayerPawn(owner); // Start timer (set for the login procedure). setTimer(1.0 / loginTimerFreq, true); } // Execute client side actions. if (role == ROLE_SimulatedProxy) { // The following code was move from postNetBeginPlay() to this function, because these // variables have to be set from the moment the actor is spawned. The function // postNetBeginPlay() may be called a few ticks later. This caused the NexgenX plugin to // think that the client was already initialized and produced accessed none warnings. // Start waiting for replication. bNetLogin = true; bNetWait = true; idleTimeRemaining = -1; } } /*************************************************************************************************** * * $DESCRIPTION Initializes the client controller. * $OVERRIDE * **************************************************************************************************/ simulated event postNetBeginPlay() { if (bNetOwner) { super.postNetBeginPlay(); // Load client configuration. gc = spawn(class'GeneralConfig'); sc = spawn(class'ServerConfig'); lng = spawn(class'NexgenLang'); // Start timer (set for the login procedure). setTimer(1.0 / loginTimerFreq, true); } else { // See remarks in NexgenClientController.postNetBeginPlay(). destroy(); } } /*************************************************************************************************** * * $DESCRIPTION Main loop for the client handler. Handles the following: * - Login triggering (waiting for replication). * - Server config update checks. * $OVERRIDE * **************************************************************************************************/ simulated function timer() { local int index; local bool bClientControllersReady; // Client side code. if (role == ROLE_SimulatedProxy) { // Received login info? if (bNetLogin && bNetOwner && serverID != "" && owner != none && sConf != none && PlayerPawn(owner).playerReplicationInfo != none && loginHandler != none && loginHandlerChecksum == class'NexgenUtil'.static.stringHash(string(loginHandler))) { bNetLogin = false; clientLogin(); } // Check for login timeout. if (bNetLogin && timeSeconds >= waitTimeOut) { setTimer(0.0, false); destroy(); // This object is not relevant for the client. } // Client has received initial replication info? if (!bNetLogin && bNetWait && bNetOwner && initialConfigReplicationComplete() && gInf != none && gInf.updateCount > 0 && player != none && rights != "" && bEncryptionParamsSet) { // Check if client controllers are ready to initialize. bClientControllersReady = true; if (timeSeconds < maxClientCtrlInitWaitTime) { index = 0; while (bClientControllersReady && index < arrayCount(clientCtrl) && clientCtrl[index] != none) { bClientControllersReady = clientCtrl[index].isReadyToInitialize(); index++; } } // Initialize client and client controllers if all are ready. if (bClientControllersReady) { bNetWait = false; setTimer(0.0, false); clientInitialize(); } } // Server config update check. if (loginComplete) { checkConfigUpdate(); } // Check if game has ended. if (!bNetWait && gameEndTime <= 0 && gInf.gameState == gInf.GS_Ended) { gameEndTime = timeSeconds; } // Automatically take a screenshot. if (gameEndTime > 0 && timeSeconds - gameEndTime >= autoScreenShotDelay && !bScreenShotTaken && player.bShowScores) { if (timeSeconds > autoSSMinGameTime && (gc.get(SSTR_AutoSSNormalGame, "false") ~= "true" || gc.get(SSTR_AutoSSMatch, "true") ~= "true" && sConf.matchModeActivated)) { player.sShot(); } bScreenShotTaken = true; } } // Server side code. if (role == ROLE_Authority) { // Check if player has left the server before able to login. if (!loginComplete && player == none) { setTimer(0.0, false); destroy(); } // Check for timeout. if (!loginComplete && timeSeconds >= waitTimeOut) { setTimer(0.0, false); control.nscLog("Login timeout for" @ player.playerReplicationInfo.playerName); control.disconnectClient(self); } } } /*************************************************************************************************** * * $DESCRIPTION Time critical event detection loop. Detects the following events: * - If the fire button is pressed. * $OVERRIDE * **************************************************************************************************/ simulated function tick(float deltaTime) { local int lastIdleTimeRemaining; // Gamespeed independent timer support. if (level.pauser == "") { timeSeconds += deltaTime / level.timeDilation; } // Client side actions. if (role == ROLE_SimulatedProxy && player != none && !bNetWait) { // Check fire button state. if (player.bFire != 0 && !bFirePressed) { // Fire button press. bFirePressed = true; firePressed(); } else if (player.bFire == 0 && bFirePressed) { // Fire button release. bFirePressed = false; } // Check for bad connection. if (player.bBadConnectionAlert && !bBadConnectionDetected) { badConnectionSince = timeSeconds; bBadConnectionDetected = true; } else if (bBadConnectionDetected && !player.bBadConnectionAlert) { bBadConnectionDetected = false; } else if (bBadConnectionDetected && sConf.autoReconnectTime > 0 && !bReconnecting && timeSeconds - badConnectionSince > sConf.autoReconnectTime) { bReconnecting = true; reconnect(); } // Idle detection. if (gInf.gameState == gInf.GS_Playing) { if (vSize((oldLocation - player.location) * vect(1, 1, 0)) > maxIdleRadius) { oldLocation = player.location; idleTime = -idleCountDelay; idleTimeCP = -idleCountDelay; idleTimeRemaining = -1; } else if (!bSpectator && !hasRight(R_CanBeIdle) && level.pauser == "") { lastIdleTimeRemaining = idleTimeRemaining; // Control panel opened? if (mainWindow.bWindowVisible) { // Yes, increase open control panel idle time. idleTimeCP += deltaTime / level.timeDilation; if (idleTimeCP < 0 || sConf.maxIdleTimeCP == 0) { idleTimeRemaining = -1; } else { idleTimeRemaining = max(0, 1 + sConf.maxIdleTimeCP - idleTimeCP); } } else { // No, increase normal idle time. idleTime += deltaTime / level.timeDilation; if (idleTime < 0 || sConf.maxIdleTime == 0) { idleTimeRemaining = -1; } else { idleTimeRemaining = max(0, 1 + sConf.maxIdleTime - idleTime); } } // Play warning sound? if (idleTimeRemaining >= 0 && idleTimeRemaining <= idleTimeWarning && idleTimeRemaining != lastIdleTimeRemaining) { player.clientPlaySound(sound'timer_tick', , true); } // Maximum idle time reached? if (idleTimeRemaining == 0) { showPopup("NexgenIdleKickedDialog"); clientCommand(disconnectCommand); } } } else if (idleTimeRemaining >= 0) { idleTime = -idleCountDelay; idleTimeCP = -idleCountDelay; idleTimeRemaining = -1; } } // Server side actions. if (role == ROLE_Authority) { // Spawn protection. if (spawnProtectionTimeX > 0) { // Disable spawn protection? if ((control.timeSeconds - lastRespawnTime >= cancelSpawnProtectDelay) && (player.playerReplicationInfo.hasFlag != none || !ignoreWeaponFire()) || (player.health <= 0) || (gInf.gameState == gInf.GS_Ended)) { // Yes. spawnProtectionTimeX = 0; spawnProtectionTime = 0; } else { // No, update timer. spawnProtectionTimeX -= deltaTime / level.timeDilation; if (spawnProtectionTimeX <= 0) { spawnProtectionTime = 0; } else { spawnProtectionTime = byte(spawnProtectionTimeX) + 1; } } } // Team kill damage protection. if (tkDmgProtectionTimeX > 0) { tkDmgProtectionTimeX -= deltaTime / level.timeDilation; if (tkDmgProtectionTimeX <= 0) { tkDmgProtectionTime = 0; } else { tkDmgProtectionTime = byte(tkDmgProtectionTimeX) + 1; } } // Team kill push protection. if (tkPushProtectionTimeX > 0) { tkPushProtectionTimeX -= deltaTime / level.timeDilation; if (tkPushProtectionTimeX <= 0) { tkPushProtectionTime = 0; } else { tkPushProtectionTime = byte(tkPushProtectionTimeX) + 1; } } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether primary or alt fire for the current weapon carried by the player * should be ignored when checking if it should cancel spawn protection. * $RETURN True if the weapon should be ignored, false if not. * **************************************************************************************************/ function bool ignoreWeaponFire() { local bool bIgnore; local bool bFound; local bool bIgnoreFire; local bool bIgnoreAltFire; local int index; local string weaponClass; local string currWeaponClass; local string excludedModes; // Trivial case. if (player.weapon == none || player.bFire == 0 && player.bAltFire == 0) { return true; } // Non trivial case, locate weapon class in configuration file. weaponClass = string(player.weapon.class); while(!bFound && index < arrayCount(sConf.spawnProtectExcludeWeapons) && sConf.spawnProtectExcludeWeapons[index] != "") { // Get currently selected weapon in exclude list. class'NexgenUtil'.static.split(sConf.spawnProtectExcludeWeapons[index], currWeaponClass, excludedModes); // Class match? if (weaponClass ~= currWeaponClass) { // Yes. bFound = true; excludedModes = caps(class'NexgenUtil'.static.trim(excludedModes)); bIgnore = (instr(excludedModes, sConf.IW_Fire) >= 0 && player.bFire > 0) || (instr(excludedModes, sConf.IW_AltFire) >= 0 && player.bAltFire > 0); } else { // No, continue searching... index++; } } // Return result. return bIgnore; } /*************************************************************************************************** * * $DESCRIPTION Called when the fire button is pressed. * **************************************************************************************************/ simulated function firePressed() { // Check if the game should be started. if (gInf != none && gInf.gameState == gInf.GS_Ready) { clientCommand(startCommand); } } /*************************************************************************************************** * * $DESCRIPTION Initiates the client side login procedure. Retrieves the necessary client info * and sends it to the server for a login request. * $REQUIRE owner != none && serverID != "" * $ENSURE player != none && playerID != none * **************************************************************************************************/ simulated function clientLogin() { // Set player. player = PlayerPawn(owner); // Retrieve login information. loginHandler.static.getLoginParameters(self, playerID, loginOptions); // Initialize the popup window, client might receive a message if login fails. initializePopupWindow(); // Try to login to the server. login(playerID, loginOptions); // Check if a Nexgen configuration is resident on the client. checkResidentConfig(); } /*************************************************************************************************** * * $DESCRIPTION Requests the login of this client. * $ENSURE playerName != "" && ipAddress != "" && playerID != "" * **************************************************************************************************/ function login(string clientID, string args) { // Stop timer (it is no longer used server side). setTimer(0.0, false); // Store client information. playerName = player.playerReplicationInfo.playerName; ipAddress = player.getPlayerNetworkAddress(); ipAddress = left(ipAddress, instr(ipAddress, ":")); bSpectator = player.isA('Spectator'); playerID = clientID; loginOptions = args; // Set account information. setAccountInfo(); // Let the server controller check the login request. control.checkLogin(self); } /*************************************************************************************************** * * $DESCRIPTION Initializes the clientside of this object. This can only be done once the initial * replication data has been received (detected in the timer function). Calling this * function will setup things like the HUD and user interface objects. * $REQUIRE player != none && sConf != none && gInf != none * $ENSURE nscHUD != none * **************************************************************************************************/ simulated function clientInitialize() { local int index; local int runCount; // Setup GUI. nscHUD = spawn(class'NexgenHUD', self); if (gc.get(SSTR_UseNexgenHUD, "true") ~= "true" && sConf.HUDReplacementClass != none) { setNexgenMessageHUD(true); } initializeControlPanel(); // Set timer frequency. setTimer(1.0 / normalModeTimerFreq, true); // Let the client controllers know we're initialized. for (index = 0; index < arrayCount(clientCtrl); index++) { if (clientCtrl[index] != none) { clientCtrl[index].clientInitialized(); } } // Update run count. runCount = int(gc.get(SSTR_RunCount, "0")) + 1; gc.set(SSTR_RunCount, string(runCount)); gc.saveConfig(); // Show 'welcome to Nexgen' message? if (runCount <= welcomeMsgCount) { showMsg(lng.welcomeMsg); } // Initialisation complete, notify server. clientInitialized(); } /*************************************************************************************************** * * $DESCRIPTION Enables or disables the Nexgen message HUD. * $PARAM bEnable Whether the nexgen message HUD should be enabled. * $REQUIRE sConf.HUDReplacementClass != none * **************************************************************************************************/ simulated function setNexgenMessageHUD(bool bEnable) { bUsingNexgenMessageHUD = false; if (bEnable) { // Store original HUD if not already set. if (originalHUD == none) { originalHUD = player.myHUD; } // Create new HUD if not already done. if (newHUD == none) { newHUD = spawn(sConf.HUDReplacementClass, player); newHUD.HUDMutator = originalHUD.HUDMutator; if (newHUD.isA('NexgenHUDWrapper')) { NexgenHUDWrapper(newHUD).initialize(ChallengeHUD(originalHUD), nscHUD); } } // Enable the new HUD. if (newHUD != none) { bUsingNexgenMessageHUD = true; player.myHUD = newHUD; // Hack to fix the false 'The enemy has your flag, recover it!' messages. if (originalHUD.isA('ChallengeCTFHUD')) { ChallengeCTFHUD(originalHUD).myFlag = none; } } } else if (originalHUD != none) { // Reset HUD to original HUD. player.myHUD = originalHUD; } } /*************************************************************************************************** * * $DESCRIPTION Called when the client has finished its initialisation process. This function is * called on the client in the clientInitialize() function and is replicated to the * server. * **************************************************************************************************/ function clientInitialized() { control.clientInitialized(self); } /*************************************************************************************************** * * $DESCRIPTION Displays the specified message on the screen of this client. * $PARAM msg The message to display. * $PARAM pri Player replication info related to this message. * **************************************************************************************************/ simulated function showMsg(string msg, optional PlayerReplicationInfo pri) { local string cleanMsg; if (role == ROLE_SimulatedProxy) { // Strip color tag. cleanMsg = class'NexgenUtil'.static.removeMessageColorTag(msg); // Write message to console. if (player.player.console != none) { player.player.console.message(pri, cleanMsg, 'Event'); } // Write message to HUD. if (player.myHUD != none) { if (bUsingNexgenMessageHUD) { player.myHUD.message(pri, msg, 'Event'); } else { player.myHUD.message(pri, cleanMsg, 'Event'); } } } } /*************************************************************************************************** * * $DESCRIPTION Retrieves the account information of this client. * $REQUIRE playerID != none * $ENSURE rights != "" && title != none * **************************************************************************************************/ function setAccountInfo() { local int accountNum; // Player has an account? accountNum = sConf.getUserAccountIndex(playerID); if (accountNum < 0) { // No, use default account type. if (bSpectator) { title = control.lng.specTitle; } else { title = sConf.getAccountTypeTitle(0); } rights = sConf.atRights[0]; } else { // Yes, load account info. bHasAccount = true; title = sConf.getUserAccountTitle(accountNum); if (sConf.get_paAccountType(accountNum) < 0) { rights = sConf.paCustomRights[accountNum]; } else { rights = sConf.atRights[sConf.get_paAccountType(accountNum)]; } // Change title for hidden admins. if (hasRight(R_HiddenAdmin)) { if (bSpectator) { title = control.lng.specTitle; } else { title = sConf.getAccountTypeTitle(0); } } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client has the specified right. * $PARAM rightID String identifier of the client right. * $REQUIRE rightID != "" * $RETURN True if the client has the specfied right, false if not. * **************************************************************************************************/ simulated function bool hasRight(string rightID) { return instr(separator $ rights $ separator, separator $ rightID $ separator) >= 0; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client has the specified rights. * $PARAM rightsStr String containing the rights to check. * $RETURN True if the client has the specfied rights, false if not. * **************************************************************************************************/ simulated function bool hasRights(string rightsStr) { local string remaining; local string rightID; local bool bHasRights; remaining = rightsStr; // Check rights. bHasRights = true; while (bHasRights && remaining != "") { class'NexgenUtil'.static.split(remaining, rightID, remaining); bHasRights = hasRight(rightID); } // Return result. return bHasRights; } /*************************************************************************************************** * * $DESCRIPTION Initializes the console window so other windows can be created. * $ENSURE consoleWindow != none * **************************************************************************************************/ simulated function initializeConsoleWindow() { consoleWindow = WindowConsole(player.player.console); if (consoleWindow.bShowConsole) { consoleWindow.hideConsole(); } if (consoleWindow.root == none) { consoleWindow.createRootWindow(none); } } /*************************************************************************************************** * * $DESCRIPTION Creates the popup window. * $ENSURE popupWindow != none * **************************************************************************************************/ simulated function initializePopupWindow() { local float wWidth; local float wHeight; local float wTop; local float wLeft; local UWindowWindow window; // Setup console window. if (consoleWindow == none) { initializeConsoleWindow(); } // Create popup window. wWidth = class'NexgenPopupFrame'.default.windowWidth; wHeight = class'NexgenPopupFrame'.default.windowHeight; wLeft = fMax(0.0, (consoleWindow.root.winWidth - wWidth) / 2.0); wTop = fMax(0.0, (consoleWindow.root.winHeight - wHeight) / 2.0); window = consoleWindow.root.createWindow(class'NexgenPopupFrame', wLeft, wTop, wWidth, wHeight); popupWindow = NexgenPopupFrame(window); popupWindow.client = self; popupWindow.gc = gc; popupWindow.sc = sc; popupWindow.serverID = serverID; //sConf.serverID; popupWindow.close(); } /*************************************************************************************************** * * $DESCRIPTION Creates the remote control panel window. * $ENSURE mainWindow != none * **************************************************************************************************/ simulated function initializeControlPanel() { local float wWidth; local float wHeight; local float wTop; local float wLeft; local UWindowWindow window; local int index; // Setup console window. if (consoleWindow == none) { initializeConsoleWindow(); } // Make sure the control panel of the last game is closed. if (consoleWindow.root != none) { window = consoleWindow.root.firstChildWindow; while (window != none) { // Window is a control panel? if (window.isA('NexgenMainFrame')) { // Yes, close it. window.close(); } // Continue with next window. window = window.nextSiblingWindow; } } // Create main window. wWidth = class'NexgenMainFrame'.default.windowWidth; wHeight = class'NexgenMainFrame'.default.windowHeight; wLeft = fMax(0.0, (consoleWindow.root.winWidth - wWidth) / 2.0); wTop = fMax(0.0, (consoleWindow.root.winHeight - wHeight) / 2.0); window = consoleWindow.root.createWindow(class'NexgenMainFrame', wLeft, wTop, wWidth, wHeight); mainWindow = NexgenMainFrame(window); //mainWindow.close(); // Causes the typing promt to be reset. mainWindow.hideWindow(); mainWindow.mainPanel.client = self; // Let the client controllers add stuff to the control panel. for (index = 0; index < arrayCount(clientCtrl); index++) { if (clientCtrl[index] != none) { clientCtrl[index].setupControlPanel(); } } } /*************************************************************************************************** * * $DESCRIPTION Shows a popup dialog at the client. * $PARAM popupClass Class name of the popup dialog to show. * $PARAM str1 Dialog specific argument. * $PARAM str2 Dialog specific argument. * $PARAM str3 Dialog specific argument. * $PARAM str4 Dialog specific argument. * $REQUIRE windowsInitialized * **************************************************************************************************/ simulated function showPopup(string popupClass, optional string str1, optional string str2, optional string str3, optional string str4) { popupWindow.showPopup(popupClass, str1, str2, str3, str4); popupWindow.focusWindow(); popupWindow.bringToFront(); popupWindow.showWindow(); consoleWindow.bQuickKeyEnable = true; consoleWindow.launchUWindow(); } /*************************************************************************************************** * * $DESCRIPTION Opens the Nexgen control panel (main window). * $PARAM panel The panel that is to be displayed. * **************************************************************************************************/ simulated function showPanel(optional string panel) { if (mainWindow != none) { mainWindow.focusWindow(); mainWindow.bringToFront(); mainWindow.showWindow(); consoleWindow.bQuickKeyEnable = true; consoleWindow.launchUWindow(); if (panel != "") { mainWindow.mainPanel.selectPanel(panel); } } } /*************************************************************************************************** * * $DESCRIPTION Reconnects this client. * $PARAM option Determines the reconnect option. * $REQUIRE option == RCN_ReconnectOnly || * option == RCN_ReconnectAsPlayer || * option == RCN_ReconnectAsSpec * **************************************************************************************************/ simulated function reconnect(optional byte option) { if (option == RCN_ReconnectAsPlayer) { player.updateURL(SSTR_OverrideClass, "", true); } else if (option == RCN_ReconnectAsSpec) { player.updateURL(SSTR_OverrideClass, spectatorClass, true); } player.consoleCommand(reconnectCommand); } /*************************************************************************************************** * * $DESCRIPTION Update a login option for the client. * $PARAM optionName Name of the option that is to be updated. * $PARAM value The new value of the option. * $PARAM bSaveDefault Whether this value should be saved as default for this option. * $REQUIRE optionName != "" * **************************************************************************************************/ simulated function updateLoginOption(string optionName, coerce string value, optional bool bSaveDefault) { player.updateURL(optionName, value, bSaveDefault); } /*************************************************************************************************** * * $DESCRIPTION Executes the given command on the client. * $PARAM cmd Console command to execute. * **************************************************************************************************/ simulated function clientCommand(string cmd) { player.consoleCommand(cmd); } /*************************************************************************************************** * * $DESCRIPTION Creates a new client controller and hooks it onto this client. * $PARAM controllerClass Type of controller to create. * $PARAM creator The Actor that wants to add the controller. * $REQUIRE controllerClass != none * $RETURN The newly created controller or none if all client controller slots are used. * **************************************************************************************************/ function NexgenClientController addController(class controllerClass, optional Actor creator) { local int index; local bool bFound; local NexgenClientController controller; // Locate empty controller slot. while (!bFound && index < arrayCount(clientCtrl)) { if (clientCtrl[index] == none) { bFound = true; } else { index++; } } // Create controller. if (bFound) { controller = spawn(controllerClass, player); controller.client = self; controller.control = control; controller.initialize(creator); clientCtrl[index] = controller; } // Return the new controller. return controller; } /*************************************************************************************************** * * $DESCRIPTION Fetches the client controller with the specified id. * $PARAM controllerID ID of the controller to return. * $RETURN The controller with the specified id, or none if the controller couln't be found. * $ENSURE result != none ? result.ctrlID == controllerID : true * **************************************************************************************************/ simulated function NexgenClientController getController(string controllerID) { local int index; local bool bFound; // Locate controller. while (!bFound && index < arrayCount(clientCtrl)) { if (clientCtrl[index] != none && clientCtrl[index].ctrlID ~= controllerID) { bFound = true; } else { index++; } } // Return controller if found. if (bFound) { return clientCtrl[index]; } else { return none; } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client that the server configuration has been updated. The new * settings might not yet have been replicated, so the client has to wait for the * replication to complete. Once the replication is completed an event will be * triggered to signal the GUI and other client controllers that the new settings * are available, see checkConfigUpdate(). * $PARAM configType Type of settings that have been changed. * $PARAM updateCount Config update number for the new settings. * $PARAM checksum Checksum of the new settings. * $REQUIRE 0 <= configType && configType < arrayCount(nextDynamicChecksum) && updateCount >= 0 * **************************************************************************************************/ simulated function configChanged(byte configType, int updateCount, int checksum) { if (updateCount > nextDynamicUpdateCount[configType]) { nextDynamicUpdateCount[configType] = updateCount; nextDynamicChecksum[configType] = checksum; bWaitingForNewConfig[configType] = byte(true); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client that the extended game information has been updated. The new * settings might not yet have been replicated, so the client has to wait for the * replication to complete. In that case the function will be automatically called * again once the replication has completed, see checkConfigUpdate(). * $PARAM infoType Type of information that has been changed. * $PARAM updateNum Game info update number for the new settings. * $REQUIRE 0 <= configType && configType < arrayCount(gameInfoUpdate) && updateNum >= 0 * **************************************************************************************************/ simulated function gameInfoChanged(byte infoType, int updateNum) { local int index; // Check if replication of the new settings has completed. if (gInf.updateCount >= updateNum) { // It has, notify GUI. mainWindow.mainPanel.gameInfoChanged(infoType); // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].gameInfoChanged(infoType); } } else { // No it hasn't, wait for replication to complete. gameInfoUpdate[infoType] = updateNum; } } /*************************************************************************************************** * * $DESCRIPTION Checks for each config type if new settings have been received. If new settings * have been received configChanged() is called. * **************************************************************************************************/ simulated function checkConfigUpdate() { local byte type; local int index; // Server configuration. for (type = 0; type < arrayCount(nextDynamicChecksum); type++) { if (bool(bWaitingForNewConfig[type]) && sConf.updateCounts[type] >= nextDynamicUpdateCount[type] && sConf.calcDynamicChecksum(type) == nextDynamicChecksum[type]) { bWaitingForNewConfig[type] = byte(false); // Notify GUI. mainWindow.mainPanel.configChanged(type); // Notify client controllers. index = 0; while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].configChanged(type); } } } // Extended game info. for (type = 0; type < arrayCount(gameInfoUpdate); type++) { if (gameInfoUpdate[type] > 0 && gInf.updateCount >= gameInfoUpdate[type]) { gameInfoUpdate[type] = 0; gameInfoChanged(type, gInf.updateCount); } } } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of a player event. Additional arguments to the event should be * combined into one string which then can be send along with the playerEvent call. * $PARAM playerNum Player identification number. * $PARAM eventType Type of event that has occurred. * $PARAM args Optional arguments. * $REQUIRE playerNum >= 0 * **************************************************************************************************/ simulated function playerEvent(int playerNum, string eventType, optional string args) { local int index; // Notify GUI. if (mainWindow != none) { mainWindow.mainPanel.playerEvent(playerNum, eventType, args); } // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].playerEvent(playerNum, eventType, args); } } /*************************************************************************************************** * * $DESCRIPTION Called when the client has respawned. * **************************************************************************************************/ function respawned() { // Actions that require the login to be completed. if (loginComplete) { if (!bSpectator) { spawnProtectionTimeX = sConf.spawnProtectionTime; } } lastRespawnTime = control.timeSeconds; } /*************************************************************************************************** * * $DESCRIPTION Checks whether this client is muted. * $RETURN True if the player has been muted, false if not. * **************************************************************************************************/ simulated function bool isMuted() { return (bMuted || gInf.bMuteAll) && !hasRight(R_Moderate); } /*************************************************************************************************** * * $DESCRIPTION Sets the current encryption parameters. * $REQUIRE sConf != none * $ENSURE bEncryptionParamsSet * **************************************************************************************************/ simulated function setEncryptionParams(int k, string cs) { sConf.setEncryptionParams(k, cs); bEncryptionParamsSet = true; } /*************************************************************************************************** * * $DESCRIPTION Changes the team for this player. * $PARAM newTeam The index of the target team. * **************************************************************************************************/ function setTeam(byte newTeam) { local bool bPlayersBalanceTeams; // We should use the changeTeam function or else we might break compatibility with other game // types. However if bPlayersBalanceTeams is set to true it might prevent the player from being // switched to the desired team. Therefore it is temporarily disabled when we make the switch. // Set bPlayersBalanceTeams to false. if (level.game.isA('TeamGamePlus')) { bPlayersBalanceTeams = TeamGamePlus(level.game).bPlayersBalanceTeams; TeamGamePlus(level.game).bPlayersBalanceTeams = false; } // Set override flag. teamSwitchOverrideTime = control.timeSeconds; // Backup the players score. scoreBeforeTeamSwitch = player.playerReplicationInfo.score; // Switch to target team. player.changeTeam(newTeam); // Restore bPlayersBalanceTeams value. if (level.game.isA('TeamGamePlus')) { TeamGamePlus(level.game).bPlayersBalanceTeams = bPlayersBalanceTeams; } } /*************************************************************************************************** * * $DESCRIPTION Checks whether there is a Nexgen configuration resident on the client. If this is * the case the client might not be able to initialize correctly because of checksum * mismatches. Therefore the client will be notified if problems may occur. * **************************************************************************************************/ simulated function checkResidentConfig() { if (player.consoleCommand("get " $ class'NexgenUtil'.default.packageName $ ".NexgenConfigExt bInstalled") ~= string(true) || player.consoleCommand("get " $ class'NexgenUtil'.default.packageName $ ".NexgenConfigSys bInstalled") ~= string(true)) { showPopup("NexgenResidentConfigDialog"); } } /*************************************************************************************************** * * $DESCRIPTION Changes the player name of this client. Automatically sets the name change * override flag, so Nexgen won't reset it if name changing isn't allowed by the * administrators. * $PARAM newName The new name of the player. * $REQUIRE newName != "" * **************************************************************************************************/ function changeName(string newName) { nameChangeOverrideTime = control.timeSeconds; //playerName = newName; // This prevents the nameChanged() event from being fired. level.game.changeName(player, newName, false); updateLoginOption("Name", newName, true); } /*************************************************************************************************** * * $DESCRIPTION Adds a new plugin configuration panel to the control panel. * $PARAM panelClass The panel class of the plugin configuration panel. * $REQUIRE panelClass != none * **************************************************************************************************/ simulated function addPluginConfigPanel(class panelClass) { local NexgenPanelContainer container; container = NexgenPanelContainer(mainWindow.mainPanel.getPanel("pluginsettings")); if (container == none) { container = NexgenPanelContainer(mainWindow.mainPanel.addPanel(lng.pluginsTabTxt, class'NexgenScrollPanelContainer', "pluginsettings", "server,serversettings")); } container.addPanel("", panelClass); } /*************************************************************************************************** * * $DESCRIPTION Adds a new client plugin configuration panel to the control panel. * $PARAM panelClass The panel class of the plugin client configuration panel. * $REQUIRE panelClass != none * **************************************************************************************************/ simulated function addPluginClientConfigPanel(class panelClass) { local NexgenPanelContainer container; container = NexgenPanelContainer(mainWindow.mainPanel.getPanel("pluginclientsettings")); if (container == none) { container = NexgenPanelContainer(mainWindow.mainPanel.addPanel(lng.settingsTabTxt, class'NexgenScrollPanelContainer', "pluginclientsettings", "client")); } container.addPanel("", panelClass); } /*************************************************************************************************** * * $DESCRIPTION Notifies the client of an event. The event is passed to the Nexgen control panel * and the client controllers attached to this client. * $PARAM type The type of event that has occurred. * $PARAM argument Optional arguments providing details about the event. * **************************************************************************************************/ simulated function notifyEvent(string type, optional string arguments) { local int index; // Notify GUI. if (mainWindow != none) { mainWindow.mainPanel.notifyEvent(type, arguments); } // Notify client controllers. while(index < arrayCount(clientCtrl) && clientCtrl[index] != none) { clientCtrl[index++].notifyEvent(type, arguments); } } /*************************************************************************************************** * * $DESCRIPTION Called when an instance of this class is destroyed. Automatically cleans up any * remaining objects. * $OVERRIDE * **************************************************************************************************/ simulated function destroyed() { local int index; // Destroy client controllers. for (index = 0; index < arrayCount(clientCtrl); index++) { if (clientCtrl[index] != none) { clientCtrl[index].destroy(); clientCtrl[index] = none; } } // Client side only. if (role == ROLE_SimulatedProxy) { // Destroy localization support. if (sConf != none) { sConf.destroy(); sConf = none; } // Destroy localization support. if (gInf != none) { gInf.destroy(); gInf = none; } // Destroy Nexgen HUD. if (nscHUD != none) { nscHUD.destroy(); nscHUD = none; } // Destroy localization support. if (lng != none) { lng.destroy(); lng = none; } } } /*************************************************************************************************** * * $DESCRIPTION Checks whether the initial replication of the configuration has completed. * $RETURN True if the initial data of the sConf instance has been recieved, false if not. * **************************************************************************************************/ simulated function bool initialConfigReplicationComplete() { local int index; // Check if configuration instance has been spawned (via replication). if (sConf == none) { return false; } // Check dynamic replication data. if (sConf.staticChecksum != sConf.calcStaticChecksum()) { return false; } // Check dynamic replication data. for (index = 0; index < arrayCount(nextDynamicChecksum); index++) { if (sConf.updateCounts[index] <= 0 || sConf.dynamicChecksums[index] != sConf.calcDynamicChecksum(index)) { return false; } } // All checks passed, initial replication complete! return true; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ p,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenBannedDialog * $VERSION 1.00 (25-12-2006 17:06) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player is banned from the server. * **************************************************************************************************/ class NexgenBannedDialog extends NexgenPopupDialog; var UMenuLabelControl reasonLabel; // Ban reason label component. var UMenuLabelControl periodLabel; // Ban period label component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reasonText; // Ban reason label text. var localized string periodText; // Ban period label text. var localized string noReasonText; // Text to display if no reason is given. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); reasonLabel = addPropertyLabel(cy, reasonText, 48.0); periodLabel = addPropertyLabel(cy, periodText, 48.0); } /*************************************************************************************************** * * $DESCRIPTION Sets the contents for this dialog. * $PARAM reason Reason why the player was banned. * $PARAM period The period for which the player is banned. * $PARAM str3 Not used. * $PARAM str4 Not used. * $OVERRIDE * **************************************************************************************************/ function setContent(optional string reason, optional string period, optional string str3, optional string str4) { if (reason == "") { reasonLabel.setText(noReasonText); } else { reasonLabel.setText(reason); } periodLabel.setText(period); } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ NGm"r|]m".~].%]p].unr-K"|]CityIntro.unr|]AutoPlay.unr|]Entry.unr|]UTCredits.unr|]UT-Logo-Map.unr~],%~]EOL_%~]TUTORIAL%-K"  B= "Basic"]WG "FlashMessages"YG "UseNexgenHUD"SGL"r@UM-N"A}L"|L"A&x0| x|9xa| x|zxA| x|Z z|- z|_ z|.N|z| N+Np% Uv |&1M"%}y}NM"-N"'JypyNAy  ZG "OverrideClass"\G "Password"A@`G "ClientID"C5/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenAdminLoginDialog * $VERSION 1.00 (27-10-2007 18:41) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the player has entered an invalid password. * **************************************************************************************************/ class NexgenAdminLoginDialog extends NexgenPopupDialog; var UWindowSmallButton loginButton; // Spectator button component. var UWindowEditControl passwordInput; // Password input field component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string passwordText; // Label to display before the password field. var localized string loginText; // Text to display on the login button. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $ENSURE reconnectButton != none && spectatorButton != none && passwordInput != none * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); addNewLine(cy); passwordInput = addEditControl(cy, passwordText, 64.0); loginButton = addButton(loginText, 64.0); // Set component properties. passwordInput.setMaxLength(32); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { local string password; local NexgenClientCore rpci; super.notify(control, eventType); // Login button. if (control == loginButton && eventType == DE_Click) { close(); password = class'NexgenUtil'.static.trim(passwordInput.getValue()); rpci = NexgenClientCore(client.getController(class'NexgenClientCore'.default.ctrlID)); if (password != "" && rpci != none) { rpci.adminLogin(password); } } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ C]GX?j 2 ^GCnʼn 3Bt9ˉOLSOLSt V]Administrator login.]qPlease enter your administrator password below and click login. If no account passwords have been set you can login as root administrator by entering the server admin password.]] Password:S]Logine"OK/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenActor * $VERSION 1.01 (24-11-2007 22:08) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION The Nexgen ServerActor. Execution of the Nexgen server controller starts in this * class. New client connections are also detected in this class. * **************************************************************************************************/ class NexgenActor extends SpawnNotify; var NexgenController serverController; // Active Nexgen Server Controller. /*************************************************************************************************** * * $DESCRIPTION Creates a NexgenServer instance if called on the server. * **************************************************************************************************/ simulated function preBeginPlay() { if(role == ROLE_Authority) { serverController = spawn(class'NexgenController'); } } /*************************************************************************************************** * * $DESCRIPTION Notifies the server controller that a new client has connected to the server. * $PARAM a Newly spawned actor. * $REQUIRE a != none * **************************************************************************************************/ simulated function Actor spawnNotification(Actor a) { if (a.isA('PlayerPawn') && serverController != none) { serverController.newClient(PlayerPawn(a)); } return a; } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ OB "ClientKey"XGwtlz~wp, , {~w, Y~w, oz%Qwzwwz,{%Qw{ww{&Y%QwYwwY&QwwQ  _Gf@i bGե6mԝX.e& B lSmSQ@h,/*************************************************************************************************** * * NSC. Nexgen Server Controller by Zeropoint. * * $CLASS NexgenAccountUpdatedDialog * $VERSION 1.00 (21-10-2007 15:22) * $AUTHOR Daan 'Defrost' Scheerens initial version * $CONTACT d.scheerens@gmail.com * $DESCRIPTION Dialog to display if the account of the player was updated. * **************************************************************************************************/ class NexgenAccountUpdatedDialog extends NexgenPopupDialog; var UWindowSmallButton reconnectButton; // Reconnect button component. var localized string caption; // Caption to display on the dialog. var localized string message; // Dialog help / info / description message. var localized string reconnectText; // Text to display on the reconnect button. const reconnectCommand = "Reconnect"; // Console command for reconnecting. /*************************************************************************************************** * * $DESCRIPTION Creates the dialog. Calling this function will setup the static dialog contents. * $OVERRIDE * **************************************************************************************************/ function created() { local float cy; super.created(); // Add components. cy = borderSize; addText(caption, cy, F_Bold, TA_Center); addNewLine(cy); addText(message, cy, F_Normal, TA_Left); reconnectButton = addButton(reconnectText, 64.0); } /*************************************************************************************************** * * $DESCRIPTION Notifies the dialog of an event (caused by user interaction with the interface). * Checks if the reconnect or spectator buttons have been clicked and deals with it * accordingly. * $PARAM control The control object where the event was triggered. * $PARAM eventType Identifier for the type of event that has occurred. * $REQUIRE control != none * $OVERRIDE * **************************************************************************************************/ function notify(UWindowDialogControl control, byte eventType) { super.notify(control, eventType); // Reconnect button. if (control == reconnectButton && eventType == DE_Click) { // Reconnect. getplayerowner().consoleCommand(reconnectCommand); close(); } } /*************************************************************************************************** * * $DESCRIPTION Default properties block. * **************************************************************************************************/ CdGM?g 2eG䦤CnŔ> Y V] Your account has been updated.m~The server has disconnected your client because your account was updated. For security reasons and in order to apply the changes to your account, you had to be disconnected from the server. You can reconnect immediately.\n \nSorry for the inconvenience.i] Reconnecte"O]eC "Move down"`(`(&`(Y(D)Y(J)Y(y(Y(m(Y(N)Y(Y(Y(M)Y(b$Y(xY(H&Y(_&Y($Y(wY(~ Y(K)`(uY({J)^&Y(bY(P)Y(w(Y(OY(c$J)AY(cM)R Y(AY(}%`(lM)FY(rJ)NY(v(Y([Y(tY(E)Y(eY(rY(M$`([ J)c A)J)$E)G"D)iY(h'Y(YY(yY(s!Y(V Y(pY(LY(H)y(h!Y(mK)dJ)}%Y(G)Y(_K)iY(d&Y(oY(eY(I Y(p$Y(gY(j'y(IY(AY(vY(Q%P)F"P)K(M)EK)_J)iY(xK)p Y(|J)}%Y(VJ)d&J)j'J)W'Y(-Y(W J)M#y(By(oJ)vJ)n`(TJ)OY(TY(VD)QP)D"Y(A)y(cY(D%Y(J)[&Y(~%y(P)f#P)d#A)~(J)@y(?P)eM)xY(r N)Xy(fJ)uy(AN)TY(a&K)hy(t#N)R"M)]J)xy(y(yJ)[M)M M)@Y(q&y(y N)]J)bN)^N)ZN)i!M)KP)P D)rD)qD)pY(v%Y(GD)oD)OG)Y(Y(UJ)M#Y(p&J)CY(iM)Zy(x!y(?J)PJ)p'Y(jy(g4J)S M)ZD)D!y(@J)@M)V"D)PK)iJ)o P)sy(]J)m y({ D)iY([&&h'X N)A!K)oJ)bJ)wy(yy(gD)E!D)rx(FJ(Y(]M)wM)s Y(o$D)hy(G!N)[D)Xy(`(n `(=M(&h'{m G| \J h ]X Au M ev T[ A j x jy mc Hy t VB \ P D N ] l#i V w VD P R :^_ e&m m o{  S j 4 N y VF k#T Ib C?xp 'a `h H H M U i d Y q _I@ S N Z ] KZl  "F F T _` a e"n :A | S I w%W Cf  e TK  r  U&~ P L ~X yf P t #C P _ ! P l e&x P F i R v  _ bP l D e"x F!F C"T )v  b e&o _ } e&J \"X 7 N  g ! N t :vA zP :{^ Cm |M| e"I "W v  e c( r 2 v  ~ CCK )P N y Z X h [u ] gC  K&j \x ~PF R V }Me P r \ A~ {M WP L t"Y Kg j v D E B R P _ P l y x X F A U  @#b \ p Q  q  N  S [ [ i P x e&E Q S ! e"` e"n _J| D!J FP Y \^  f h s l B VO _] ] I y j i'x w G W T  "c ^ q D@  S N  "\ Dj AP x b' D P e&Q  @#_ g Dm Y"{ eI U X Wr' h e&u q" C 2 P P eP \ s e&h y  v _ C g  P f X\ U P t R @ U e"M ei [ X vh A r w U C N  O N\ a k : x  ] E V S T b B q X#~ g# L I aY 3g Ez D F" Q P ^ A k g x V E iv  U _ tb 4 e"p W ~ ;uN k u#\ j& j ^" w y D aR $y ` R{n D | A K M X __e B s e@ @ ] M y ] ew k Vx Z#F g T { c Y#p  q$~  N L P Y Df m P t u" @ e"M {[  Ji X w Dl D ] P e&_  P m c" y L( F dS l" a )M n nr { R#J P X \P e P r e& ~ P K M X _r e N  t Y  A :| M hS) Z Yg 8 u T A P P N hr& Z T g qr t hA C cP P _ _P k :@ x br E P T iP ` ym y e&| P J ur W D f os m h" A e"N P \ y S" i 0} v I P C aP r^ ^  A&k y y ; Y" G :} T 0R a {n QS| h K D Z :qg Y v Wj( D PQ  s%_  \$m P { g X H P U `' b mP o DP | x& I GP V zc ` q K ~ C M  S ] Uy k V e"y \ e"G v e&U  "c { q 5P ~ P J .B W Q  c S p e&} ] K P X Y  e Wr { @  E#L  y Z O  h <^( u 4 P A  V&M , N [  a g (P t Il @ v  L \&Y Pl g s"s P B  P O 0| [  N h Z" u \#B ^#P P ^ N k Y$w g&E W"S m v  a \ v  n P { P H H pT z b d n n#| r] J P W C~ d A Y"r {%@ ru( N JU  Z V g s(t w' D aQ :^ _ X t :u A :sN {& \ Y i dw  t%F {T K Ac y d X r g P w& M g e&Z r h P u d'B S P ns  ] W j nny \e&G _s  U _nb H p qs   qnL B Z ;U j bs  v bnC {%Q I_ us  n D{ cJ 0x& Y ee"f at { B z Q CP ` unm P { q x# G Gl T P a D n t { |  I G~ V P d P q n"} } K 0P Z %ag  u PC +& R t _ e&n a| P J _( V eP c P p B} 5a L E Z Ei  P w F D _ e&S za :_ o 8`& | W e&I JW V F g P q t P A [ P N  E%Z  "h P v 'B% B } O D [ \h h Xu mX D d&P ir( ^ hB j B w # P C K'P  P ^ EU j EP y T F " e"U aDc ` q C ~ [#K ]#Y u  g nt _X B f O f \ g i ,D y JW  F  E& S g K` JV  n X {  D&K  P# Y Q e g r < P# \ A {h :~v  a$ D D Q E {^ v  l >} y N!F {( U "b { Yp o#I Qg W )O d )d q ^g ~ }( K ^ X zg cv %y E _ S { b eo t ~ dL j Z y h a v B C ` O a _ { o R! | B H  ST gb Qz' q Y!~ e& L L Y |#f {#t z#B AP H e& ^ 9p k } y T F q { U q _ b r& o i | g H k TU Zt  c K# p b | P L e&X H f M s P @ ML O[ Sj B y  r%F I i T e&` I r n @ { C" G % T K  a \ n  _$~ K u L  p%Y  `$g P u {B M e&P O  ^ g k  o%x B'F IT 2 l& b 7 { o \ { | ; q I - P V C { c D p S} Y L ? { Y c' f P s a @ { M < { Z H W" g P  t W @ EK# M B Z h f U s  }$B B P U#] T#k 8d y P E N( Q n ^ P O l \`  x  F%E  G%S "a d P o 8O { e"H G V K# e  H%r H@  I%O  P ] P i Gv OB D P P | ] t h" j Z  w d D Z  P u ] Jo j  J# w s n D  I#P  H#^ -K# l e&y s G %i' V n"b %H p { } W J  D#W Y$e  C#s  B#A 5F" O Y \ 0v  i Y$ v %f C e&P B^ \ l ] y Y$ F  H#S E!a Czp 3]( ~ J kJ ru  F#D P R  e"_ P m |" z C} F WS e&a Cl o P |  Y$ H Wk( U R b 5e&q { Y$  w L v [  e"j e&x ^ F S S $h b u n <D} !P K A(X e&g T u y' D P Q  W& ] , e"j P x X E R  Q R ^ P k y w 3l E X R ` ^ W n e"{ e&I  E#W _e  F#u  G#C -H Q ,w ^ X k  S&w  R&E Y$S  Q&a -P o 4& { 4N H  P&U ,t c z p  g# } H J P W x c FY p Fv  } p i J QW h e $P r S# W P M Ed Y V#f 0P t # M @ W#P X e^ Eb& l J  y l F +  R Zn_ Ei' M P Z | g 2 a s H@ P O A t& [ CIg Z v E t& E P Q  "] z( k  H(x 0 e& F  Q" S  O" ` CZ m  H" z I j# G L# T P a l" n c" { >P H |( T ^ ` q m | y A" E @) R QP _  l a P y eI  F |! R f P _ ^ l  y w F _ R n! _ z%l E z P G }#S n! a ~#n n | ur& H # U a b e& o Cp | e& I q  V N$ c P o C& | ~ H P W g d F) q B ~ y J B X  "d c# r B  X K h W ~ d c r ] A d N Y ] f e"i o C w e z f I  V#X N$ f p r !Q   y L a' Z g L g L t L A K M * DZ * Ph P v " {C ] Q  y$] k k  z$z B) H _Y U l c m r P A K H M o Z uy i  e% w A D Q) Q  d%^ q l l { $a H e&U  c%c N q 'D! B  e& O JP \ r h L w J F ( e"U I c rP r <P   b%K  a%Y  `%g  _%u  ^%C  ]%Q vv  _  "l D z  G e&V 5h  d ~ q MP @ Mc L 0R' Y G{ f Gzs e& A y N x ] IP l FP x x D Q P `  l P { G * V P e K P q \_  } !n J dW K e&e w s B P Q P ^ * P j ! w 'Q F " }  T " ~ a g P o P | Y I iV ^ d e&q C  P L P X P d X!p P  V) L }& X o! e g r t!  {! L }! Y {& f {! r B"  {& L g X }& e c r x L"M 0 X [ Lh A q F M X S W m ` W O# m :C z \ P G d y T h O# b m r' n t v { _ i H y nT t r' b P n S" z E  G W T ba ^ o P | W"H  P V S" b P o { { P H h U P b P n v" z W"G e&U e&c P q P } P I a U V P a ` m _ z ^ G L T { a e&n P |  A#H I( V q c e&p K# ~ J J K# W  P d  Tp  P   P K # F W # X c _#p ) M ~ ) P K 7 m W 2 {d 2 [r C @ K x M K v  Y N P f I {r p#@ s#N ^ M \ k v  h k P u  S A v  O u \  P$i X w K D A Q ~ ^ ^  k yx  ]$H  V  ^$b m p I} JK y Y a g  k$t  l$B T P U ] V j y  w !DD  m$R  n$ ` f m TB  y asF  t$T  u$b  v$p  w$~  x$L  {$Z  |$h 'P v  J%C af Q  K%^  q#l vP z  L%F  M%T  N%b ap  O%~ V L R Y De  P% s AY" @ AM M Ua Z lP g  j%s  k%A e"O  l%] e" k  m%x [F H T e"`  W#n  u%|  G$ J [W |% e  B&r  C&@ Z N y [ a i A v V C U P S ] R j  "w  I&E  J&S  L&a  M&o X}  N&L  O&Z zh K# v C! B g O y \ 7Qh d v '~ B %j P +M$ ] 0V  j 0z w 2d D 5O P HP \ EO i Ec& v RP B CT  N JD! [ CW  h YP u CY B _P O f \ e m d ~ A O ^A \ r& i S) v B C L#P @  ^ g k W x W E P R p ^ n m i | [ K P  Z y g n s y& A Q# N E Z A f S) s E( @ C#M B j B v I'B A'P 0Q# ^ AB k yw 9P I ;E NV ry d ra r e'  EK q' _ g l Q{' x P E @( R X _ c(k d( y  d(F Hg T HP# a 5m U b V o [" | z I e&U y c Y p v  } { N ] J \ g ,P t 3\( A Wh( N dn( [ rZ h au M E t& R P _ t& k H x P E P R r P ^ X n P { g G l S p J` P j c( w c( C T) P U) ] `# j s w rC X Q r ] P i r v }r B qr N }y  Z I) g \a t X B \b  O Je  \ <e  i xCv 5j  y Hw bF {& h f u ( B t O P \ | vi u _ |( l  Tx ^ L iX Y ii" f do( s f Wl( M WY u  g Ms PZ  A LN IZ  \ c u i  fv  WD ,{& R  u  ^ u j x w L( D q P f\ 6g j 6c# v 6V! C WP u  ^ [" j / u w [" D * fQ p _ k l [" y j F * WS * u  a K q m K M z u G K DT fb Wp u  ~ * X J 4 u W * Kd ! fr @j@  g j ! Ww ' E ! u  R q ^ * `j * Wx QF' F BIS * f\ Qj u x * p( D " c" Q t' ^ " l" k Sx * G n' V m' b Z o "   | > e&I l' W k' d } q ~ ~ B e& K :_ X G { l :H y } E ~ R s m _ P l T! x D B uX O q! [ hL# h V'u X$C Va Q Vy ^ D l v! y y! F jI'S z! a X n X z 0S' F @ |R 0v& N x  [ ! h @" u X B 9X N #a [ #y h &["v q" D g  Q $X ] g i i v  X B { O  U" ^ Qk ` y P F \ p R | _ A n \ r' { \ X H p X T y o a P n m" z UG BU o" c X p n} C'K " Y m f q s \ @ k O r  [ A'g l" u j B w" O J [ Vi m" x e& E P R r  ^ \ j X y e&E l S  X ` { l  M y  @' F  X S L#` { n # E { r H # C U |& b + P o + N  { z& H L  U M  b 7 u o S) { - {H 7 | V v& c 2 e# p L# } B J { W K l d ~S) q K v } V N J m#W \ N e us& q uS) ~ I#K q#Y r#g a { u f {B q y# P q M$ ] j& j Z\ v q P E y P R Ck& _ lP l lg y y { E y v  R L ^ { j Ce&w L E >Y R e&`  D$n Ln  | L I  E$U  F$c  G$q |  D L v  Y g f a s  O$@ B N =H [ =d g 0Y t  sB Z$P x^ wm t| Q J PX o f F s 4P @ G M 7X Z 4yg +P v PC 7xQ g  _ (O  l b y a G a U !X b \& o ` | O  J _  W ^  d !C q ] } A K X X d e s(q \  ~ M n Z r f  d$r s @ _' L  e$Y ~ g  f$t qB pQ  g$`  h$n  i$|  j$J c X M e Z r ]  ^ L j Y b f W s x @  T& L NY h g j t X A g N a [  J#h  I#v X D I Q x ^ R"m C~ } P J Cc( W H d Gc( q Qg ~ [K \ Y ^ f ] s Z @ X M P Z Qa g TC  t  R%A a O _^ \  S%i dw y%E x%S fa q o  T%| eX J  U%V  V%d a r g   W%L \ Z X f i r  X%  q%M  n%[  Y%i  i%w X E  h%Q  g%_ i m  f%z X H  Z%U  [%c v  q rX } vB! J vy# W  \%d rv  r l}~ v_ L vy Y rs f ua s va @ s M q Z h f Y s f @ q M W Y wf ae u aq B \ O [[ _ i  J#u  I#C ]q Q Qq ] [ i I  u T A X M Ic Y W f l s  I#@  J#N J \ Lh Mx S G R S 'o& ` 'C% l 'A% y Z F q S l _ T  l X y !h F Q R l _ W l M y Q F y S [` \ n { {  ~$ H a U s b D! o j | F I X U T b E o P | t I  oU  s$c  r$q j&  a K X X i e n r r ~ D  K C X | d U  q V  ~  J#K  Y  "e W  s o @ p M X Y !h e Y"r v @ q M '  Z %h f y s y @ +|  M 4z  Y n f FX s y  <t Lp X E eq r<s~Fx  L<r Y a f C s M  @<X MFw  Z{gFB! v V$C U$QJp _ T$k S$y R$G Q$U>| cC pJ| } q JJD  VJr c k pJP | h  I X V v# cJn p {}E KJm XF e Y r I @G M K$ Z J$g I$u H$CJl QJk ^Jj k>xOf FYX S { _ C$lUn  z N G B$T A$b @$p_X ~ qJCX$XH f_h rCe nn LCW Zy fq v  ta Aug Nk v# [v L hf k tb @a E" L^{XuA guB ti A~r Q J#^~A lK x y~B HK v U~r& br oK w |r IA V7 P c- b# pr& |7 } IY VK  d+ ~%qi I  L# D Yb  fe"s o AT\ N K Z^e"g J u N A N# N^g [A h ]  u ^  B R O I \ w i e&v Z DB P \ ] ~" jr& wS) D _ P ` ]W j k" w f& D _P `^ X l p" y o" F _( SHF  `C" lQ$yy G k" U {" b X n }' za GU$T { by oQ  }aIs  W Oc QrD'A oO k" ]{ j p" w\Q$D} Ry a Ooa ~ VK i' Z ng ou k" C;?GPy M Wq dt O# qm O# ~a Kd aXg fOQ$sy AW d" OO \M j!iM ~ w e"CDa QDy ^a l Vy VHB W J"dI'r y @ a NQ$[ X'i Y'wJE l T P a ^  nL'{M'I D W<y d l r P <a L ^  YjQ$fEh t D A P N w! ZjT'g u! u X B u' OWW" \ r' h P u r! Ane"N p! \ K  i `  vwe"C["Q@ _ Ql O!z h IT  VIbHqe& E Mr r Zz [" fr t sr e"@U  N Z J gR  sc R  g h KU l" XU c" e_ q rd#U {Mg C" [:G hW q uO {A:FOg& ]:E j:D wG DD:B R@ _g E l yg J FBR {aJ ol |s'I[ WCidX M'yZQ|' h ~'u JC IQ u _ X lb y! t  F! Z S! ``XonHC( ]! p( jc" w4 t  Dc P4 X ]4 Qj8 e" xm E* J Rnq  ^ J k Ix u F X Sy ` t  m Z z `Gf US b p( oc" | t  IX U X b Qo y  }j I e"VK N dK r' qj" ~ JK IY u g X td# A* t  N* Z [* `hh vd# B* p( O/ t  \d# h/ X u/ QB8 e" P( e&]_q  k J x IE u S X `( q mP JyP IGP u UP X bd# o t  | Z I `VzndP R p( _V ["l t  zA [F X a Qn e" | UIqq  W Bdu J ru Iu u Mu X Z Jg Iu u C X P ]& ],M" i t  v Z C `P3Z( ^3f& k p( xc t  EIa( Qc X ^c Qki e" y C" F W Sbq  `Q J mQ IzQ u HQ X UPa( b Jo I} u K X XWf( eWg( r t   Z L `YWi( gdi" t p( A u N t  [h g X t QA e" Ouq  \- J i- Iv- u D- X Qo ^n ll  zc  GM" T:p aG( n5i {F( I5b  VG( c<^  pF( }J^  J\c  Wz  cl p\w# }Ck `JdUjmB) O) LY XZ  eR) rq ~eUKO`h o I#|ES JET VEX cEY pEZ }E] JE^ WEC"dE rZ T LX XZ e] r^ M LL  YT fX rZ ] L^ YM fL  s:a @N$ NL)[X is Yvt XOT gX tZ A] N^ [L  hT uX BZ O] \^ iy b vN X{] e{^ rx^ rt( Liq( Yin( fdg" sWq @We( MPb( ZP\ gIb( tI\ AD\ N<| [<{ h<z u<y B<\ O3[( \3\ i v v w Be zO6M" I_  Vc# bc" o]" |e" Ic dV_  zc# Fc" S]" `e" m_  zc# Fc" S]" `e" m_  zc# Fc" S]" `e" mj" zl" Gc" Tg.aj" Ol" \c" i:Rvg ,Dl" pc" }e3Jj" }l" Jk3WHi# JHD( WHq'dHB( rh  i# L c( YUGfi# mq zZ GFY hM s'uAKCs NX `-ZV x#zU drT NVS gdx' KR K XDlcJ OP k \O oGd#v["DN pRy BM YOL ghJ ` OI GoH JvG v$@wl" vwc" Bnl" Onc" [vC hvy uG BH NI [vz hv{ uv| BQW  NQV  [QU  hjd uji AjJ Nj [jV hDF us BsV NDr [DM  h;r' t9I@0D P0 ]K jL vM CV P9J]J' k$L xd Ei Q] ^ kV xw Ed R ^V k@ xG DGV POi ]O iOV v\i C\ O\V \H' iI vN CQ OR \ iV vw CZ OS ]] i vV CVPV_c" ni { GV Tl aO' n^Z" {l" Gc" TTVar ~P Ju& Yi fi sui @CN  MCX Zlh gC[ tC\ AC] NC^ [\l hCHuZF" CYh PSr ]N\ jNy wNz DN{ QN| ^E`& kAJ xAH E<g R7v _7r l/^ y/] F/Z S/X `/T m7w z7t G)L  T)^ a)] n)Z {)X H)T U(P  bI!Co(Q  r(R  w LP  YQ  fR  sG H Li' Y J f & s K @l" Mc" ZX gW) tk An  Nk [j hi u  BP OH \  i~!Cvi yk Fh SW!C`T cS pg }f Jg WU!Cdn  gZ te AP NZ [d hg u] Bc Og \] iQ vX Cr PX ]g ja wY DbW) Qbk ^bj kQ xbi E@ Rbh fh sP!C@[ Cj Pf ]k jl wB) DE' Q\ ^n k]  x^  EB) RE' _B! lbg ybf FB) SE'`[ n\ {]  H}B) U}E' bxn  o^  |ry HqB) UqE' bvs ov^ |v{ Iv` Vmr cv  pK!C}be @vZ" Mbd Zbc gjI tZn  AcUNbUcjw xaUDH!CY`U\TD  qTE  ~VI KVw XTF  eTG  rJh TH  LTI  Y{ Cf=P i:P v<h C:r PTJ  \Tj iTk v]KACTl NTm [,g h!g ug B{ Og \ Rii wZ  Da Qb  _Z lJxX F n  S Z  ` [  mucz k  N c  [ L  h j  u i  B d  O e  \ f  i l  v n  B v  O n& [y  g ~ t  A n  N d[| i X v _ C `P a^ Rl y z z G { T } a m  m n  z o  G p  T q  a Q n r  { s  HbcU t  i u  v B! C w  P r' \ q i n  vy y Cf B! Pj e&]a y k= n  x HE e&T n  b K o J | H I e&V ]  d ^  q W ~ C" K q X a e g r Q n MHH  [H| h i' HHXU g  s q"  l" LqcY c" m e"z O' H{ W U{ C" bb*;Tuo q d a q g ~];<vK QAj;LrO n Ah;3pOHV;mA]Hh^H[|HbZ;!XxH\P;sOny G}m f" Kh t Xg ie\ f" sa i@X X NX i[H N"iH VxM y GM z TM { a_cnA g BA t&OM | ]M Qj( r x; g E( S R; t&_TmM } K0 y Xg e0 z q0 { ~ l" K c" Wy]d l" A c" M t ZWn}0lnA\ v { w In\tW* h# KnCEX|I]{fz<|y7xx:ow=iv1fz KWI"CbDenoQ~'C}Ou-Lt ysYruE/SF-BqoAGG> k N'c5Z, { OG*\ {F h#THb> h  h# LnPXIh h# ~'W.KnfDy7 h# }'r.J: h# x'j_EJdJ*\Cz'_Z}KW? h#mH*bE{'\Z@L2ZC h#Lg h# Z'o`gG*[^GP h# e[xrDU@E,[bLUM"aL h#CC,[]OQ`s`O h# SD,<_)`N'IR h# pCI|OEF*hs_:I R:J ]Ph!C=@\Y}h w V\ h#dD*V{rc5m` { b?noGC]h v `q)X,nyxZCo R!AA!c7`f q" Wf g  df p  q P}o h# M!e=Y:K V:L ayRkTE}r c" Br l" Og+\z h# G| h# T| e"`o)|Gn*?Gu!p!|![:]kWjlK z:M D?]NMNk h# yQFRaS!yTZ h# r X ~UK Df@PtHa DMYdVe}H~b`z`HVZfYw|MPf  ]Z! g[!q\!y]!A^!I_!Q`! Ya! cb! nc! yd! De! Of! Zz  eakoVZWuXP!o_k h# J P WKjcljMQ$wY[ vgVgw}H|tZQ W l h# x C" E\NRHT` n  ~H3K B hgu U\HPj[H h# c h# o h# | OI S" X Ue BsHB AaaF ` S" f h# r Q ON Y] Bk h#y\G R bHGoxtM'A J  @HDM h# k @' x o D TQ h#`!]nYtKHJ D ] H j:_w!L:V h#P!k4^HDR h# p h#|rtJ!j4~ `r W@QN!a4_]S^m h#F_Tcr!fTU!`4i`)] h#FaTtj!|M^ fkby h#O%s] p( Pc]]`@c0`d P h#pe(~b8f h#^ mTW|fS_CEp_\tu w i h#w PFQT v e h#s_AAOV` h#v^JDNHN h#V Deg(sAe[h&@ h#fHA tZ h# Ti3`Z h SZ C" `j,mkYl(pm%Xns}Z c# p_oQ}n2No0@ppqK_Pid|yn  uZ QBQ Pr-]g Ja Wq d_fDp C" t W AqXMs&et6Ku#A; h#dv*rpa\w6}x#sy'VA h#}z(K{&s@cY||},VH h#BZ PPG^Z DeX5sqCEh_" m`"wa"Gb"Zr#FlI}rBoYZna h# HH@ Tq\ttDahu h# Ii w U\ h# cu C" pZH}g h# Eu h QDz^i v Xu X fpBsn*WcuqAXh c" wh l" Du PQEg_u DF~TdgvJKs h# U8aG~YHC WHHwH`Tq[[C k ^@1j h [ h#h e&vA4D h# xBE e& ]C/j e& YDf h#D e& RHV_ e& }EJ e& \BsiqoQ\Fm h#  U L T X { eGrHD S VIcqPuG@ EJEqfDWPm[KH e&ZL#hMKNdbC 3r"BA3?fJ3V%p34SUOhH^A h# _ U k T x { E\GR[LYh]e S Bx" Oy" Yz" cA?Voj YEP^QsH{L e&GHK Uq e& ul )BRkSBj*yT e"s c" A l" NK*sQZTk h#AVM Oo h# \I*!BhUjbCECHcBHHG  J h#WDJeVo:N M WW h# e:O qWS|E*mqONR@ h# R h# _U?kC*3Gjb\tq:PeA*LIm w vRND)<MR v _bAmW Lmxl{)TLdXpY-F h# sr #X@ h# X}"SdK"Cw"Szz"=t" h# q"ZH~"[QF"oSW" h# j":Q w"p~@" h# ~":RK"V`S"r #|s":S o":T x"KkB" Mpm" h# ]":U j" Ljs"\;]"rcX"oI{":V D":W M" LSY" h# l" 3y"]\l"b CH" h# K" GfW" h# }" h# J"boQW"s\h"D N D":XR#sqZ# JK# h# U#h 3F(h# D(o& P(jY(B)o(KFX(r #i ^( M G( B~S(D EQ(\nV(JTD(h 39X(C-Q( iV~(^@T(d` T(Bit(:L ](}P~i(@`g(D.G(Nu(cuC(_ x(^ D(;e(P(E:x(F2r(;=8d(;g2\(}gNN(:~B\(r #L^)h# j)G1v) BMg)j LQt)\~E) I@C)H6C);hYy)I3R)] <DE)~bI)pCk)|@n) Jn)"h# x)D _ D);bc)TA) pnI)Sw)\K)RJ)QR)z_Z)[ey) t2^)PP)TP X)Od)J1l)N])K5e)}KVZ)^Up)L3E*<h# x*M8E*b)}*=h# f*}hTr*O&F*N8l*Jh# d* hq*r #iy*Ch# b*ULo*O1{*Gh# l* M~y*X]w* I4T* YlH*}jTt*Vh# H* ZWT* i]k*PDH*Q7L* [2C*] <@u*R8u*_Um*xKB*D dM*n,q*S:]* JW*Xh# a*h 39m*\h# f*}ss*yBf*ag h+:}A t+_a u+ZlA+:F_m+}cIL+ec U+Ynb+}] P+T/X+UAG+mh# H+I,U+V%A+qxf+EC^+W1a+qh# R+rh# _+|Gl+rPs+r #ZC+ Bp]+D EM+X} R+X/O+h 39~+}h# w+ JD+zh# N+ iUZ+}Go+YIv+h#+~GM+^WT+ZIk+h# t+h# A+{PN+mK^+[i+Ny~+h# w+\D+ubW+h# y+h# F+2nS+fUA+CJV,]`,r #Dq,^u, BUK, IX`,zFx, YK~, JI,h# S, \Z_,mty,P m,D q y,h# j,_w,NwM,] <DD,gUH,ft],E`6Q,j L9G,`@,h# T,a`,hV p,nAF,agG,_rn,h# `,[rl,gF^,r#V d,bz, JO,h# Y,]<Ze,c-dM-{s\-D@O-eO-uSb-fu-~wG-g~-Y~L-QjJ-ht-h# K-wQX-hUi-h# ~-}_J-:z_i-iH-Cuq-h# f-|Or-r #qA- wGr-WHy-h# A- v^M-h# k-yqx- Ji-h# s-D E-thD-h# l-h 39x-qNq-r #h-iUg-h# |.efI. Sh#`1 3o1xb1a^x1Ah# V1 Ld b1NkF1cOq1l>@1k%~1wMc1Nh# p2j>|2Sh# z2 6F2 qJ|2n%VaF2Oh# g2 Dvs2"+i2\h# T2h 3_`2Zh# 2 JK2Wh# U2Q]a2XV~2H 6T2#J2G 1m2:pz^2ZiX2Ch& A2Ci& L2F .Y2lh# G2eh# T2qca2E 0D2D t2nh# H2C T2B j2A @2UXQ2CW i2tKu2ZL#@2e N2l  W2k  a2z  k2du2ClF2@ .r2c `2uh#m2zh# {2Tn G2}W g2}C" s2TT@2Ca^2Y  2~h#L2zZ2F!h2dv2i D2CdgQ2} x2#E2Tlh2h#F2c T2Y  \2b i2a q2zy2C)PG2~/W2F!F2}T2|i2d@2i N2` [2} c2Tgp2_ N2q V2a b2g o2T<|2Q Y2CoDf2TLj2n  H2h# U2T3b2C9%2{d2CnzB2z|2ThW2J t2h#A2H O2G W2F _2E g2T|o2D M2CyqU2T]/F3y!u3xV3^h# t3wA3h# ]3fRj3C4S|3vO3:qA d3C e3`}m3CM*j3u,T3h# @3B L3y T3z a3{ n3| {3TqG3t+x3s*c3r,M3Oy3Z?SK3A ^3NZf3q!@3p-a3dhN3ov3n"R3mt3lS3k+p3Z?x[3@ S3NV[3L jq3jP[3_k3j'j3zyQ3i(J3h'r3g&Y3f73e)v3d,_3Z?sK3 ~3@#RF3c%X3b1}3{ n3| {3I6G3a)}3` f3_ F3\OS3Z\?Se3h# x3N\ZD3h?^3]I]3<tf3^Z3]+o3\Z3OOs3ZO?YE3C! ^3NOZk3UfE3LCk3GQ$n3[ |3Z H3ZG?MU3Yb3:HHs3X{3WL3V_3wq3SG3Oc3Z?Yu3z  N3NJX33Rb3h# t3$h#A3UP3&h# f3Ts3SI3R\3Qp3P&E3vk3w|3OR3Sd3Z?D@3O D3@#Sd3N:w3z q3{ ~3| K3NGW3>|^3FO1Z3dNi49h#w4M?F4Li4Ig4Q' |4Z?bE4c3g4@#qZ4HK4Gc40{ z40| G4FT4 a#4fd4;a#4h1J8}a#4Jr9SY|:EU:K-d:NXQ:Di:C w:BD:AR:Zs?Gc:<a#4Qj:@{:d3W:EVJ:`:~y:}X:j] t:|A:{T:jwm:jOC:jSU:Zj?oq: a#4@`:@j#=`:z]:yq:x D:Q| Q:NjM]:e{j:w e:vq:uE:a#4C5X:t[;sp;rD;Z' Y;[' c;\' m;N  w;X B;{ M;W  X;]' c;^' n;?lz;ja#4NIf;CD | t;a#4zp;sa#4pj=qZ=yEh=pm=o=nZ=lr=CF b^= a#4E@=QXE@Ms]@)yDP@Q T@m^@lt@k+C@ a#4kn@j(YA a#4U?AAiVB a#4EsiBhnCa#4N@C a#4[NDf'iFmVqFT/GFh# vFUKCFg!NFT'oFh# VF a#4cbFfEG)Jq_GR PGNe[Gh# @GJCMGihPGh# xGeEG a#4c[Gd!~I JD_Ic#cIbFIh# UI a#4LaIamK a#4Xk@K`XLDokLGcZLh# }Lh# JL_VL=mL a#4UzjL^M]RMFYgM a#4I~@M\IN[^N a#4DoNZsO a#4Z[GOYaO$}qO a#4InOh+wQXbQ a#4suQWhT a#4I@TVIUU]UTuUSKUIkhU a#4V"SURiU`BFUa#4SNHVC[ }[VQXVa#4D3mVCa#4cqWPTYHa#4MeY'a#4tr[Of\Ny\a#4n?X\MF]Le]a#4~uA]jH^KG^wI[^Jd^Iy^HJ^)a#4lY^[a#4r%E^a#4FNw^tR}_l O_h# \_G h_k t_FA_EP_h# `_Dl_C{_BN_h# c_h# p_A|_N_~^_}z_h# S_|__{p_zF_y[_h# j_Cb iv_;a#4V__sru`xg`w~`v]`uz`tJ`a#4ptc`\PSagI caslarCaq#SapvaoNaa#4y^a]YWbccpbnSbm.cbl3QbkDbj4cbi1Wbf Hbe Qbh&Zbg"@bfbbeybdLbd gbc rbc|b6h# Rbt'_bb xba BboV~Mba#4B1KbCd wMb` Dba#4W[Ob_ fcoWeocbTch# dc3a#4j,qc^[c*a#4Xcc] {caEca#4M_^c`kd S(-Cd T(pd U( Fd z  Rd V( \d W( fd X(qd wDEd,h# Id\VdEa#4Gfda#4Mmd[ ziZGi GUi_\iY si^@i3h# ViX ci:a#4n niH"K c\ia#4Wi eVi]{i\Qi[ biZoiY}i<h# WiGa#4gdi [KjXfjDh# jCI ILjOa#4v,Uj 7jKjWujW JjVUjV hjIh# sjCH A @j ukAjUljU BjTMjT ejPh# pj\a#4w,}j XgtkS[kz  tkR~kWh# SkCG [`k_a#4T{{kSOlF AklRllba#4Z{ElE A_mQ `m [lmmna#4M{YmPfnQynOMn Hw_nPVnNknOsnih# NnD [nMZnqa#4S{bnC Auo _RvoNHoua#4I{]oB Afp!a#4Ngp:a#4Kuq c7@wMwwxh# Kw PUWwLlwKw{h# Tw ES`wLswh# {w dXGwJ _xh# lxa#4Y'yxK Rxa#4k,\xa#4mGxa#4H"txJ |y bdFyI jyh# tyH AyG Ky)a#4|Uy gcQyF tyh# ~ya#4SKyE ^zIhzD {zC Eza#4Oz awN{HE{GW{F h{Eu{DD{h# R{ `N^{C l{B y{AE{@$Z{P~{~-N|h# {|B H|a#4L R|a#4[^ (aya#4X ZA rȋ@ |ȋ}Fɋ LbTɋa#4O vʋ EՋ~OՋ}`Ջ|tՋ _fD֋a#4qj׋h# [ fOga#4qvh# gƕ kTsƕ|Gȕh# `ȕ Slȕ{ɕh# Sʕz`ʕa#4Dvʕ mZzoa#4y5T{Mh# ba#4N o oC}z@h# OTa#4O\ pikC)Th# }a#4SJa#4{,] rCXy[h# iyvxL roawPĪvfĪh# wĪuDŪa#4N5UŪCj Tca#4VwtM tR_i yqh# ja#4s,wCg fjҫ@P٫[b٫