diff --git a/GameFiles/IW4x/userraw/scripts/_customcallbacks.gsc b/GameFiles/AntiCheat/IW4x/userraw/scripts/_customcallbacks.gsc similarity index 100% rename from GameFiles/IW4x/userraw/scripts/_customcallbacks.gsc rename to GameFiles/AntiCheat/IW4x/userraw/scripts/_customcallbacks.gsc diff --git a/GameFiles/IW5/storage/iw5/scripts/README.MD b/GameFiles/AntiCheat/IW5/README.MD similarity index 100% rename from GameFiles/IW5/storage/iw5/scripts/README.MD rename to GameFiles/AntiCheat/IW5/README.MD diff --git a/GameFiles/AntiCheat/IW5/storage/iw5/scripts/README.MD b/GameFiles/AntiCheat/IW5/storage/iw5/scripts/README.MD new file mode 100644 index 000000000..def15c990 --- /dev/null +++ b/GameFiles/AntiCheat/IW5/storage/iw5/scripts/README.MD @@ -0,0 +1,23 @@ +# IW5 + +This expands IW4M-Admins's Anti-cheat to Plutonium IW5 +## Installation + +Add ``_customcallbacks.gsc`` into the scripts folder. (%localappdata%\Plutonium\storage\iw5\scripts) + +For more info check out Chase's [how-to guide](https://forum.plutonium.pw/topic/10738/tutorial-loading-custom-gsc-scripts). + +You need to add this to you ``StatsPluginSettings.json`` found in your IW4M-Admin configuration folder. + +``` + "IW5": { + "Recoil": [ + "iw5_1887_mp.*", + "turret_minigun_mp" + ], + "Button": [ + ".*akimbo.*" + ] + } +``` +[Example](https://imgur.com/Ji9AafI) \ No newline at end of file diff --git a/GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc b/GameFiles/AntiCheat/IW5/storage/iw5/scripts/_customcallbacks.gsc similarity index 100% rename from GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc rename to GameFiles/AntiCheat/IW5/storage/iw5/scripts/_customcallbacks.gsc diff --git a/GameFiles/PT6/README.MD b/GameFiles/AntiCheat/PT6/README.MD similarity index 78% rename from GameFiles/PT6/README.MD rename to GameFiles/AntiCheat/PT6/README.MD index b38f41c7b..99493231a 100644 --- a/GameFiles/PT6/README.MD +++ b/GameFiles/AntiCheat/PT6/README.MD @@ -24,12 +24,12 @@ Add this to the WeaponNameParserConfigurations List in the StatsPluginSettings.j } ``` -Now create the following entry for __EVERY__ T6 server you are using this on in the ServerDetectionTypes list: +Now update the `GameDetectionTypes` list with the following, if it does not already exist: ``` - "1270014976": [ + "T6": [ "Offset", - "Strain", - "Snap" - ] -``` \ No newline at end of file + "Snap", + "Strain" + ] +``` diff --git a/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc b/GameFiles/AntiCheat/PT6/storage/t6/scripts/mp/_customcallbacks.gsc similarity index 100% rename from GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc rename to GameFiles/AntiCheat/PT6/storage/t6/scripts/mp/_customcallbacks.gsc diff --git a/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src b/GameFiles/AntiCheat/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src similarity index 100% rename from GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src rename to GameFiles/AntiCheat/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src diff --git a/GameFiles/GameInterface/_integration_base.gsc b/GameFiles/GameInterface/_integration_base.gsc new file mode 100644 index 000000000..20d4cbc65 --- /dev/null +++ b/GameFiles/GameInterface/_integration_base.gsc @@ -0,0 +1,714 @@ +#include common_scripts\utility; +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; + +Init() +{ + level thread Setup(); +} + +Setup() +{ + level endon( "game_ended" ); + + // setup default vars + level.eventBus = spawnstruct(); + level.eventBus.inVar = "sv_iw4madmin_in"; + level.eventBus.outVar = "sv_iw4madmin_out"; + level.eventBus.failKey = "fail"; + level.eventBus.timeoutKey = "timeout"; + level.eventBus.timeout = 30; + + level.commonFunctions = spawnstruct(); + level.commonFunctions.SetDvar = "SetDvarIfUninitialized"; + + level.notifyTypes = spawnstruct(); + level.notifyTypes.gameFunctionsInitialized = "GameFunctionsInitialized"; + level.notifyTypes.integrationBootstrapInitialized = "IntegrationBootstrapInitialized"; + + level.clientDataKey = "clientData"; + + level.eventTypes = spawnstruct(); + level.eventTypes.localClientEvent = "client_event"; + level.eventTypes.clientDataReceived = "ClientDataReceived"; + level.eventTypes.clientDataRequested = "ClientDataRequested"; + level.eventTypes.setClientDataRequested = "SetClientDataRequested"; + level.eventTypes.setClientDataCompleted = "SetClientDataCompleted"; + level.eventTypes.executeCommandRequested = "ExecuteCommandRequested"; + + level.iw4madminIntegrationDebug = 0; + + // map the event type to the handler + level.eventCallbacks = []; + level.eventCallbacks[level.eventTypes.clientDataReceived] = ::OnClientDataReceived; + level.eventCallbacks[level.eventTypes.executeCommandRequested] = ::OnExecuteCommand; + level.eventCallbacks[level.eventTypes.setClientDataCompleted] = ::OnSetClientDataCompleted; + + level.clientCommandCallbacks = []; + level.clientCommandRusAsTarget = []; + level.logger = spawnstruct(); + level.overrideMethods = []; + + level.iw4madminIntegrationDebug = GetDvarInt( "sv_iw4madmin_integration_debug" ); + InitializeLogger(); + + wait ( 0.05 ); // needed to give script engine time to propagate notifies + + level notify( level.notifyTypes.integrationBootstrapInitialized ); + level waittill( level.notifyTypes.gameFunctionsInitialized ); + + LogDebug( "Integration received notify that game functions are initialized" ); + + _SetDvarIfUninitialized( level.eventBus.inVar, "" ); + _SetDvarIfUninitialized( level.eventBus.outVar, "" ); + _SetDvarIfUninitialized( "sv_iw4madmin_integration_enabled", 1 ); + _SetDvarIfUninitialized( "sv_iw4madmin_integration_debug", 0 ); + + if ( GetDvarInt( "sv_iw4madmin_integration_enabled" ) != 1 ) + { + return; + } + + // start long running tasks + level thread MonitorClientEvents(); + level thread MonitorBus(); + level thread OnPlayerConnect(); +} + +////////////////////////////////// +// Client Methods +////////////////////////////////// + +OnPlayerConnect() +{ + level endon ( "game_ended" ); + + for ( ;; ) + { + level waittill( "connected", player ); + + if ( _IsBot( player ) ) + { + // we don't want to track bots + continue; + } + + if ( !IsDefined( player.pers[level.clientDataKey] ) ) + { + player.pers[level.clientDataKey] = spawnstruct(); + } + + player thread OnPlayerSpawned(); + player thread OnPlayerJoinedTeam(); + player thread OnPlayerJoinedSpectators(); + player thread PlayerTrackingOnInterval(); + } +} + +OnPlayerSpawned() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + self waittill( "spawned_player" ); + self PlayerSpawnEvents(); + } +} + +OnPlayerDisconnect() +{ + self endon ( "disconnect" ); + + for ( ;; ) + { + self waittill( "disconnect" ); + self SaveTrackingMetrics(); + } +} + +OnPlayerJoinedTeam() +{ + self endon( "disconnect" ); + + for( ;; ) + { + self waittill( "joined_team" ); + // join spec and join team occur at the same moment - out of order logging would be problematic + wait( 0.25 ); + LogPrint( GenerateJoinTeamString( false ) ); + } +} + +OnPlayerJoinedSpectators() +{ + self endon( "disconnect" ); + + for( ;; ) + { + self waittill( "joined_spectators" ); + LogPrint( GenerateJoinTeamString( true ) ); + } +} + +OnGameEnded() +{ + for ( ;; ) + { + level waittill( "game_ended" ); + // note: you can run data code here but it's possible for + // data to get truncated, so we will try a timer based approach for now + } +} + +DisplayWelcomeData() +{ + self endon( "disconnect" ); + + clientData = self.pers[level.clientDataKey]; + + if ( clientData.permissionLevel == "User" || clientData.permissionLevel == "Flagged" ) + { + return; + } + + self IPrintLnBold( "Welcome, your level is ^5" + clientData.permissionLevel ); + wait( 2.0 ); + self IPrintLnBold( "You were last seen ^5" + clientData.lastConnection ); +} + +PlayerSpawnEvents() +{ + self endon( "disconnect" ); + + clientData = self.pers[level.clientDataKey]; + + // this gives IW4MAdmin some time to register the player before making the request; + // although probably not necessary some users might have a slow database or poll rate + wait ( 2 ); + + if ( IsDefined( clientData.state ) && clientData.state == "complete" ) + { + return; + } + + self RequestClientBasicData(); + // example of requesting meta from IW4MAdmin + // self RequestClientMeta( "LastServerPlayed" ); +} + +PlayerTrackingOnInterval() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + wait ( 120 ); + if ( IsAlive( self ) ) + { + self SaveTrackingMetrics(); + } + } +} + +MonitorClientEvents() +{ + level endon( "game_ended" ); + + for ( ;; ) + { + level waittill( level.eventTypes.localClientEvent, client ); + + LogDebug( "Processing Event " + client.event.type + "-" + client.event.subtype ); + + eventHandler = level.eventCallbacks[client.event.type]; + + if ( IsDefined( eventHandler ) ) + { + client [[eventHandler]]( client.event ); + LogDebug( "notify client for " + client.event.type ); + client notify( level.eventTypes.localClientEvent, client.event ); + } + + client.eventData = []; + } +} + +////////////////////////////////// +// Helper Methods +////////////////////////////////// + +_IsBot( entity ) +{ + // there already is a cgame function exists as "IsBot", for IW4, but unsure what all titles have it defined, + // so we are defining it here + return IsDefined( entity.pers["isBot"] ) && entity.pers["isBot"]; +} + +_SetDvarIfUninitialized( dvarName, dvarValue ) +{ + [[level.overrideMethods[level.commonFunctions.SetDvar]]]( dvarName, dvarValue ); +} + +// Not every game can output to console or even game log. +// Adds a very basic logging system that every +// game specific script can extend.accumulate +// Logging to dvars used as example. +InitializeLogger() +{ + level.logger._logger = []; + RegisterLogger( ::Log2Dvar ); + RegisterLogger( ::Log2IngamePrint ); + level.logger.debug = ::LogDebug; + level.logger.error = ::LogError; + level.logger.warning = ::LogWarning; +} + +_Log( LogLevel, message ) +{ + for( i = 0; i < level.logger._logger.size; i++ ) + { + [[level.logger._logger[i]]]( LogLevel, message ); + } +} + +LogDebug( message ) +{ + if ( level.iw4madminIntegrationDebug ) + { + _Log( "debug", level.eventBus.gamename + ": " + message ); + } +} + +LogError( message ) +{ + _Log( "error", message ); +} + +LogWarning( message ) +{ + _Log( "warning", message ); +} + +Log2Dvar( LogLevel, message ) +{ + switch ( LogLevel ) + { + case "debug": + SetDvar( "sv_iw4madmin_last_debug", message ); + break; + case "error": + SetDvar( "sv_iw4madmin_last_error", message ); + break; + case "warning": + SetDvar( "sv_iw4madmin_last_warning", message ); + break; + } +} + +Log2IngamePrint( LogLevel, message ) +{ + switch ( LogLevel ) + { + case "debug": + IPrintLn( "[DEBUG] " + message ); + break; + case "error": + IPrintLn( "[ERROR] " + message ); + break; + case "warning": + IPrintLn( "[WARN] " + message ); + break; + } +} + +RegisterLogger( logger ) +{ + level.logger._logger[level.logger._logger.size] = logger; +} + +RequestClientMeta( metaKey ) +{ + getClientMetaEvent = BuildEventRequest( true, level.eventTypes.clientDataRequested, "Meta", self, metaKey ); + level thread QueueEvent( getClientMetaEvent, level.eventTypes.clientDataRequested, self ); +} + +RequestClientBasicData() +{ + getClientDataEvent = BuildEventRequest( true, level.eventTypes.clientDataRequested, "None", self, "" ); + level thread QueueEvent( getClientDataEvent, level.eventTypes.clientDataRequested, self ); +} + +IncrementClientMeta( metaKey, incrementValue, clientId ) +{ + SetClientMeta( metaKey, incrementValue, clientId, "increment" ); +} + +DecrementClientMeta( metaKey, decrementValue, clientId ) +{ + SetClientMeta( metaKey, decrementValue, clientId, "decrement" ); +} + +GenerateJoinTeamString( isSpectator ) +{ + team = self.team; + + if ( IsDefined( self.joining_team ) ) + { + team = self.joining_team; + } + else + { + if ( isSpectator || !IsDefined( team ) ) + { + team = "spectator"; + } + } + + guid = self GetXuid(); + + if ( guid == "0" ) + { + guid = self.guid; + } + + if ( !IsDefined( guid ) || guid == "0" ) + { + guid = "undefined"; + } + + return "JT;" + guid + ";" + self getEntityNumber() + ";" + team + ";" + self.name + "\n"; +} + +SetClientMeta( metaKey, metaValue, clientId, direction ) +{ + data = "key=" + metaKey + "|value=" + metaValue; + clientNumber = -1; + + if ( IsDefined ( clientId ) ) + { + data = data + "|clientId=" + clientId; + clientNumber = -1; + } + + if ( IsDefined( direction ) ) + { + data = data + "|direction=" + direction; + } + + if ( IsPlayer( self ) ) + { + clientNumber = self getEntityNumber(); + } + + setClientMetaEvent = BuildEventRequest( true, level.eventTypes.setClientDataRequested, "Meta", clientNumber, data ); + level thread QueueEvent( setClientMetaEvent, level.eventTypes.setClientDataRequested, self ); +} + +SaveTrackingMetrics() +{ + if ( !IsDefined( self.persistentClientId ) ) + { + return; + } + + LogDebug( "Saving tracking metrics for " + self.persistentClientId ); + + if ( !IsDefined( self.lastShotCount ) ) + { + self.lastShotCount = 0; + } + + currentShotCount = self [[level.overrideMethods["GetTotalShotsFired"]]](); + change = currentShotCount - self.lastShotCount; + self.lastShotCount = currentShotCount; + + LogDebug( "Total Shots Fired increased by " + change ); + + if ( !IsDefined( change ) ) + { + change = 0; + } + + if ( change == 0 ) + { + return; + } + + IncrementClientMeta( "TotalShotsFired", change, self.persistentClientId ); + +} + +BuildEventRequest( responseExpected, eventType, eventSubtype, entOrId, data ) +{ + if ( !IsDefined( data ) ) + { + data = ""; + } + + if ( !IsDefined( eventSubtype ) ) + { + eventSubtype = "None"; + } + + if ( IsPlayer( entOrId ) ) + { + entOrId = entOrId getEntityNumber(); + } + + request = "0"; + + if ( responseExpected ) + { + request = "1"; + } + + request = request + ";" + eventType + ";" + eventSubtype + ";" + entOrId + ";" + data; + return request; +} + +MonitorBus() +{ + level endon( "game_ended" ); + + for( ;; ) + { + wait ( 0.1 ); + + // check to see if IW4MAdmin is ready to receive more data + if ( getDvar( level.eventBus.inVar ) == "" ) + { + level notify( "bus_ready" ); + } + + eventString = getDvar( level.eventBus.outVar ); + + if ( eventString == "" ) + { + continue; + } + LogDebug( "-> " + eventString ); + + NotifyClientEvent( strtok( eventString, ";" ) ); + + SetDvar( level.eventBus.outVar, "" ); + } +} + +QueueEvent( request, eventType, notifyEntity ) +{ + level endon( "game_ended" ); + + start = GetTime(); + maxWait = level.eventBus.timeout * 1000; // 30 seconds + timedOut = ""; + + while ( GetDvar( level.eventBus.inVar ) != "" && ( GetTime() - start ) < maxWait ) + { + level [[level.overrideMethods["waittill_notify_or_timeout"]]]( "bus_ready", 1 ); + + if ( GetDvar( level.eventBus.inVar ) != "" ) + { + LogDebug( "A request is already in progress..." ); + timedOut = "set"; + continue; + } + + timedOut = "unset"; + } + + if ( timedOut == "set") + { + LogDebug( "Timed out waiting for response..." ); + + if ( IsDefined( notifyEntity ) ) + { + notifyEntity NotifyClientEventTimeout( eventType ); + } + + SetDvar( level.eventBus.inVar, "" ); + + return; + } + + LogDebug("<- " + request ); + + SetDvar( level.eventBus.inVar, request ); +} + +ParseDataString( data ) +{ + if ( !IsDefined( data ) ) + { + LogDebug( "No data to parse" ); + return []; + } + + dataParts = strtok( data, "|" ); + dict = []; + + for ( i = 0; i < dataParts.size; i++ ) + { + part = dataParts[i]; + splitPart = strtok( part, "=" ); + key = splitPart[0]; + value = splitPart[1]; + dict[key] = value; + dict[i] = key; + } + + return dict; +} + +NotifyClientEventTimeout( eventType ) +{ + // todo: make this actual eventing + if ( eventType == level.eventTypes.clientDataRequested ) + { + self.pers["clientData"].state = level.eventBus.timeoutKey; + } +} + +NotifyClientEvent( eventInfo ) +{ + origin = getPlayerFromClientNum( int( eventInfo[3] ) ); + target = getPlayerFromClientNum( int( eventInfo[4] ) ); + + event = spawnstruct(); + event.type = eventInfo[1]; + event.subtype = eventInfo[2]; + event.data = eventInfo[5]; + event.origin = origin; + event.target = target; + + LogDebug( "NotifyClientEvent->" + event.data ); + if ( int( eventInfo[3] ) != -1 && !IsDefined( origin ) ) + { + LogDebug( "origin is null but the slot id is " + int( eventInfo[3] ) ); + } + if ( int( eventInfo[4] ) != -1 && !IsDefined( target ) ) + { + LogDebug( "target is null but the slot id is " + int( eventInfo[4] ) ); + } + + if ( IsDefined( target ) ) + { + client = event.target; + } + else if ( IsDefined( origin ) ) + { + client = event.origin; + } + else + { + LogDebug( "Neither origin or target are set but we are a Client Event, aborting" ); + + return; + } + + client.event = event; + level notify( level.eventTypes.localClientEvent, client ); +} + +GetPlayerFromClientNum( clientNum ) +{ + if ( clientNum < 0 ) + { + return undefined; + } + + for ( i = 0; i < level.players.size; i++ ) + { + if ( level.players[i] getEntityNumber() == clientNum ) + { + return level.players[i]; + } + } + + return undefined; +} + +AddClientCommand( commandName, shouldRunAsTarget, callback, shouldOverwrite ) +{ + if ( IsDefined( level.clientCommandCallbacks[commandName] ) && IsDefined( shouldOverwrite ) && !shouldOverwrite ) + { + return; + } + + level.clientCommandCallbacks[commandName] = callback; + level.clientCommandRusAsTarget[commandName] = shouldRunAsTarget == true; //might speed up things later in case someone gives us a string or number instead of a boolean +} + +////////////////////////////////// +// Event Handlers +///////////////////////////////// + +OnClientDataReceived( event ) +{ + event.data = ParseDataString( event.data ); + clientData = self.pers[level.clientDataKey]; + + if ( event.subtype == "Fail" ) + { + LogDebug( "Received fail response" ); + clientData.state = level.eventBus.failKey; + return; + } + + if ( event.subtype == "Meta" ) + { + if ( !IsDefined( clientData.meta ) ) + { + clientData.meta = []; + } + + metaKey = event.data[0]; + clientData.meta[metaKey] = event.data[metaKey]; + + return; + } + + clientData.permissionLevel = event.data["level"]; + clientData.clientId = event.data["clientId"]; + clientData.lastConnection = event.data["lastConnection"]; + clientData.state = "complete"; + self.persistentClientId = event.data["clientId"]; + + self thread DisplayWelcomeData(); +} + +OnExecuteCommand( event ) +{ + data = ParseDataString( event.data ); + response = ""; + + command = level.clientCommandCallbacks[event.subtype]; + runAsTarget = level.clientCommandRusAsTarget[event.subtype]; + executionContextEntity = event.origin; + + if ( runAsTarget ) + { + executionContextEntity = event.target; + } + + if ( IsDefined( command ) ) + { + response = executionContextEntity [[command]]( event, data ); + } + else + { + LogDebug( "Unknown Client command->" + event.subtype ); + } + + // send back the response to the origin, but only if they're not the target + if ( response != "" && IsPlayer( event.origin ) && event.origin != event.target ) + { + event.origin IPrintLnBold( response ); + } +} + +OnSetClientDataCompleted( event ) +{ + // IW4MAdmin let us know it persisted (success or fail) + LogDebug( "Set Client Data -> subtype = " + event.subType + " status = " + event.data["status"] ); +} diff --git a/GameFiles/GameInterface/_integration_iw4x.gsc b/GameFiles/GameInterface/_integration_iw4x.gsc new file mode 100644 index 000000000..87af86e6a --- /dev/null +++ b/GameFiles/GameInterface/_integration_iw4x.gsc @@ -0,0 +1,482 @@ +#include common_scripts\iw4x_utility; + +Init() +{ + level.eventBus.gamename = "IW4"; + + level thread Setup(); +} + +Setup() +{ + level endon( "game_ended" ); + + // it's possible that the notify type has not been defined yet so we have to hard code it + level waittill( "IntegrationBootstrapInitialized" ); + + scripts\_integration_base::RegisterLogger( ::Log2Console ); + + level.overrideMethods["GetTotalShotsFired"] = ::GetTotalShotsFired; + level.overrideMethods["SetDvarIfUninitialized"] = ::_SetDvarIfUninitialized; + level.overrideMethods["waittill_notify_or_timeout"] = ::_waittill_notify_or_timeout; + + RegisterClientCommands(); + + level notify( level.notifyTypes.gameFunctionsInitialized ); + + if ( GetDvarInt( "sv_iw4madmin_integration_enabled" ) != 1 ) + { + return; + } + + level thread OnPlayerConnect(); +} + +OnPlayerConnect() +{ + level endon ( "game_ended" ); + + for ( ;; ) + { + level waittill( "connected", player ); + + if ( scripts\_integration_base::_IsBot( player ) ) + { + // we don't want to track bots + continue; + } + + player thread SetPersistentData(); + } +} + +RegisterClientCommands() +{ + scripts\_integration_base::AddClientCommand( "GiveWeapon", true, ::GiveWeaponImpl ); + scripts\_integration_base::AddClientCommand( "TakeWeapons", true, ::TakeWeaponsImpl ); + scripts\_integration_base::AddClientCommand( "SwitchTeams", true, ::TeamSwitchImpl ); + scripts\_integration_base::AddClientCommand( "Hide", false, ::HideImpl ); + scripts\_integration_base::AddClientCommand( "Unhide", false, ::UnhideImpl ); + scripts\_integration_base::AddClientCommand( "Alert", true, ::AlertImpl ); + scripts\_integration_base::AddClientCommand( "Goto", false, ::GotoImpl ); + scripts\_integration_base::AddClientCommand( "Kill", true, ::KillImpl ); + scripts\_integration_base::AddClientCommand( "SetSpectator", true, ::SetSpectatorImpl ); + scripts\_integration_base::AddClientCommand( "LockControls", true, ::LockControlsImpl ); + scripts\_integration_base::AddClientCommand( "UnlockControls", true, ::UnlockControlsImpl ); + scripts\_integration_base::AddClientCommand( "PlayerToMe", true, ::PlayerToMeImpl ); + scripts\_integration_base::AddClientCommand( "NoClip", false, ::NoClipImpl ); +} + +GetTotalShotsFired() +{ + return maps\mp\_utility::getPlayerStat( "mostshotsfired" ); +} + +_SetDvarIfUninitialized( dvar, value ) +{ + SetDvarIfUninitialized( dvar, value ); +} + +_waittill_notify_or_timeout( _notify, timeout ) +{ + common_scripts\utility::waittill_notify_or_timeout( _notify, timeout ); +} + +Log2Console( logLevel, message ) +{ + PrintConsole( "[" + logLevel + "] " + message + "\n" ); +} + +////////////////////////////////// +// GUID helpers +///////////////////////////////// + +SetPersistentData() +{ + guidHigh = self GetPlayerData( "bests", "none" ); + guidLow = self GetPlayerData( "awards", "none" ); + persistentGuid = guidHigh + "," + guidLow; + guidIsStored = guidHigh != 0 && guidLow != 0; + + if ( guidIsStored ) + { + scripts\_integration_base::LogDebug( "Uploading persistent guid " + persistentGuid ); + scripts\_integration_base::SetClientMeta( "PersistentClientGuid", persistentGuid ); + return; + } + + guid = self SplitGuid(); + + scripts\_integration_base::LogDebug( "Persisting client guid " + guidHigh + "," + guidLow ); + + self SetPlayerData( "bests", "none", guid["high"] ); + self SetPlayerData( "awards", "none", guid["low"] ); +} + +SplitGuid() +{ + guid = self GetGuid(); + + if ( isDefined( self.guid ) ) + { + guid = self.guid; + } + + firstPart = 0; + secondPart = 0; + stringLength = 17; + firstPartExp = 0; + secondPartExp = 0; + + for ( i = stringLength - 1; i > 0; i-- ) + { + char = GetSubStr( guid, i - 1, i ); + if ( char == "" ) + { + char = "0"; + } + + if ( i > stringLength / 2 ) + { + value = GetIntForHexChar( char ); + power = Pow( 16, secondPartExp ); + secondPart = secondPart + ( value * power ); + secondPartExp++; + } + else + { + value = GetIntForHexChar( char ); + power = Pow( 16, firstPartExp ); + firstPart = firstPart + ( value * power ); + firstPartExp++; + } + } + + split = []; + split["low"] = int( secondPart ); + split["high"] = int( firstPart ); + + return split; +} + +Pow( num, exponent ) +{ + result = 1; + while( exponent != 0 ) + { + result = result * num; + exponent--; + } + + return result; +} + +GetIntForHexChar( char ) +{ + char = ToLower( char ); + // generated by co-pilot because I can't be bothered to make it more "elegant" + switch( char ) + { + case "0": + return 0; + case "1": + return 1; + case "2": + return 2; + case "3": + return 3; + case "4": + return 4; + case "5": + return 5; + case "6": + return 6; + case "7": + return 7; + case "8": + return 8; + case "9": + return 9; + case "a": + return 10; + case "b": + return 11; + case "c": + return 12; + case "d": + return 13; + case "e": + return 14; + case "f": + return 15; + default: + return 0; + } +} + +////////////////////////////////// +// Command Implementations +///////////////////////////////// + +GiveWeaponImpl( event, data ) +{ + if ( !IsAlive( self ) ) + { + return self.name + "^7 is not alive"; + } + + self IPrintLnBold( "You have been given a new weapon" ); + self GiveWeapon( data["weaponName"] ); + self SwitchToWeapon( data["weaponName"] ); + + return self.name + "^7 has been given ^5" + data["weaponName"]; +} + +TakeWeaponsImpl() +{ + if ( !IsAlive( self ) ) + { + return self.name + "^7 is not alive"; + } + + self TakeAllWeapons(); + self IPrintLnBold( "All your weapons have been taken" ); + + return "Took weapons from " + self.name; +} + +TeamSwitchImpl() +{ + if ( !IsAlive( self ) ) + { + return self + "^7 is not alive"; + } + + team = level.allies; + + if ( self.team == "allies" ) + { + team = level.axis; + } + + self IPrintLnBold( "You are being team switched" ); + wait( 2 ); + self [[team]](); + + return self.name + "^7 switched to " + self.team; +} + +LockControlsImpl() +{ + if ( !IsAlive( self ) ) + { + return self.name + "^7 is not alive"; + } + + + self freezeControls( true ); + self God(); + self Hide(); + + info = []; + info[ "alertType" ] = "Alert!"; + info[ "message" ] = "You have been frozen!"; + + self AlertImpl( undefined, info ); + + return self.name + "\'s controls are locked"; +} + +UnlockControlsImpl() +{ + if ( !IsAlive( self ) ) + { + return self.name + "^7 is not alive"; + } + + self freezeControls( false ); + self God(); + self Show(); + + return self.name + "\'s controls are unlocked"; +} + +NoClipImpl() +{ + if ( !IsAlive( self ) ) + { + self IPrintLnBold( "You are not alive" ); + } + + if ( !IsDefined ( self.isNoClipped ) ) + { + self.isNoClipped = false; + } + + if ( !self.isNoClipped ) + { + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 1 ); + self SetClientDvar( "sv_cheats", 0 ); + + self God(); + self Noclip(); + self Hide(); + + self.isNoClipped = true; + + self IPrintLnBold( "NoClip enabled" ); + } + else + { + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 1 ); + self SetClientDvar( "sv_cheats", 0 ); + + self God(); + self Noclip(); + self Hide(); + + self.isNoClipped = false; + + self IPrintLnBold( "NoClip disabled" ); + } + + self IPrintLnBold( "NoClip enabled" ); +} + +HideImpl() +{ + if ( !IsAlive( self ) ) + { + self IPrintLnBold( "You are not alive" ); + return; + } + + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 1 ); + self SetClientDvar( "sv_cheats", 0 ); + + if ( !IsDefined( self.savedHealth ) || self.health < 1000 ) + { + self.savedHealth = self.health; + self.savedMaxHealth = self.maxhealth; + } + + self God(); + self Hide(); + + self.isHidden = true; + + self IPrintLnBold( "You are now ^5hidden ^7from other players" ); +} + +UnhideImpl() +{ + if ( !IsAlive( self ) ) + { + self IPrintLnBold( "You are not alive" ); + return; + } + + if ( !IsDefined( self.isHidden ) || !self.isHidden ) + { + self IPrintLnBold( "You are not hidden" ); + return; + } + + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 0 ); + self SetClientDvar( "sv_cheats", 0 ); + + self God(); + self Show(); + + self.isHidden = false; + + self IPrintLnBold( "You are now ^5visible ^7to other players" ); +} + +AlertImpl( event, data ) +{ + if ( level.eventBus.gamename == "IW4" ) + { + self thread maps\mp\gametypes\_hud_message::oldNotifyMessage( data["alertType"], data["message"], "compass_waypoint_target", ( 1, 0, 0 ), "ui_mp_nukebomb_timer", 7.5 ); + } + + return "Sent alert to " + self.name; +} + +GotoImpl( event, data ) +{ + if ( IsDefined( event.target ) ) + { + return self GotoPlayerImpl( event.target ); + } + else + { + return self GotoCoordImpl( data ); + } +} + +GotoCoordImpl( data ) +{ + if ( !IsAlive( self ) ) + { + self IPrintLnBold( "You are not alive" ); + return; + } + + position = ( int( data["x"] ), int( data["y"] ), int( data["z"]) ); + self SetOrigin( position ); + self IPrintLnBold( "Moved to " + "("+ position[0] + "," + position[1] + "," + position[2] + ")" ); +} + +GotoPlayerImpl( target ) +{ + if ( !IsAlive( target ) ) + { + self IPrintLnBold( target.name + " is not alive" ); + return; + } + + self SetOrigin( target GetOrigin() ); + self IPrintLnBold( "Moved to " + target.name ); +} + +PlayerToMeImpl( event ) +{ + if ( !IsAlive( self ) ) + { + return self.name + " is not alive"; + } + + self SetOrigin( event.origin GetOrigin() ); + return "Moved here " + self.name; +} + +KillImpl() +{ + if ( !IsAlive( self ) ) + { + return self.name + " is not alive"; + } + + self Suicide(); + self IPrintLnBold( "You were killed by " + self.name ); + + return "You killed " + self.name; +} + +SetSpectatorImpl() +{ + if ( self.pers["team"] == "spectator" ) + { + return self.name + " is already spectating"; + } + + self [[level.spectator]](); + self IPrintLnBold( "You have been moved to spectator" ); + + return self.name + " has been moved to spectator"; +} diff --git a/GameFiles/_integration.gsc b/GameFiles/_integration.gsc deleted file mode 100644 index 9bb55c292..000000000 --- a/GameFiles/_integration.gsc +++ /dev/null @@ -1,1172 +0,0 @@ -#include common_scripts\utility; -#include maps\mp\_utility; -#include maps\mp\gametypes\_hud_util; - -init() -{ - // setup default vars - level.eventBus = spawnstruct(); - level.eventBus.inVar = "sv_iw4madmin_in"; - level.eventBus.outVar = "sv_iw4madmin_out"; - level.eventBus.failKey = "fail"; - level.eventBus.timeoutKey = "timeout"; - level.eventBus.timeout = 30; - level.eventBus.gamename = getDvar( "gamename" ); // We want to do a few small detail different on IW5 compared to IW4, nothing where 2 files would make sense. - - level.clientDataKey = "clientData"; - - level.eventTypes = spawnstruct(); - level.eventTypes.localClientEvent = "client_event"; - level.eventTypes.clientDataReceived = "ClientDataReceived"; - level.eventTypes.clientDataRequested = "ClientDataRequested"; - level.eventTypes.setClientDataRequested = "SetClientDataRequested"; - level.eventTypes.setClientDataCompleted = "SetClientDataCompleted"; - level.eventTypes.executeCommandRequested = "ExecuteCommandRequested"; - - level.iw4adminIntegrationDebug = false; - - SetDvarIfUninitialized( level.eventBus.inVar, "" ); - SetDvarIfUninitialized( level.eventBus.outVar, "" ); - SetDvarIfUninitialized( "sv_iw4madmin_integration_enabled", 1 ); - SetDvarIfUninitialized( "sv_iw4madmin_integration_debug", 0 ); - - // map the event type to the handler - level.eventCallbacks = []; - level.eventCallbacks[level.eventTypes.clientDataReceived] = ::OnClientDataReceived; - level.eventCallbacks[level.eventTypes.executeCommandRequested] = ::OnExecuteCommand; - level.eventCallbacks[level.eventTypes.setClientDataCompleted] = ::OnSetClientDataCompleted; - - level.clientCommandCallbacks = []; - level.clientCommandRusAsTarget = []; - - if ( GetDvarInt( "sv_iw4madmin_integration_enabled" ) != 1 ) - { - return; - } - - InitializeGameMethods(); - RegisterClientCommands(); - - // start long running tasks - level thread MonitorClientEvents(); - level thread MonitorBus(); - level thread OnPlayerConnect(); -} - -////////////////////////////////// -// Client Methods -////////////////////////////////// - -OnPlayerConnect() -{ - level endon ( "game_ended" ); - - for ( ;; ) - { - level waittill( "connected", player ); - - level.iw4adminIntegrationDebug = GetDvarInt( "sv_iw4madmin_integration_debug" ); - - if ( isDefined( player.pers["isBot"] ) && player.pers["isBot"] ) - { - // we don't want to track bots - continue; - } - - if ( !isDefined( player.pers[level.clientDataKey] ) ) - { - player.pers[level.clientDataKey] = spawnstruct(); - } - - player thread OnPlayerSpawned(); - player thread OnPlayerJoinedTeam(); - player thread OnPlayerJoinedSpectators(); - player thread PlayerTrackingOnInterval(); - - // only toggle if it's enabled - if ( IsDefined( level.nightModeEnabled ) && level.nightModeEnabled ) - { - player ToggleNightMode(); - } - } -} - -OnPlayerSpawned() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - self waittill( "spawned_player" ); - self PlayerConnectEvents(); - } -} - -OnPlayerDisconnect() -{ - self endon ( "disconnect" ); - - for ( ;; ) - { - self waittill( "disconnect" ); - self SaveTrackingMetrics(); - } -} - -OnPlayerJoinedTeam() -{ - self endon( "disconnect" ); - - for( ;; ) - { - self waittill( "joined_team" ); - // join spec and join team occur at the same moment - out of order logging would be problematic - wait( 0.25 ); - LogPrint( GenerateJoinTeamString( false ) ); - } -} - -OnPlayerJoinedSpectators() -{ - self endon( "disconnect" ); - - for( ;; ) - { - self waittill( "joined_spectators" ); - LogPrint( GenerateJoinTeamString( true ) ); - } -} - -OnGameEnded() -{ - for ( ;; ) - { - level waittill( "game_ended" ); - // note: you can run data code here but it's possible for - // data to get truncated, so we will try a timer based approach for now - } -} - -DisplayWelcomeData() -{ - self endon( "disconnect" ); - - clientData = self.pers[level.clientDataKey]; - - if ( clientData.permissionLevel == "User" || clientData.permissionLevel == "Flagged" ) - { - return; - } - - self IPrintLnBold( "Welcome, your level is ^5" + clientData.permissionLevel ); - wait( 2.0 ); - self IPrintLnBold( "You were last seen ^5" + clientData.lastConnection ); -} - -SetPersistentData() -{ - guidHigh = self GetPlayerData( "bests", "none" ); - guidLow = self GetPlayerData( "awards", "none" ); - persistentGuid = guidHigh + "," + guidLow; - - if ( guidHigh != 0 && guidLow != 0) - { - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Uploading persistent guid " + persistentGuid ); - } - - SetClientMeta( "PersistentClientGuid", persistentGuid ); - } - - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Persisting client guid " + persistentGuid ); - } - - guid = self SplitGuid(); - - self SetPlayerData( "bests", "none", guid["high"] ); - self SetPlayerData( "awards", "none", guid["low"] ); -} - -PlayerConnectEvents() -{ - self endon( "disconnect" ); - - if ( IsDefined( self.isHidden ) && self.isHidden ) - { - self HideImpl(); - } - - clientData = self.pers[level.clientDataKey]; - - // this gives IW4MAdmin some time to register the player before making the request; - // although probably not necessary some users might have a slow database or poll rate - wait ( 2 ); - - if ( isDefined( clientData.state ) && clientData.state == "complete" ) - { - return; - } - - self RequestClientBasicData(); - // example of requesting meta from IW4MAdmin - // self RequestClientMeta( "LastServerPlayed" ); -} - -PlayerTrackingOnInterval() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - wait ( 120 ); - if ( IsAlive( self ) ) - { - self SaveTrackingMetrics(); - } - } -} - -MonitorClientEvents() -{ - level endon( "game_ended" ); - - for ( ;; ) - { - level waittill( level.eventTypes.localClientEvent, client ); - - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Processing Event " + client.event.type + "-" + client.event.subtype ); - } - - eventHandler = level.eventCallbacks[client.event.type]; - - if ( isDefined( eventHandler ) ) - { - client [[eventHandler]]( client.event ); - } - - client.eventData = []; - } -} - -////////////////////////////////// -// Helper Methods -////////////////////////////////// - -RegisterClientCommands() -{ - AddClientCommand( "GiveWeapon", true, ::GiveWeaponImpl ); - AddClientCommand( "TakeWeapons", true, ::TakeWeaponsImpl ); - AddClientCommand( "SwitchTeams", true, ::TeamSwitchImpl ); - AddClientCommand( "Hide", false, ::HideImpl ); - AddClientCommand( "Unhide", false, ::UnhideImpl ); - AddClientCommand( "Alert", true, ::AlertImpl ); - AddClientCommand( "Goto", false, ::GotoImpl ); - AddClientCommand( "Kill", true, ::KillImpl ); - AddClientCommand( "SetSpectator", true, ::SetSpectatorImpl ); - AddClientCommand( "NightMode", false, ::NightModeImpl ); //This really should be a level command - AddClientCommand( "LockControls", true, ::LockControlsImpl ); - AddClientCommand( "UnlockControls", true, ::UnlockControlsImpl ); - AddClientCommand( "PlayerToMe", true, ::PlayerToMeImpl ); - AddClientCommand( "NoClip", false, ::NoClipImpl ); - AddClientCommand( "NoClipOff", false, ::NoClipOffImpl ); -} - -InitializeGameMethods() -{ - level.overrideMethods = []; - level.overrideMethods["god"] = ::_god; - level.overrideMethods["noclip"] = ::UnsupportedFunc; - - if ( isDefined( ::God ) ) - { - level.overrideMethods["god"] = ::God; - } - - if ( isDefined( ::NoClip ) ) - { - level.overrideMethods["noclip"] = ::NoClip; - } - - if ( level.eventBus.gamename == "IW5" ) - { //PlutoIW5 only allows Godmode and NoClip if cheats are on.. - level.overrideMethods["god"] = ::IW5_God; - level.overrideMethods["noclip"] = ::IW5_NoClip; - } -} - -UnsupportedFunc() -{ - self IPrintLnBold( "Function is not supported!" ); -} - -RequestClientMeta( metaKey ) -{ - getClientMetaEvent = BuildEventRequest( true, level.eventTypes.clientDataRequested, "Meta", self, metaKey ); - level thread QueueEvent( getClientMetaEvent, level.eventTypes.clientDataRequested, self ); -} - -RequestClientBasicData() -{ - getClientDataEvent = BuildEventRequest( true, level.eventTypes.clientDataRequested, "None", self, "" ); - level thread QueueEvent( getClientDataEvent, level.eventTypes.clientDataRequested, self ); -} - -IncrementClientMeta( metaKey, incrementValue, clientId ) -{ - SetClientMeta( metaKey, incrementValue, clientId, "increment" ); -} - -DecrementClientMeta( metaKey, decrementValue, clientId ) -{ - SetClientMeta( metaKey, decrementValue, clientId, "decrement" ); -} - -SplitGuid() -{ - guid = self GetGuid(); - - if ( isDefined( self.guid ) ) - { - guid = self.guid; - } - - firstPart = 0; - secondPart = 0; - stringLength = 17; - firstPartExp = 0; - secondPartExp = 0; - - for ( i = stringLength - 1; i > 0; i-- ) - { - char = GetSubStr( guid, i - 1, i ); - if ( char == "" ) - { - char = "0"; - } - - if ( i > stringLength / 2 ) - { - value = GetIntForHexChar( char ); - power = Pow( 16, secondPartExp ); - secondPart = secondPart + ( value * power ); - secondPartExp++; - } - else - { - value = GetIntForHexChar( char ); - power = Pow( 16, firstPartExp ); - firstPart = firstPart + ( value * power ); - firstPartExp++; - } - } - - split = []; - split["low"] = int( secondPart ); - split["high"] = int( firstPart ); - - return split; -} - -Pow( num, exponent ) -{ - result = 1; - while( exponent != 0 ) - { - result = result * num; - exponent--; - } - - return result; -} - -GetIntForHexChar( char ) -{ - char = ToLower( char ); - // generated by co-pilot because I can't be bothered to make it more "elegant" - switch( char ) - { - case "0": - return 0; - case "1": - return 1; - case "2": - return 2; - case "3": - return 3; - case "4": - return 4; - case "5": - return 5; - case "6": - return 6; - case "7": - return 7; - case "8": - return 8; - case "9": - return 9; - case "a": - return 10; - case "b": - return 11; - case "c": - return 12; - case "d": - return 13; - case "e": - return 14; - case "f": - return 15; - default: - return 0; - } -} - -GenerateJoinTeamString( isSpectator ) -{ - team = self.team; - - if ( IsDefined( self.joining_team ) ) - { - team = self.joining_team; - } - else - { - if ( isSpectator || !IsDefined( team ) ) - { - team = "spectator"; - } - } - - guid = self GetXuid(); - - if ( guid == "0" ) - { - guid = self.guid; - } - - if ( !IsDefined( guid ) || guid == "0" ) - { - guid = "undefined"; - } - - return "JT;" + guid + ";" + self getEntityNumber() + ";" + team + ";" + self.name + "\n"; -} - -SetClientMeta( metaKey, metaValue, clientId, direction ) -{ - data = "key=" + metaKey + "|value=" + metaValue; - clientNumber = -1; - - if ( IsDefined ( clientId ) ) - { - data = data + "|clientId=" + clientId; - clientNumber = -1; - } - - if ( IsDefined( direction ) ) - { - data = data + "|direction=" + direction; - } - - if ( IsPlayer( self ) ) - { - clientNumber = self getEntityNumber(); - } - - setClientMetaEvent = BuildEventRequest( true, level.eventTypes.setClientDataRequested, "Meta", clientNumber, data ); - level thread QueueEvent( setClientMetaEvent, level.eventTypes.setClientDataRequested, self ); -} - -SaveTrackingMetrics() -{ - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Saving tracking metrics for " + self.persistentClientId ); - } - - if ( !IsDefined( self.lastShotCount ) ) - { - self.lastShotCount = 0; - } - - currentShotCount = self getPlayerStat( "mostshotsfired" ); - change = currentShotCount - self.lastShotCount; - self.lastShotCount = currentShotCount; - - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Total Shots Fired increased by " + change ); - } - - if ( !IsDefined( change ) ) - { - change = 0; - } - - if ( change == 0 ) - { - return; - } - - IncrementClientMeta( "TotalShotsFired", change, self.persistentClientId ); - -} - -BuildEventRequest( responseExpected, eventType, eventSubtype, entOrId, data ) -{ - if ( !isDefined( data ) ) - { - data = ""; - } - - if ( !isDefined( eventSubtype ) ) - { - eventSubtype = "None"; - } - - if ( IsPlayer( entOrId ) ) - { - entOrId = entOrId getEntityNumber(); - } - - request = "0"; - - if ( responseExpected ) - { - request = "1"; - } - - request = request + ";" + eventType + ";" + eventSubtype + ";" + entOrId + ";" + data; - return request; -} - -MonitorBus() -{ - level endon( "game_ended" ); - - for( ;; ) - { - wait ( 0.1 ); - - // check to see if IW4MAdmin is ready to receive more data - if ( getDvar( level.eventBus.inVar ) == "" ) - { - level notify( "bus_ready" ); - } - - eventString = getDvar( level.eventBus.outVar ); - - if ( eventString == "" ) - { - continue; - } - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "-> " + eventString ); - } - - NotifyClientEvent( strtok( eventString, ";" ) ); - - SetDvar( level.eventBus.outVar, "" ); - } -} - -QueueEvent( request, eventType, notifyEntity ) -{ - level endon( "game_ended" ); - - start = GetTime(); - maxWait = level.eventBus.timeout * 1000; // 30 seconds - timedOut = ""; - - while ( GetDvar( level.eventBus.inVar ) != "" && ( GetTime() - start ) < maxWait ) - { - level waittill_notify_or_timeout( "bus_ready", 1 ); - - if ( GetDvar( level.eventBus.inVar ) != "" ) - { - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "A request is already in progress..." ); - } - timedOut = "set"; - continue; - } - - timedOut = "unset"; - } - - if ( timedOut == "set") - { - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Timed out waiting for response..." ); - } - - if ( IsDefined( notifyEntity) ) - { - notifyEntity NotifyClientEventTimeout( eventType ); - } - - SetDvar( level.eventBus.inVar, "" ); - - return; - } - - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn("<- " + request); - } - - SetDvar( level.eventBus.inVar, request ); -} - -ParseDataString( data ) -{ - dataParts = strtok( data, "|" ); - dict = []; - - counter = 0; - foreach ( part in dataParts ) - { - splitPart = strtok( part, "=" ); - key = splitPart[0]; - value = splitPart[1]; - dict[key] = value; - dict[counter] = key; - counter++; - } - - return dict; -} - -NotifyClientEventTimeout( eventType ) -{ - // todo: make this actual eventing - if ( eventType == level.eventTypes.clientDataRequested ) - { - self.pers["clientData"].state = level.eventBus.timeoutKey; - } -} - -NotifyClientEvent( eventInfo ) -{ - origin = getPlayerFromClientNum( int( eventInfo[3] ) ); - target = getPlayerFromClientNum( int( eventInfo[4] ) ); - - event = spawnstruct(); - event.type = eventInfo[1]; - event.subtype = eventInfo[2]; - event.data = eventInfo[5]; - event.origin = origin; - event.target = target; - - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "NotifyClientEvent->" + event.data ); - if( int( eventInfo[3] ) != -1 && !isDefined( origin ) ) - { - IPrintLn( "origin is null but the slot id is " + int( eventInfo[3] ) ); - } - if( int( eventInfo[4] ) != -1 && !isDefined( target ) ) - { - IPrintLn( "target is null but the slot id is " + int( eventInfo[4] ) ); - } - } - - if( isDefined( target ) ) - { - client = event.target; - } - else if( isDefined( origin ) ) - { - client = event.origin; - } - else - { - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Neither origin or target are set but we are a Client Event, aborting" ); - } - - return; - } - client.event = event; - - level notify( level.eventTypes.localClientEvent, client ); -} - -GetPlayerFromClientNum( clientNum ) -{ - if ( clientNum < 0 ) - return undefined; - - for ( i = 0; i < level.players.size; i++ ) - { - if ( level.players[i] getEntityNumber() == clientNum ) - { - return level.players[i]; - } - } - return undefined; -} - -AddClientCommand( commandName, shouldRunAsTarget, callback, shouldOverwrite ) -{ - if ( isDefined( level.clientCommandCallbacks[commandName] ) && isDefined( shouldOverwrite ) && !shouldOverwrite ) { - - return; - } - level.clientCommandCallbacks[commandName] = callback; - level.clientCommandRusAsTarget[commandName] = shouldRunAsTarget == true; //might speed up things later in case someone gives us a string or number instead of a boolean -} - - - -////////////////////////////////// -// Event Handlers -///////////////////////////////// - -OnClientDataReceived( event ) -{ - event.data = ParseDataString( event.data ); - clientData = self.pers[level.clientDataKey]; - - if ( event.subtype == "Fail" ) - { - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Received fail response" ); - } - clientData.state = level.eventBus.failKey; - return; - } - - if ( event.subtype == "Meta" ) - { - if ( !isDefined( clientData.meta ) ) - { - clientData.meta = []; - } - - metaKey = event.data[0]; - clientData.meta[metaKey] = event.data[metaKey]; - - return; - } - - clientData.permissionLevel = event.data["level"]; - clientData.clientId = event.data["clientId"]; - clientData.lastConnection = event.data["lastConnection"]; - clientData.state = "complete"; - self.persistentClientId = event.data["clientId"]; - - self thread DisplayWelcomeData(); - self setPersistentData(); -} - -OnExecuteCommand( event ) -{ - data = ParseDataString( event.data ); - response = ""; - - command = level.clientCommandCallbacks[event.subtype]; - runAsTarget = level.clientCommandRusAsTarget[event.subtype]; - executionContextEntity = event.origin; - if ( runAsTarget ) { - executionContextEntity = event.target; - } - if ( isDefined( command ) ) { - response = executionContextEntity [[command]]( event, data ); - } - else if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Unkown Client command->" + event.subtype); - } - - // send back the response to the origin, but only if they're not the target - if ( response != "" && IsPlayer( event.origin ) && event.origin != event.target ) - { - event.origin IPrintLnBold( response ); - } -} - -OnSetClientDataCompleted( event ) -{ - // IW4MAdmin let us know it persisted (success or fail) - if ( level.iw4adminIntegrationDebug == 1 ) - { - IPrintLn( "Set Client Data -> subtype = " + event.subType + " status = " + event.data["status"] ); - } -} - -////////////////////////////////// -// Command Implementations -///////////////////////////////// - -GiveWeaponImpl( event, data ) -{ - if ( !IsAlive( self ) ) - { - return self.name + "^7 is not alive"; - } - - self IPrintLnBold( "You have been given a new weapon" ); - self GiveWeapon( data["weaponName"] ); - self SwitchToWeapon( data["weaponName"] ); - - return self.name + "^7 has been given ^5" + data["weaponName"]; -} - -TakeWeaponsImpl() -{ - if ( !IsAlive( self ) ) - { - return self.name + "^7 is not alive"; - } - - self TakeAllWeapons(); - self IPrintLnBold( "All your weapons have been taken" ); - - return "Took weapons from " + self.name; -} - -TeamSwitchImpl() -{ - if ( !IsAlive( self ) ) - { - return self + "^7 is not alive"; - } - - team = level.allies; - - if ( self.team == "allies" ) - { - team = level.axis; - } - - self IPrintLnBold( "You are being team switched" ); - wait( 2 ); - self [[team]](); - - return self.name + "^7 switched to " + self.team; -} - -LockControlsImpl() -{ - if ( !IsAlive( self ) ) - { - return self.name + "^7 is not alive"; - } - - - self freezeControls( true ); - self call [[level.overrideMethods["god"]]]( true ); - self Hide(); - - info = []; - info[ "alertType" ] = "Alert!"; - info[ "message" ] = "You have been frozen!"; - - self AlertImpl( undefined, info ); - - return self.name + "\'s controls are locked"; -} - -UnlockControlsImpl() -{ - if ( !IsAlive( self ) ) - { - return self.name + "^7 is not alive"; - } - - self freezeControls( false ); - self call [[level.overrideMethods["god"]]]( false ); - self Show(); - - return self.name + "\'s controls are unlocked"; -} - -NoClipImpl() -{ - if ( !IsAlive( self ) ) - { - self IPrintLnBold( "You are not alive" ); - // Due to bug when sometimes disabling noclip game thinks you're dead - // removing the return and allowing them to go back into noclip is probably better. - //return; - } - - self SetClientDvar( "sv_cheats", 1 ); - self SetClientDvar( "cg_thirdperson", 1 ); - self SetClientDvar( "sv_cheats", 0 ); - - self call [[level.overrideMethods["god"]]]( true ); - self call [[level.overrideMethods["noclip"]]]( true ); - self Hide(); - - self.isNoClipped = true; - - self IPrintLnBold( "NoClip enabled" ); -} - -NoClipOffImpl() -{ - if ( !IsDefined( self.isNoClipped ) || !self.isNoClipped ) - { - self IPrintLnBold( "You are not no-clipped" ); - return; - } - - self SetClientDvar( "sv_cheats", 1 ); - self SetClientDvar( "cg_thirdperson", 0 ); - self SetClientDvar( "sv_cheats", 0 ); - - self call [[level.overrideMethods["god"]]]( false ); - self call [[level.overrideMethods["noclip"]]]( false ); - self Show(); - - self IPrintLnBold( "NoClip disabled" ); - - if ( !IsAlive( self ) && self.isNoClipped ) - { - // Sometimes you will bug exiting noclip where the game thinks you're dead - // but you're not. You will retain godmode but be able to run around and kill people. - // So, it's important to let the user know. - self IPrintLnBold( "^1You are bugged! ^4Swap team." ); - } - - self.isNoClipped = false; -} - -HideImpl() -{ - if ( !IsAlive( self ) ) - { - self IPrintLnBold( "You are not alive" ); - return; - } - - self SetClientDvar( "sv_cheats", 1 ); - self SetClientDvar( "cg_thirdperson", 1 ); - self SetClientDvar( "sv_cheats", 0 ); - - if ( !IsDefined( self.savedHealth ) || self.health < 1000 ) - { - self.savedHealth = self.health; - self.savedMaxHealth = self.maxhealth; - } - - self call [[level.overrideMethods["god"]]]( true ); - self Hide(); - - self.isHidden = true; - - self IPrintLnBold( "You are now ^5hidden ^7from other players" ); -} - -UnhideImpl() -{ - if ( !IsAlive( self ) ) - { - self IPrintLnBold( "You are not alive" ); - return; - } - - if ( !IsDefined( self.isHidden ) || !self.isHidden ) - { - self IPrintLnBold( "You are not hidden" ); - return; - } - - self SetClientDvar( "sv_cheats", 1 ); - self SetClientDvar( "cg_thirdperson", 0 ); - self SetClientDvar( "sv_cheats", 0 ); - - self call [[level.overrideMethods["god"]]]( false ); - self Show(); - - self.isHidden = false; - - self IPrintLnBold( "You are now ^5visible ^7to other players" ); -} - -AlertImpl( event, data ) -{ - if ( level.eventBus.gamename == "IW4" ) { - self thread maps\mp\gametypes\_hud_message::oldNotifyMessage( data["alertType"], data["message"], "compass_waypoint_target", ( 1, 0, 0 ), "ui_mp_nukebomb_timer", 7.5 ); - } - if ( level.eventBus.gamename == "IW5" ) { //IW5's notification are a bit different... - self thread maps\mp\gametypes\_hud_message::oldNotifyMessage( data["alertType"], data["message"], undefined, ( 1, 0, 0 ), "ui_mp_nukebomb_timer", 7.5 ); - } - return "Sent alert to " + self.name; -} - -GotoImpl( event, data ) -{ - if ( IsDefined( event.target ) ) - { - return self GotoPlayerImpl( event.target ); - } - else - { - return self GotoCoordImpl( data ); - } -} - -GotoCoordImpl( data ) -{ - if ( !IsAlive( self ) ) - { - self IPrintLnBold( "You are not alive" ); - return; - } - - position = ( int( data["x"] ), int( data["y"] ), int( data["z"]) ); - self SetOrigin( position ); - self IPrintLnBold( "Moved to " + "("+ position[0] + "," + position[1] + "," + position[2] + ")" ); -} - -GotoPlayerImpl( target ) -{ - if ( !IsAlive( target ) ) - { - self IPrintLnBold( target.name + " is not alive" ); - return; - } - - self SetOrigin( target GetOrigin() ); - self IPrintLnBold( "Moved to " + target.name ); -} - -PlayerToMeImpl( event ) -{ - if ( !IsAlive( self ) ) - { - return self.name + " is not alive"; - } - - self SetOrigin( event.origin GetOrigin() ); - return "Moved here " + self.name; -} - - -KillImpl() -{ - if ( !IsAlive( self ) ) - { - return self.name + " is not alive"; - } - - self Suicide(); - self IPrintLnBold( "You were killed by " + self.name ); - - return "You killed " + self.name; -} - -NightModeImpl() -{ - if ( !IsDefined ( level.nightModeEnabled ) ) - { - level.nightModeEnabled = true; - } - else - { - level.nightModeEnabled = !level.nightModeEnabled; - } - - message = "^5NightMode ^7is disabled"; - - if ( level.nightModeEnabled ) - { - message = "^5NightMode ^7is enabled"; - } - - IPrintLnBold( message ); - - foreach( player in level.players ) - { - player ToggleNightMode(); - } -} - -ToggleNightMode() -{ - colorMap = 1; - fxDraw = 1; - - if ( IsDefined( level.nightModeEnabled ) && level.nightModeEnabled ) - { - colorMap = 0; - fxDraw = 0; - } - - self SetClientDvar( "sv_cheats", 1 ); - self SetClientDvar( "r_colorMap", colorMap ); - self SetClientDvar( "fx_draw", fxDraw ); - self SetClientDvar( "sv_cheats", 0 ); -} - -SetSpectatorImpl() -{ - if ( self.pers["team"] == "spectator" ) - { - return self.name + " is already spectating"; - } - - self [[level.spectator]](); - self IPrintLnBold( "You have been moved to spectator" ); - - return self.name + " has been moved to spectator"; -} - -////////////////////////////////// -// Function Overrides -////////////////////////////////// - -_god( isEnabled ) -{ - if ( isEnabled == true ) - { - if ( !IsDefined( self.savedHealth ) || self.health < 1000 ) - { - self.savedHealth = self.health; - self.savedMaxHealth = self.maxhealth; - } - - self.maxhealth = 99999; - self.health = 99999; - } - - else - { - if ( !IsDefined( self.savedHealth ) || !IsDefined( self.savedMaxHealth ) ) - { - return; - } - - self.health = self.savedHealth; - self.maxhealth = self.savedMaxHealth; - } -} - - -IW5_God() -{ - SetDvar( "sv_cheats", 1 ); - self God(); - SetDvar( "sv_cheats", 0 ); -} - -IW5_NoClip() -{ - SetDvar( "sv_cheats", 1 ); - self NoClip(); - SetDvar( "sv_cheats", 0 ); -} diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index 897e501ed..cc4007448 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -6,14 +6,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C8F3945-0AEF-4949-A1F7-B18E952E50BC}" ProjectSection(SolutionItems) = preProject - GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc = GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc DeploymentFiles\deployment-pipeline.yml = DeploymentFiles\deployment-pipeline.yml DeploymentFiles\PostPublish.ps1 = DeploymentFiles\PostPublish.ps1 README.md = README.md version.txt = version.txt DeploymentFiles\UpdateIW4MAdmin.ps1 = DeploymentFiles\UpdateIW4MAdmin.ps1 DeploymentFiles\UpdateIW4MAdmin.sh = DeploymentFiles\UpdateIW4MAdmin.sh - GameFiles\_integration.gsc = GameFiles\_integration.gsc EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLibraryCore", "SharedLibraryCore\SharedLibraryCore.csproj", "{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}" @@ -71,6 +69,34 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integrations.Cod", "Integra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integrations.Source", "Integrations\Source\Integrations.Source.csproj", "{9512295B-3045-40E0-9B7E-2409F2173E9D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GameFiles", "GameFiles", "{6CBF412C-EFEE-45F7-80FD-AC402C22CDB9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GameInterface", "GameInterface", "{5C2BE2A8-EA1D-424F-88E1-7FC33EEC2E55}" + ProjectSection(SolutionItems) = preProject + GameFiles\GameInterface\_integration_base.gsc = GameFiles\GameInterface\_integration_base.gsc + GameFiles\GameInterface\_integration_iw4x.gsc = GameFiles\GameInterface\_integration_iw4x.gsc + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AntiCheat", "AntiCheat", "{AB83BAC0-C539-424A-BF00-78487C10753C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IW4x", "IW4x", "{3EA564BD-3AC6-479B-96B6-CB059DCD0C77}" + ProjectSection(SolutionItems) = preProject + GameFiles\AntiCheat\IW4x\userraw\scripts\_customcallbacks.gsc = GameFiles\AntiCheat\IW4x\userraw\scripts\_customcallbacks.gsc + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pluto T6", "Pluto T6", "{866F453D-BC89-457F-8B55-485494759B31}" + ProjectSection(SolutionItems) = preProject + GameFiles\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc = GameFiles\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc + GameFiles\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc.src = GameFiles\AntiCheat\PT6\storage\t6\scripts\mp\_customcallbacks.gsc.src + GameFiles\AntiCheat\PT6\README.MD = GameFiles\AntiCheat\PT6\README.MD + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pluto IW5", "Pluto IW5", "{603725A4-BC0B-423B-955B-762C89E1C4C2}" + ProjectSection(SolutionItems) = preProject + GameFiles\AntiCheat\IW5\storage\iw5\scripts\_customcallbacks.gsc = GameFiles\AntiCheat\IW5\storage\iw5\scripts\_customcallbacks.gsc + GameFiles\AntiCheat\IW5\README.MD = GameFiles\AntiCheat\IW5\README.MD + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -389,6 +415,12 @@ Global {00A1FED2-2254-4AF7-A5DB-2357FA7C88CD} = {26E8B310-269E-46D4-A612-24601F16065F} {A9348433-58C1-4B9C-8BB7-088B02529D9D} = {A2AE33B4-0830-426A-9E11-951DAB12BE5B} {9512295B-3045-40E0-9B7E-2409F2173E9D} = {A2AE33B4-0830-426A-9E11-951DAB12BE5B} + {6CBF412C-EFEE-45F7-80FD-AC402C22CDB9} = {8C8F3945-0AEF-4949-A1F7-B18E952E50BC} + {5C2BE2A8-EA1D-424F-88E1-7FC33EEC2E55} = {6CBF412C-EFEE-45F7-80FD-AC402C22CDB9} + {AB83BAC0-C539-424A-BF00-78487C10753C} = {6CBF412C-EFEE-45F7-80FD-AC402C22CDB9} + {3EA564BD-3AC6-479B-96B6-CB059DCD0C77} = {AB83BAC0-C539-424A-BF00-78487C10753C} + {866F453D-BC89-457F-8B55-485494759B31} = {AB83BAC0-C539-424A-BF00-78487C10753C} + {603725A4-BC0B-423B-955B-762C89E1C4C2} = {AB83BAC0-C539-424A-BF00-78487C10753C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87} diff --git a/Plugins/ScriptPlugins/GameInterface.js b/Plugins/ScriptPlugins/GameInterface.js index 63617ea05..ce514df86 100644 --- a/Plugins/ScriptPlugins/GameInterface.js +++ b/Plugins/ScriptPlugins/GameInterface.js @@ -336,21 +336,6 @@ let commands = [{ sendScriptCommand(gameEvent.Owner, 'Kill', gameEvent.Origin, gameEvent.Target, undefined); } }, - { - name: 'nightmode', - description: 'sets server into nightmode', - alias: 'nitem', - permission: 'SeniorAdmin', - targetRequired: false, - arguments: [], - supportedGames: ['IW4', 'IW5'], - execute: (gameEvent) => { - if (!validateEnabled(gameEvent.Owner, gameEvent.Origin)) { - return; - } - sendScriptCommand(gameEvent.Owner, 'NightMode', gameEvent.Origin, undefined, undefined); - } - }, { name: 'setspectator', description: 'sets a player as spectator',