From e83947ffdaaeaad7251adf2cf9b6010a01aebb85 Mon Sep 17 00:00:00 2001 From: xerxes-at <5236639+xerxes-at@users.noreply.github.com> Date: Wed, 25 Aug 2021 18:06:46 +0200 Subject: [PATCH 1/2] Updated T6 AC GSC (#214) * PlutoT6 AC GSC Updated PlutoT6's GSC modding capabilities changed, this allows us to bring the script on parity with the IW4x one. The following things changed: * Script no longer replaces stock GSC since custom GSC files are now supported. * The Script now captures the last time the client used his attack button; this is used to detect trigger bots. * Cleaned up the code a bit * Create README.MD Basic installation guide. --- GameFiles/PT6/README.MD | 35 ++++++++++ .../t6/scripts/mp/_customcallbacks.gsc | Bin 0 -> 5522 bytes .../t6/scripts/mp/_customcallbacks.gsc.src} | 61 ++++++------------ .../t6r/data/maps/mp/gametypes/_clientids.gsc | Bin 5518 -> 0 bytes 4 files changed, 56 insertions(+), 40 deletions(-) create mode 100644 GameFiles/PT6/README.MD create mode 100644 GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc rename GameFiles/PT6/{t6r/data/maps/mp/gametypes/_clientids.gsc.src => storage/t6/scripts/mp/_customcallbacks.gsc.src} (91%) delete mode 100644 GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc diff --git a/GameFiles/PT6/README.MD b/GameFiles/PT6/README.MD new file mode 100644 index 00000000..b38f41c7 --- /dev/null +++ b/GameFiles/PT6/README.MD @@ -0,0 +1,35 @@ +# T6 + +This brings IW4M-Admins's Anti-cheat to Plutonium T6 +The following limitations are known: +* Can't get the recoil from weapons fire; you have to disable this detection type manually. +* in extreme cases it can produce false positives for Snap and Offset detection. + +## Installation + + +Move `_customcallbacks.gsc` to `%localappdata%\Plutonium\storage\t6\scripts\mp\` + +Add this to the WeaponNameParserConfigurations List in the StatsPluginSettings.json file: + +``` + { + "Game": "T6", + "Delimiters": [ + "_", + "+" + ], + "WeaponSuffix": "mp", + "WeaponPrefix": null + } +``` + +Now create the following entry for __EVERY__ T6 server you are using this on in the ServerDetectionTypes list: + +``` + "1270014976": [ + "Offset", + "Strain", + "Snap" + ] +``` \ No newline at end of file diff --git a/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc b/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc new file mode 100644 index 0000000000000000000000000000000000000000..909b029219601f7f5d88d23fa23a00cff3c5a0e9 GIT binary patch literal 5522 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 IT(}ed7n)%BwEzGB literal 0 HcmV?d00001 diff --git a/GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc.src b/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src similarity index 91% rename from GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc.src rename to GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src index 59af24a4..e0916d8c 100644 --- a/GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc.src +++ b/GameFiles/PT6/storage/t6/scripts/mp/_customcallbacks.gsc.src @@ -3,24 +3,6 @@ #include common_scripts\utility; init() -{ - level.clientid = 0; - level thread onplayerconnect(); - level thread IW4MA_init(); -} - -onplayerconnect() -{ - for ( ;; ) - { - level waittill( "connecting", player ); - player.clientid = level.clientid; - level.clientid++; - } -} - - -IW4MA_init() { SetDvarIfUninitialized( "sv_customcallbacks", true ); SetDvarIfUninitialized( "sv_framewaittime", 0.05 ); @@ -29,53 +11,52 @@ IW4MA_init() SetDvarIfUninitialized( "sv_printradarupdates", 0 ); SetDvarIfUninitialized( "sv_printradar_updateinterval", 500 ); SetDvarIfUninitialized( "sv_iw4madmin_url", "http://127.0.0.1:1624" ); - - level thread IW4MA_onPlayerConnect(); + + level thread onPlayerConnect(); if (getDvarInt("sv_printradarupdates") == 1) { level thread runRadarUpdates(); } - + level waittill( "prematch_over" ); level.callbackPlayerKilled = ::Callback_PlayerKilled; level.callbackPlayerDamage = ::Callback_PlayerDamage; level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect; } -//Does not exist in T6 +//It's called slightly different in T6 +//set_dvar_if_unset(dvar, val, reset) SetDvarIfUninitialized(dvar, val) { - curval = getDvar(dvar); - if (curval == "") - SetDvar(dvar,val); + set_dvar_if_unset(dvar,val); } -IW4MA_onPlayerConnect( player ) +onPlayerConnect( player ) { for( ;; ) { level waittill( "connected", player ); player thread waitForFrameThread(); - //player thread waitForAttack(); + player thread waitForAttack(); } } -//Does not work in T6 -/*waitForAttack() +//Got added to T6 on April 2020 +waitForAttack() { self endon( "disconnect" ); - + self.lastAttackTime = 0; - + for( ;; ) { self notifyOnPlayerCommand( "player_shot", "+attack" ); self waittill( "player_shot" ); - + self.lastAttackTime = getTime(); } -}*/ +} runRadarUpdates() { @@ -95,7 +76,7 @@ runRadarUpdates() } wait( interval / 1000 ); - } + } } hitLocationToBone( hitloc ) @@ -144,7 +125,7 @@ hitLocationToBone( hitloc ) waitForFrameThread() { self endon( "disconnect" ); - + self.currentAnglePosition = 0; self.anglePositions = []; @@ -152,7 +133,7 @@ waitForFrameThread() { self.anglePositions[i] = self getPlayerAngles(); } - + for( ;; ) { self.anglePositions[self.currentAnglePosition] = self getPlayerAngles(); @@ -165,9 +146,9 @@ waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount ) { currentIndex = self.currentAnglePosition; wait( 0.05 * afterFrameCount ); - + self.angleSnapshot = []; - + for( j = 0; j < self.anglePositions.size; j++ ) { self.angleSnapshot[j] = self.anglePositions[j]; @@ -209,7 +190,7 @@ waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount ) i++; } - lastAttack = 100;//int(getTime()) - int(self.lastAttackTime); + lastAttack = int(getTime()) - int(self.lastAttackTime); isAlive = isAlive(self); logPrint(logString + ";" + anglesStr + ";" + isAlive + ";" + lastAttack + "\n" ); @@ -261,7 +242,7 @@ Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sW { return; } - + if ( self.health - iDamage > 0 ) { self Process_Hit( "Damage", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon ); diff --git a/GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc b/GameFiles/PT6/t6r/data/maps/mp/gametypes/_clientids.gsc deleted file mode 100644 index e511aac9b253daa7417c5e140c1f3679a1841349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5518 zcmZ{n3vg8B6@bruCfNW%N5q>!Ax<49aM+*FD-g}j>`>&>|vS!2mdh4ss-VzK1sgOZza zOZ~-^8+a!;6OorXUTMJ1sutm_Q#n7M=r3g@69mNtbLPyQH@~Bke{&bi?Vi_FS=ou} zE%oQejZ`doIX6gV5`_V;B-O}A>)+sKvn;pjOnj*~a;Dsz+vlBF^#pIFs$Z@K|Ad}X zqa`Zrq86L%57%7X3N|_1H>Rb6nl6;WFb0{D=cc3hXGI2 zc849MBq#(-Ia}xpcT|>9yh?3rTw;>(gKQz`s)Ps%>k4_V;-R}Nxh(4w6@~6op#@0M z4d@|AnsR|$D6&uM6Xl|x_aY-I$QA}i3nGJcW`cxU%8j4!a@mdE;7) zE2uR|7YZu4Zhk{{wE8$FRRgy#QAvw2Nm$wTFh7cga(I|!oK>!7;pkbfWD`kH4mj6+ zvd$w*y)aFag?>aP*p*@Ye9GGxbzjbN?`71yD>hw``JBdNP`fzN{zfkqHCiB9 zw;Z@Zz@;ezR4SB6UYCUxONFFYE+@!7QTL@pmB^HGl#o)+d2YU3NQXHue&xuPE4hB@ ziVG9Hi{zI>8#sVmVshH-30?F zza&LgndV9l?`L%aqu{A?zs@alqj0jya$$6nRlhuAJ~VyV!aBZ8g+4!7xeB9;!p-}+ zRDUTv+VV}c#L*n0*Oh$O)*;s-GmVKYa<{a#Pwv8!ZAf=Te_ZJ@ReOcIVvkI|{A+U7 z#2M6eWPS@R!gh;#-Ws8kc2kSVJJKPC!uAdFrvu9t#^g928RI*EuTdNInG<5tGR8Jy zU06{cwtP&VOKNsZ2C>sMJDTs1#vEzon_6#N8bnQfTj+aSn|%3MQ(i-#*z%&*#y4Km zmHzKg?+o>+dYeo&mg+0?)w-i+TBXflt!wn&c$2g{nxm_!%3A7HX>#%t0m0m-HJcPvqp{fiQc^Y zOMXP4J=SEXC^Q?(mv@NGBdEJoU(_S>M;ybJe;O-i_eiT+v(`bqtC;IDBqrw>T5KtD z1>z&uA@?EAA#WfbBQersI!L_g-_QUVNn_&)ahdyK~ z;)R^54H3_MX4AKg=`C{5Y&&Cm%Mp_u4wI%u^Dl|ZsnXum8Sb4Us&ragt1Rkyb3}Qg zJ)J(|pc$2@)3JWGvQ2GyjP|Rhx6o_PVHMlPMr(>S)9SWXTC1(Atsy;UZMNRkRqfWg zwgw5$ms5Y)Y$hR#EoNc2Irj7!a>QI6&O;eT%x0j0jK$V!$4IYG>1(xD&VkBlRni>WXf0n(Kdt7XR%21m@5v0O zt~Y*)IF@6KuVE|-2Q!`eOX3$)$6%iHZ)z)+#2fL2qpcbp>Z`_idc;g|c-osP`K*%F ziG`=c3jeR1m?(F{><-Uwn8Pwvzux+}WpdV<9l6ZZB61j%xmF|QTdi%@F6#+vueINL z$9m8D!aBvi%695va@guUes7->V_ITrp~q5{d#MwbiZABc zl~@?HTg9wu?XisQ7%B%yN_*H zp<8BN*%YgT_n_w@zqJPaMs$-_{pc4rG9J>)8XgJ#Vm^+)#JYcqd>#L*QCst0 z_}@j>X=@R$S7~P(k$pb)2=+g$sP7fT$47>cmyl`H(`ffFdYv7%F`q)8K>xk=S+**B z9lJ;wu`;j1|B>)Pk=E$Xl(ax&w8LfL7s3A+n68@eojpE92gL({&4&f7@VL*7S{ zhrE%b1bHXP0OYMCn<4Kdxfb$flItMvCb`v^x*j?CnVs~Kg$L_}7iaihe9r`^N`#rb-egV&er`VcyKD-KE01sQb z^ul|P&n#Wr2(Lq2?x15Z1MBH?Gh7FMXzSXoa28|SL?3sf55xQ6U6eftyC~a=z61SS n^oJ?miT)(q1$T4K55VWK55ax({TciKdja*0I_@c04+Ho=BE;$5 From cb06772f117cae5069c32ad47e41ff07f8ab7ca8 Mon Sep 17 00:00:00 2001 From: efinst0rm Date: Wed, 25 Aug 2021 11:06:52 -0500 Subject: [PATCH 2/2] Add support for IW5 (#213) --- GameFiles/IW5/storage/iw5/scripts/README.MD | 23 ++ .../storage/iw5/scripts/_customcallbacks.gsc | 249 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 GameFiles/IW5/storage/iw5/scripts/README.MD create mode 100644 GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc diff --git a/GameFiles/IW5/storage/iw5/scripts/README.MD b/GameFiles/IW5/storage/iw5/scripts/README.MD new file mode 100644 index 00000000..def15c99 --- /dev/null +++ b/GameFiles/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/IW5/storage/iw5/scripts/_customcallbacks.gsc new file mode 100644 index 00000000..4647187d --- /dev/null +++ b/GameFiles/IW5/storage/iw5/scripts/_customcallbacks.gsc @@ -0,0 +1,249 @@ +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; +#include common_scripts\utility; + +init() +{ + Print("IW4MADMIN Anti-Cheat Loaded"); + SetDvarIfUninitialized( "sv_customcallbacks", true ); + SetDvarIfUninitialized( "sv_framewaittime", 0.05 ); + SetDvarIfUninitialized( "sv_additionalwaittime", 0.1 ); + SetDvarIfUninitialized( "sv_maxstoredframes", 12 ); + SetDvarIfUninitialized( "sv_printradarupdates", 0 ); + SetDvarIfUninitialized( "sv_printradar_updateinterval", 500 ); + SetDvarIfUninitialized( "sv_iw4madmin_url", "http://127.0.0.1:1624" ); + + level thread onPlayerConnect(); + if (getDvarInt("sv_printradarupdates") == 1) + { + level thread runRadarUpdates(); + } + + level waittill( "prematch_over" ); + level.callbackPlayerKilled = ::Callback_PlayerKilled; + level.callbackPlayerDamage = ::Callback_PlayerDamage; + level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect; +} + +onPlayerConnect( player ) +{ + for( ;; ) + { + level waittill( "connected", player ); + player setClientDvar("cl_demo_enabled", 1); + player thread waitForFrameThread(); + player thread waitForAttack(); + } +} + +waitForAttack() +{ + self endon( "disconnect" ); + + self.lastAttackTime = 0; + + for( ;; ) + { + self notifyOnPlayerCommand( "player_shot", "+attack" ); + self waittill( "player_shot" ); + + self.lastAttackTime = getTime(); + } +} + +runRadarUpdates() +{ + interval = int(getDvar("sv_printradar_updateinterval")); + + for ( ;; ) + { + for ( i = 0; i <= 17; i++ ) + { + player = level.players[i]; + + if ( isDefined( player ) ) + { + payload = player.guid + ";" + player.origin + ";" + player getPlayerAngles() + ";" + player.team + ";" + player.kills + ";" + player.deaths + ";" + player.score + ";" + player GetCurrentWeapon() + ";" + player.health + ";" + isAlive(player) + ";" + player.timePlayed["total"]; + logPrint( "LiveRadar;" + payload + "\n" ); + } + } + + wait( interval / 1000 ); + } +} + +hitLocationToBone( hitloc ) +{ + switch( hitloc ) + { + case "helmet": + return "j_helmet"; + case "head": + return "j_head"; + case "neck": + return "j_neck"; + case "torso_upper": + return "j_spineupper"; + case "torso_lower": + return "j_spinelower"; + case "right_arm_upper": + return "j_shoulder_ri"; + case "left_arm_upper": + return "j_shoulder_le"; + case "right_arm_lower": + return "j_elbow_ri"; + case "left_arm_lower": + return "j_elbow_le"; + case "right_hand": + return "j_wrist_ri"; + case "left_hand": + return "j_wrist_le"; + case "right_leg_upper": + return "j_hip_ri"; + case "left_leg_upper": + return "j_hip_le"; + case "right_leg_lower": + return "j_knee_ri"; + case "left_leg_lower": + return "j_knee_le"; + case "right_foot": + return "j_ankle_ri"; + case "left_foot": + return "j_ankle_le"; + default: + return "tag_origin"; + } +} + +waitForFrameThread() +{ + self endon( "disconnect" ); + + self.currentAnglePosition = 0; + self.anglePositions = []; + + for (i = 0; i < getDvarInt( "sv_maxstoredframes" ); i++) + { + self.anglePositions[i] = self getPlayerAngles(); + } + + for( ;; ) + { + self.anglePositions[self.currentAnglePosition] = self getPlayerAngles(); + wait( getDvarFloat( "sv_framewaittime" ) ); + self.currentAnglePosition = (self.currentAnglePosition + 1) % getDvarInt( "sv_maxstoredframes" ); + } +} + +waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount ) +{ + currentIndex = self.currentAnglePosition; + wait( 0.05 * afterFrameCount ); + + self.angleSnapshot = []; + + for( j = 0; j < self.anglePositions.size; j++ ) + { + self.angleSnapshot[j] = self.anglePositions[j]; + } + + anglesStr = ""; + collectedFrames = 0; + i = currentIndex - beforeFrameCount; + + while (collectedFrames < beforeFrameCount) + { + fixedIndex = i; + if (i < 0) + { + fixedIndex = self.angleSnapshot.size - abs(i); + } + anglesStr += self.angleSnapshot[int(fixedIndex)] + ":"; + collectedFrames++; + i++; + } + + if (i == currentIndex) + { + anglesStr += self.angleSnapshot[i] + ":"; + i++; + } + + collectedFrames = 0; + + while (collectedFrames < afterFrameCount) + { + fixedIndex = i; + if (i > self.angleSnapshot.size - 1) + { + fixedIndex = i % self.angleSnapshot.size; + } + anglesStr += self.angleSnapshot[int(fixedIndex)] + ":"; + collectedFrames++; + i++; + } + + lastAttack = int(getTime()) - int(self.lastAttackTime); + isAlive = isAlive(self); + + logPrint(logString + ";" + anglesStr + ";" + isAlive + ";" + lastAttack + "\n" ); +} + +vectorScale( vector, scale ) +{ + return ( vector[0] * scale, vector[1] * scale, vector[2] * scale ); +} + +Process_Hit( type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon ) +{ + if (sMeansOfDeath == "MOD_FALLING" || !isPlayer(attacker)) + { + return; + } + + victim = self; + _attacker = attacker; + + if ( !isPlayer( attacker ) && isDefined( attacker.owner ) ) + { + _attacker = attacker.owner; + } + + else if( !isPlayer( attacker ) && sMeansOfDeath == "MOD_FALLING" ) + { + _attacker = victim; + } + + location = victim GetTagOrigin( hitLocationToBone( sHitLoc ) ); + isKillstreakKill = !isPlayer( attacker ) || isKillstreakWeapon( sWeapon ); + + logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + int(gettime()) + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";0;0"; + attacker thread waitForAdditionalAngles( logLine, 2, 2 ); +} + +Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ) +{ + if ( self.health - iDamage > 0 ) + { + isFriendlyFire = level.teamBased && isDefined( attacker ) && ( self != attacker ) && isDefined( attacker.team ) && ( self.pers[ "team" ] == attacker.team ); + + if ( !isFriendlyFire ) + { + self Process_Hit( "Damage", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon ); + } + } + + self maps\mp\gametypes\_damage::Callback_PlayerDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ); +} + +Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ) +{ + Process_Hit( "Kill", attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon ); + self maps\mp\gametypes\_damage::Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ); +} + +Callback_PlayerDisconnect() +{ + level notify( "disconnected", self ); + self maps\mp\gametypes\_playerlogic::Callback_PlayerDisconnect(); +} \ No newline at end of file