diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index bc4347bb..d001bec1 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -295,7 +295,7 @@ namespace IW4MAdmin } } - if (E.Type == GameEvent.EventType.ConnectionLost) + else if (E.Type == GameEvent.EventType.ConnectionLost) { var exception = E.Extra as Exception; ServerLogger.LogError(exception, @@ -309,7 +309,7 @@ namespace IW4MAdmin Throttled = true; } - if (E.Type == GameEvent.EventType.ConnectionRestored) + else if (E.Type == GameEvent.EventType.ConnectionRestored) { ServerLogger.LogInformation( "Connection restored with {server}", ToString()); @@ -327,7 +327,7 @@ namespace IW4MAdmin Throttled = false; } - if (E.Type == GameEvent.EventType.ChangePermission) + else if (E.Type == GameEvent.EventType.ChangePermission) { var newPermission = (Permission) E.Extra; ServerLogger.LogInformation("{origin} is setting {target} to permission level {newPermission}", @@ -619,7 +619,7 @@ namespace IW4MAdmin await OnClientUpdate(E.Origin); } - if (E.Type == GameEvent.EventType.Say) + else if (E.Type == GameEvent.EventType.Say) { if (E.Data?.Length > 0) { @@ -649,7 +649,7 @@ namespace IW4MAdmin } } - if (E.Type == GameEvent.EventType.MapChange) + else if (E.Type == GameEvent.EventType.MapChange) { ServerLogger.LogInformation("New map loaded - {clientCount} active players", ClientNum); @@ -690,7 +690,7 @@ namespace IW4MAdmin } } - if (E.Type == GameEvent.EventType.MapEnd) + else if (E.Type == GameEvent.EventType.MapEnd) { ServerLogger.LogInformation("Game ending..."); @@ -700,12 +700,12 @@ namespace IW4MAdmin } } - if (E.Type == GameEvent.EventType.Tell) + else if (E.Type == GameEvent.EventType.Tell) { await Tell(E.Message, E.Target); } - if (E.Type == GameEvent.EventType.Broadcast) + else if (E.Type == GameEvent.EventType.Broadcast) { if (!Utilities.IsDevelopment && E.Data != null) // hides broadcast when in development mode { diff --git a/GameFiles/IW4x/userraw/scripts/_integration.gsc b/GameFiles/IW4x/userraw/scripts/_integration.gsc index 8276008a..85d3893d 100644 --- a/GameFiles/IW4x/userraw/scripts/_integration.gsc +++ b/GameFiles/IW4x/userraw/scripts/_integration.gsc @@ -66,6 +66,7 @@ OnPlayerConnect() player thread OnPlayerSpawned(); player thread PlayerTrackingOnInterval(); + player ToggleNightMode(); } } @@ -99,7 +100,7 @@ OnGameEnded() { level waittill( "game_ended" ); // note: you can run data code here but it's possible for - // data to get trucated, so we will try a timer based approach for now + // data to get truncated, so we will try a timer based approach for now } } @@ -233,7 +234,7 @@ SaveTrackingMetrics() { if ( level.iw4adminIntegrationDebug == 1 ) { - self IPrintLn( "Saving tracking metrics for " + self.persistentClientId ); + IPrintLn( "Saving tracking metrics for " + self.persistentClientId ); } currentShotCount = self getPlayerStat( "mostshotsfired" ); @@ -242,7 +243,7 @@ SaveTrackingMetrics() if ( level.iw4adminIntegrationDebug == 1 ) { - self IPrintLn( "Total Shots Fired increased by " + change ); + IPrintLn( "Total Shots Fired increased by " + change ); } if ( !IsDefined( change ) ) @@ -396,18 +397,22 @@ NotifyClientEventTimeout( eventType ) NotifyClientEvent( eventInfo ) { - client = getPlayerFromClientNum( int( eventInfo[3] ) ); + origin = getPlayerFromClientNum( int( eventInfo[3] ) ); + target = getPlayerFromClientNum( int( eventInfo[4] ) ); event = spawnstruct(); event.type = eventInfo[1]; event.subtype = eventInfo[2]; - event.data = eventInfo[4]; + event.data = eventInfo[5]; + event.origin = origin; + event.target = target; if ( level.iw4adminIntegrationDebug == 1 ) { - IPrintLn(event.data); + IPrintLn( "NotifyClientEvent->" + event.data ); } - + + client = event.origin; client.event = event; level notify( level.eventTypes.localClientEvent, client ); @@ -457,33 +462,56 @@ OnClientDataReceived( event ) OnExecuteCommand( event ) { data = ParseDataString( event.data ); + response = ""; + switch ( event.subtype ) { case "GiveWeapon": - self GiveWeaponImpl( data ); + response = event.target GiveWeaponImpl( data ); break; case "TakeWeapons": - self TakeWeaponsImpl(); + response = event.target TakeWeaponsImpl(); break; case "SwitchTeams": - self TeamSwitchImpl(); + response = event.target TeamSwitchImpl(); break; case "Hide": - self HideImpl(); + response = self HideImpl(); break; case "Unhide": - self UnhideImpl(); + response = self UnhideImpl(); break; case "Alert": - self AlertImpl( data ); + response = event.target AlertImpl( data ); break; + case "Goto": + if ( IsDefined( event.target ) ) + { + response = self GotoPlayerImpl( event.target ); + } + else + { + response = self GotoImpl( event.data ); + } + break; + case "Kill": + response = event.target KillImpl(); + break; + case "NightMode": + NightModeImpl(); + break; + } + + // 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"] ); @@ -496,54 +524,181 @@ OnSetClientDataCompleted( event ) GiveWeaponImpl( data ) { - if ( IsAlive( self ) ) + if ( !IsAlive( self ) ) { - self IPrintLnBold( "You have been given a new weapon" ); - self GiveWeapon( data["weaponName"] ); - self SwitchToWeapon( data["weaponName"] ); + 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 ) ) + if ( !IsAlive( self ) ) { - self TakeAllWeapons(); - self IPrintLnBold( "All your weapons have been taken" ); + 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.name + "^7 is not alive"; + } + + team = level.allies; + if ( self.team == "allies" ) { - self [[level.axis]](); - } - else - { - self [[level.allies]](); + team = level.axis; } + + self IPrintLnBold( "You are being team switched" ); + wait( 2 ); + self [[team]](); + + return self.name + "^7 switched to " + self.team; } HideImpl() { - if ( IsAlive( self ) ) + if ( !IsAlive( self ) ) { - self Hide(); - self IPrintLnBold( "You are now hidden" ); + self IPrintLnBold( "You are not alive" ); + return; } + + if ( self.isHidden ) + { + self IPrintLnBold( "You are already hidden" ); + return; + } + + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 1 ); + self SetClientDvar( "sv_cheats", 0 ); + + self.savedHealth = self.health; + self.health = 9999; + self.isHidden = true; + + self Hide(); + + self IPrintLnBold( "You are now ^5hidden ^7from other players" ); } UnhideImpl() { - if ( IsAlive( self ) ) + if ( !IsAlive( self ) ) { - self Show(); - self IPrintLnBold( "You are now visible" ); + self IPrintLnBold( "You are not alive" ); + return; } + + self SetClientDvar( "sv_cheats", 1 ); + self SetClientDvar( "cg_thirdperson", 0 ); + self SetClientDvar( "sv_cheats", 0 ); + + self.health = self.savedHealth; + self.isHidden = false; + + self Show(); + self IPrintLnBold( "You are now ^5visible ^7to other players" ); } AlertImpl( data ) { 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( 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 ); +} + +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 ) + { + self 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 ); } diff --git a/Plugins/ScriptPlugins/GameInterface.js b/Plugins/ScriptPlugins/GameInterface.js index 3ba64614..d4b491aa 100644 --- a/Plugins/ScriptPlugins/GameInterface.js +++ b/Plugins/ScriptPlugins/GameInterface.js @@ -22,11 +22,11 @@ let plugin = { } const eventType = eventTypes[gameEvent.Type]; - + if (eventType === undefined) { return; } - + switch (eventType) { case 'start': const enabled = initialize(server); @@ -77,145 +77,180 @@ let plugin = { }; let commands = [{ - // required name: 'giveweapon', - // required description: 'gives specified weapon', - // required alias: 'gw', - // required permission: 'SeniorAdmin', - // optional (defaults to false) - targetRequired: false, - // optional + targetRequired: true, arguments: [{ + name: 'player', + required: true + }, + { name: 'weapon name', required: true }], supportedGames: ['IW4'], - // required execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'GiveWeapon', gameEvent.Origin, {weaponName: gameEvent.Data}); + sendScriptCommand(gameEvent.Owner, 'GiveWeapon', gameEvent.Origin, gameEvent.Target, {weaponName: gameEvent.Data}); } }, - { - // required - name: 'takeweapons', - // required - description: 'take all weapons from specifies player', - // required - alias: 'tw', - // required - permission: 'SeniorAdmin', - // optional (defaults to false) - targetRequired: true, - // optional - arguments: [], - supportedGames: ['IW4'], - // required - execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'TakeWeapons', gameEvent.Target, undefined); - } +{ + name: 'takeweapons', + description: 'take all weapons from specified player', + alias: 'tw', + permission: 'SeniorAdmin', + targetRequired: true, + arguments: [{ + name: 'player', + required: true + }], + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'TakeWeapons', gameEvent.Origin, gameEvent.Target, undefined); + } +}, +{ + name: 'switchteam', + description: 'switches specified player to the opposite team', + alias: 'st', + permission: 'Administrator', + targetRequired: true, + arguments: [{ + name: 'player', + required: true + }], + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'SwitchTeams', gameEvent.Origin, gameEvent.Target, undefined); + } +}, +{ + name: 'hide', + description: 'hide yourself', + alias: 'hi', + permission: 'SeniorAdmin', + targetRequired: false, + arguments: [], + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'Hide', gameEvent.Origin, gameEvent.Origin, undefined); + } +}, +{ + name: 'unhide', + description: 'unhide yourself', + alias: 'unh', + permission: 'SeniorAdmin', + targetRequired: false, + arguments: [], + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'Unhide', gameEvent.Origin, gameEvent.Origin, undefined); + } +}, +{ + name: 'alert', + description: 'alert a player', + alias: 'alr', + permission: 'SeniorAdmin', + targetRequired: true, + arguments: [{ + name: 'player', + required: true }, - { - // required - name: 'switchteam', - // required - description: 'switches specified player to the opposite team', - // required - alias: 'st', - // required - permission: 'Administrator', - // optional (defaults to false) - targetRequired: true, - // optional - arguments: [{ - name: 'player', + { + name: 'message', required: true }], - supportedGames: ['IW4'], - // required - execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'SwitchTeams', gameEvent.Target, undefined); - } + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'Alert', gameEvent.Origin, gameEvent.Target, { + alertType: 'Alert', + message: gameEvent.Data + }); + } +}, +{ + name: 'gotoplayer', + description: 'teleport to a player', + alias: 'g2p', + permission: 'SeniorAdmin', + targetRequired: true, + arguments: [{ + name: 'player', + required: true + }], + supportedGames: ['IW4'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'Goto', gameEvent.Origin, gameEvent.Target, undefined); + } +}, +{ + name: 'goto', + description: 'teleport to a position', + alias: 'g2', + permission: 'SeniorAdmin', + targetRequired: false, + arguments: [{ + name: 'x', + required: true }, { - // required - name: 'hide', - // required - description: 'hide yourself', - // required - alias: 'hi', - // required - permission: 'SeniorAdmin', - // optional (defaults to false) - targetRequired: false, - // optional - arguments: [], - supportedGames: ['IW4'], - // required - execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'Hide', gameEvent.Origin, undefined); - } + name: 'y', + required: true }, { - // required - name: 'unhide', - // required - description: 'unhide yourself', - // required - alias: 'unh', - // required - permission: 'SeniorAdmin', - // optional (defaults to false) - targetRequired: false, - // optional - arguments: [], - supportedGames: ['IW4'], - // required - execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'Unhide', gameEvent.Origin, undefined); - } - }, - { - // required - name: 'alert', - // required - description: 'alert a player', - // required - alias: 'alr', - // required - permission: 'SeniorAdmin', - // optional (defaults to false) - targetRequired: true, - // optional - arguments: [{ - name: 'player', - required: true - }, - { - name: 'message', - required: true - }], - supportedGames: ['IW4'], - // required - execute: (gameEvent) => { - sendScriptCommand(gameEvent.Owner, 'Alert', gameEvent.Target, { - alertType: 'Alert', - message: gameEvent.Data - }); - } - }]; + name: 'z', + required: true + }], + supportedGames: ['IW4'], + execute: (gameEvent) => { + const args = String(gameEvent.Data).split(' '); + sendScriptCommand(gameEvent.Owner, 'Goto', gameEvent.Origin, gameEvent.Target, { + x: args[0], + y: args[1], + z: args[2] + }); + } +}, +{ + name: 'kill', + description: 'kill a player', + alias: 'kpl', + permission: 'SeniorAdmin', + targetRequired: true, + arguments: [{ + name: 'player', + required: true + }], + supportedGames: ['IW4'], + execute: (gameEvent) => { + 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'], + execute: (gameEvent) => { + sendScriptCommand(gameEvent.Owner, 'NightMode', gameEvent.Origin, undefined, undefined); + } +}]; -const sendScriptCommand = (server, command, target, data) => { +const sendScriptCommand = (server, command, origin, target, data) => { const state = servers[server.EndPoint]; if (state === undefined || !state.enabled) { return; } - sendEvent(server, false, 'ExecuteCommandRequested', command, target, data); + sendEvent(server, false, 'ExecuteCommandRequested', command, origin, target, data); } -const sendEvent = (server, responseExpected, event, subtype, client, data) => { +const sendEvent = (server, responseExpected, event, subtype, origin, target, data) => { const logger = _serviceResolver.ResolveService('ILogger'); let pendingOut = true; @@ -241,7 +276,12 @@ const sendEvent = (server, responseExpected, event, subtype, client, data) => { logger.WriteWarning(`Reached maximum attempts waiting for output to be available for ${server.EndPoint}`) } - const output = `${responseExpected ? '1' : '0'};${event};${subtype};${client.ClientNumber};${buildDataString(data)}`; + let targetClientNumber = -1; + if (target != null) { + targetClientNumber = target.ClientNumber; + } + + const output = `${responseExpected ? '1' : '0'};${event};${subtype};${origin.ClientNumber};${targetClientNumber};${buildDataString(data)}`; logger.WriteDebug(`Sending output to server ${output}`); try { @@ -273,7 +313,7 @@ const initialize = (server) => { servers[server.EndPoint] = { enabled: false } - + let enabled = false; try { enabled = server.GetServerDvar('sv_iw4madmin_integration_enabled') === '1'; @@ -348,18 +388,18 @@ const pollForEvents = server => { }; } - sendEvent(server, false, 'ClientDataReceived', event.subType, client, data); + sendEvent(server, false, 'ClientDataReceived', event.subType, client, undefined, data); } else { logger.WriteWarning(`Could not find client slot ${event.clientNumber} when processing ${event.eventType}`); - sendEvent(server, false, 'ClientDataReceived', 'Fail', {ClientNumber: event.clientNumber}, undefined); + sendEvent(server, false, 'ClientDataReceived', 'Fail', event.clientNumber, undefined, {ClientNumber: event.clientNumber}); } } if (event.eventType === 'SetClientDataRequested') { let client = server.GetClientByNumber(event.clientNumber); let clientId; - - if (client != null){ + + if (client != null) { clientId = client.ClientId; } else { clientId = parseInt(event.data.clientId); @@ -369,7 +409,7 @@ const pollForEvents = server => { if (clientId == null) { logger.WriteWarning(`Could not find client slot ${event.clientNumber} when processing ${event.eventType}`); - sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, {status: 'Fail'}); + sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, undefined, {status: 'Fail'}); } else { if (event.subType === 'Meta') { const metaService = _serviceResolver.ResolveService('IMetaService'); @@ -382,9 +422,9 @@ const pollForEvents = server => { } else { metaService.SetPersistentMeta(event.data['key'], event.data['value'], clientId).GetAwaiter().GetResult(); } - sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, {status: 'Complete'}); + sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, undefined,{status: 'Complete'}); } catch (error) { - sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, {status: 'Fail'}); + sendEvent(server, false, 'SetClientDataCompleted', 'Meta', {ClientNumber: event.clientNumber}, undefined,{status: 'Fail'}); } } } @@ -395,8 +435,7 @@ const pollForEvents = server => { } catch (error) { logger.WriteError(`Could not reset in bus value for ${server.EndPoint} - ${error}`); } - } - else if (server.ClientNum === 0) { + } else if (server.ClientNum === 0) { servers[server.EndPoint].timer.Stop(); } } diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 4ca48729..1799fb76 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -406,7 +406,7 @@ namespace SharedLibraryCore public string GetServerDvar(string dvarName) { var tokenSource = new CancellationTokenSource(); - tokenSource.CancelAfter(TimeSpan.FromSeconds(400)); + tokenSource.CancelAfter(TimeSpan.FromMilliseconds(400)); try { return this.GetDvarAsync(dvarName).WithWaitCancellation(tokenSource.Token).GetAwaiter() @@ -421,7 +421,7 @@ namespace SharedLibraryCore public bool SetServerDvar(string dvarName, string dvarValue) { var tokenSource = new CancellationTokenSource(); - tokenSource.CancelAfter(TimeSpan.FromSeconds(400)); + tokenSource.CancelAfter(TimeSpan.FromMilliseconds(400)); try { this.SetDvarAsync(dvarName, dvarValue).WithWaitCancellation(tokenSource.Token).GetAwaiter().GetResult();