Skip to content

Commit

Permalink
Handle Roles with Tower Defense Mode
Browse files Browse the repository at this point in the history
- Creates generic methods that work with both MP_Strategy and MP_TowerDefense modes for capturing player role changes and whether to permit commander applicants
  • Loading branch information
data-bomb committed Nov 16, 2024
1 parent 4a53374 commit 77aab4f
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 61 deletions.
198 changes: 138 additions & 60 deletions Si_AdminMod/Event_Roles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ You should have received a copy of the GNU General Public License
using System.Reflection;
using System.Data;
using System.Globalization;
using static MelonLoader.MelonLogger;


#if NET6_0
using Il2Cpp;
Expand All @@ -41,7 +43,8 @@ namespace SilicaAdminMod
public static class Event_Roles
{
#if !NET6_0
public static byte ERPC_RequestRole = HelperMethods.FindByteValueInEnum(typeof(MP_Strategy), "ERPCs", "REQUEST_ROLE");
public static byte ERPC_Strategy_RequestRole = HelperMethods.FindByteValueInEnum(typeof(MP_Strategy), "ERPCs", "REQUEST_ROLE");
public static byte ERPC_TowerDefense_RequestRole = HelperMethods.FindByteValueInEnum(typeof(MP_TowerDefense), "ERPCs", "REQUEST_ROLE");
#endif
public static event EventHandler<OnRequestCommanderArgs> OnRequestCommander = delegate { };
public static event EventHandler<OnRoleChangedArgs> OnRoleChanged = delegate { };
Expand All @@ -62,96 +65,171 @@ public static bool Prefix(MP_Strategy __instance, ref GameByteStreamReader __0,
#if NET6_0
if (__1 != (byte)MP_Strategy.ERPCs.REQUEST_ROLE)
#else
if (__1 != ERPC_RequestRole)
if (__1 != ERPC_Strategy_RequestRole)
#endif
{
return true;
}

Player requestingPlayer = Player.FindPlayer((NetworkID)__0.ReadUInt64(), (int)__0.ReadByte());
GameModeExt.ETeamRole eRole = (GameModeExt.ETeamRole)__0.ReadByte();
return ProcessRequestRole(__instance, ref __0, __1);
}
catch (Exception error)
{
HelperMethods.PrintError(error, "Failed to run MP_Strategy::ProcessNetRPC");
}

if (requestingPlayer == null)
{
MelonLogger.Warning("Cannot find player in role request.");
return false;
}
return true;
}
}

// would the game code treat it as an infantry/no role request?
if (eRole != GameModeExt.ETeamRole.COMMANDER || __instance.GetCommanderForTeam(requestingPlayer.Team))
[HarmonyPatch(typeof(MP_TowerDefense), nameof(MP_TowerDefense.ProcessNetRPC))]
static class ApplyPatch_MPTowerDefense_RequestRole
{
public static bool Prefix(MP_TowerDefense __instance, ref GameByteStreamReader __0, byte __1)
{
try
{
if (__instance == null || __0 == null)
{
__0 = RestoreRPC_RequestRoleReader(requestingPlayer, eRole);
FireOnRoleChangedEvent(requestingPlayer, eRole);
return true;
}

BaseTeamSetup baseTeamSetup = __instance.GetTeamSetup(requestingPlayer.Team);
if (baseTeamSetup == null)
// only look at RPC_RequestRole
#if NET6_0
if (__1 != (byte)MP_TowerDefense.ERPCs.REQUEST_ROLE)
#else
if (__1 != ERPC_TowerDefense_RequestRole)
#endif
{
return false;
return true;
}

OnRequestCommanderArgs onRequestCommanderArgs = FireOnRequestCommanderEvent(requestingPlayer);
return ProcessRequestRole(__instance, ref __0, __1);
}
catch (Exception error)
{
HelperMethods.PrintError(error, "Failed to run MP_TowerDefense::ProcessNetRPC");
}

if (onRequestCommanderArgs.Block)
{
if (SiAdminMod.Pref_Admin_DebugLogMessages.Value)
{
MelonLogger.Msg("Blocking commander role request for " + onRequestCommanderArgs.Requester.PlayerName);
}

if (!onRequestCommanderArgs.PreventSpawnWhenBlocked)
{
if (SiAdminMod.Pref_Admin_DebugLogMessages.Value)
{
MelonLogger.Msg("Preventing Spawn");
}
__instance.SpawnUnitForPlayer(requestingPlayer, requestingPlayer.Team);
FireOnRoleChangedEvent(requestingPlayer, GameModeExt.ETeamRole.INFANTRY);
}

return false;
}
return true;
}
}

public static bool ProcessRequestRole<T>(T gameModeInstance, ref GameByteStreamReader reader, byte rpcIndex) where T : GameModeExt
{
Player requestingPlayer = Player.FindPlayer((NetworkID)reader.ReadUInt64(), (int)reader.ReadByte());
GameModeExt.ETeamRole eRole = (GameModeExt.ETeamRole)reader.ReadByte();

if (requestingPlayer == null)
{
MelonLogger.Warning("Cannot find player in role request.");
return false;
}

// would the game code treat it as an infantry/no role request?
if (eRole != GameModeExt.ETeamRole.COMMANDER || gameModeInstance.GetCommanderForTeam(requestingPlayer.Team))
{
reader = RestoreRPC_RequestRoleReader(gameModeInstance, requestingPlayer, eRole);
FireOnRoleChangedEvent(requestingPlayer, eRole);
return true;
}

BaseTeamSetup? baseTeamSetup = GetTeamSetup(gameModeInstance, requestingPlayer.Team);
if (baseTeamSetup == null)
{
return false;
}

OnRequestCommanderArgs onRequestCommanderArgs = FireOnRequestCommanderEvent(requestingPlayer);

if (onRequestCommanderArgs.Block)
{
if (SiAdminMod.Pref_Admin_DebugLogMessages.Value)
{
MelonLogger.Msg("Blocking commander role request for " + onRequestCommanderArgs.Requester.PlayerName);
}

if (!onRequestCommanderArgs.PreventSpawnWhenBlocked)
{
if (SiAdminMod.Pref_Admin_DebugLogMessages.Value)
{
MelonLogger.Msg("Allowing to join commander");
MelonLogger.Msg("Preventing Spawn");
}
#if NET6_0
__instance.SetCommander(baseTeamSetup.Team, requestingPlayer);
__instance.RPC_SynchCommander(baseTeamSetup.Team);
#else
Type strategyType = typeof(MP_Strategy);
MethodInfo setCommanderMethod = strategyType.GetMethod("SetCommander", BindingFlags.Instance | BindingFlags.NonPublic);
setCommanderMethod.Invoke(__instance, parameters: new object?[] { baseTeamSetup.Team, requestingPlayer });
gameModeInstance.SpawnUnitForPlayer(requestingPlayer, requestingPlayer.Team);
FireOnRoleChangedEvent(requestingPlayer, GameModeExt.ETeamRole.INFANTRY);
}

MethodInfo synchCommanderMethod = strategyType.GetMethod("RPC_SynchCommander", BindingFlags.Instance | BindingFlags.NonPublic);
synchCommanderMethod.Invoke(__instance, new object[] { baseTeamSetup.Team });
#endif
return false;
}

FireOnRoleChangedEvent(requestingPlayer, GameModeExt.ETeamRole.COMMANDER);
if (SiAdminMod.Pref_Admin_DebugLogMessages.Value)
{
MelonLogger.Msg("Allowing to join commander");
}

return false;
}
catch (Exception error)
#if NET6_0
if (gameModeInstance is MP_Strategy strategyInstance)
{
strategyInstance.SetCommander(baseTeamSetup.Team, requestingPlayer);
strategyInstance.RPC_SynchCommander(baseTeamSetup.Team);
}
else if (gameModeInstance is MP_TowerDefense defenseInstance)
{
defenseInstance.SetCommander(baseTeamSetup.Team, requestingPlayer);
defenseInstance.RPC_SynchCommander(baseTeamSetup.Team);
}
#else
Type gameModeType = gameModeInstance.GetType();
MethodInfo setCommanderMethod = gameModeType.GetMethod("SetCommander", BindingFlags.Instance | BindingFlags.NonPublic);
setCommanderMethod.Invoke(gameModeInstance, parameters: new object?[] { baseTeamSetup.Team, requestingPlayer });

MethodInfo synchCommanderMethod = gameModeType.GetMethod("RPC_SynchCommander", BindingFlags.Instance | BindingFlags.NonPublic);
synchCommanderMethod.Invoke(gameModeInstance, new object[] { baseTeamSetup.Team });
#endif

FireOnRoleChangedEvent(requestingPlayer, GameModeExt.ETeamRole.COMMANDER);

return false;
}

public static BaseTeamSetup? GetTeamSetup<T>(T gameModeInstance, Team team) where T : GameModeExt
{
foreach (BaseTeamSetup baseTeamSetup in gameModeInstance.BaseTeamSetups)
{
if (gameModeInstance.GetTeamSetupActive(baseTeamSetup) && baseTeamSetup.Team == team)
{
HelperMethods.PrintError(error, "Failed to run MP_Strategy::ProcessNetRPC");
return baseTeamSetup;
}

return true;
}

return null;
}

public static GameByteStreamReader RestoreRPC_RequestRoleReader(Player requestingPlayer, GameModeExt.ETeamRole role)
public static GameByteStreamReader RestoreRPC_RequestRoleReader<T>(T gameModeInstance, Player requestingPlayer, GameModeExt.ETeamRole role) where T : GameModeExt
{
GameByteStreamWriter gameByteStreamWriter = GameByteStreamWriter.GetGameByteStreamWriter(0U, "Si_AdminMod::RestoreRPC_RequestRoleReader", true);
gameByteStreamWriter.WriteByte((byte)ENetworkPacketType.GameModeRPC);
gameByteStreamWriter.WriteByte(0);
#if NET6_0
gameByteStreamWriter.WriteByte((byte)MP_Strategy.ERPCs.REQUEST_ROLE);
#else
gameByteStreamWriter.WriteByte(ERPC_RequestRole);
#endif
//#if NET6_0
if (gameModeInstance is MP_Strategy)
{
#if NET6_0
gameByteStreamWriter.WriteByte((byte)MP_Strategy.ERPCs.REQUEST_ROLE);
#else
gameByteStreamWriter.WriteByte((byte)ERPC_Strategy_RequestRole);
#endif
}
else if (gameModeInstance is MP_TowerDefense)
{
#if NET6_0
gameByteStreamWriter.WriteByte((byte)MP_TowerDefense.ERPCs.REQUEST_ROLE);
#else
gameByteStreamWriter.WriteByte((byte)ERPC_TowerDefense_RequestRole);
#endif
}
//#else
//gameByteStreamWriter.WriteByte(ERPC_Strategy_RequestRole);
//#endif
gameByteStreamWriter.WriteUInt64((ulong)requestingPlayer.PlayerID);
gameByteStreamWriter.WriteByte((byte)requestingPlayer.PlayerChannel);
gameByteStreamWriter.WriteByte((byte)role);
Expand Down
2 changes: 1 addition & 1 deletion Si_AdminMod/ModAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License
using SilicaAdminMod;
using System.Drawing;

[assembly: MelonInfo(typeof(SiAdminMod), "Admin Mod", "2.0.965", "databomb", "https://github.com/data-bomb/Silica")]
[assembly: MelonInfo(typeof(SiAdminMod), "Admin Mod", "2.0.995", "databomb", "https://github.com/data-bomb/Silica")]
[assembly: MelonGame("Bohemia Interactive", "Silica")]

// Color.Cyan
Expand Down

0 comments on commit 77aab4f

Please sign in to comment.