Skip to content

Commit

Permalink
Merge pull request #36 from Ardub92/main
Browse files Browse the repository at this point in the history
Cybergrind Synchronization
  • Loading branch information
xzxADIxzx authored Nov 22, 2023
2 parents 99bc603 + ffeef1a commit 428fe0d
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 1 deletion.
1 change: 1 addition & 0 deletions Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public void Init()
// initialize world components
World.World.Load(); // C# sucks
Movement.Load();
CyberGrind.Load();

// initialize ui components
WidescreenFix.Load();
Expand Down
4 changes: 3 additions & 1 deletion content/PacketType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ public enum PacketType
/// <summary> Need to activate a certain object. It can be anything, because there are a lot of different stuff in the game. </summary>
ActivateObject,
/// <summary> Any action with the cinema, like starting a video, pausing or rewinding. </summary>
CinemaAction
CinemaAction,
/// <summary> Any action with CyberGrind, like pattern and wave. </summary>
CybergrindAction
}
114 changes: 114 additions & 0 deletions harmony-patches/CyberGrindPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
namespace Jaket.HarmonyPatches;

using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

using Jaket.Net;
using Jaket.World;

#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable RCS1213 // Remove unused member declaration.

[HarmonyPatch(typeof(EndlessGrid), "LoadPattern")]
public class EndlessGridLoadPatternPatch
{
static void Prefix(ref ArenaPattern pattern)
{
// load current pattern in client
if (LobbyController.Lobby != null)
{
// send pattern if the player is the owner of the lobby
if (LobbyController.IsOwner) CyberGrind.Instance.SendPattern(pattern);
// replacing client pattern with server pattern on client
else pattern = CyberGrind.Instance.CurrentPattern;
}
}
}

[HarmonyPatch(typeof(EndlessGrid), "Start")]
public class EndlessGridStartPatch
{
static bool Prefix()
{
// getting cybergrind grid deathzone to change it later
foreach (var deathZone in Resources.FindObjectsOfTypeAll<DeathZone>()) if (deathZone.name == "Cube") CyberGrind.Instance.GridDeathZoneInstance = deathZone;

// don't skip original method
return true;
}

// use postfix to wait object to initialize
static void Postfix()
{
var cg = CyberGrind.Instance;
// check when the player in a lobby
if (LobbyController.Lobby != null || LobbyController.IsOwner)
{
// sets as first time
cg.LoadTimes = 0;
// check if current pattern is loaded and the player is the client
if (cg.CurrentPattern != null && !LobbyController.IsOwner)
// loads current pattern from the server
cg.LoadCurrentPattern();
// send empty pattern when game starts and the player is the owner to prevent load previous cybergrind pattern
else cg.SendPattern(new ArenaPattern());
}
else
{
// resetting values if the player is not in a lobby.
cg.LoadTimes = 0;
cg.CurrentWave = 0;
cg.CurrentPattern = null;
}
}
}

[HarmonyPatch(typeof(EndlessGrid), "OnTriggerEnter")]
public class EndlessGridOnTriggerEnterPatch
{
// dont allow to launch CyberGrind to client
static bool Prefix(ref Text ___waveNumberText)
{
// for some reason, the wave number text is not shown on the client
___waveNumberText.transform.parent.parent.gameObject.SetActive(value: true);
// don't activate trigger if the player is not the owner of the lobby
if (LobbyController.Lobby != null && !LobbyController.IsOwner) return false;
return true;
}
}

[HarmonyPatch(typeof(EndlessGrid), "Update")]
public class EndlessGridUpdatePatch
{
static bool Prefix(ref ActivateNextWave ___anw, EndlessGrid __instance)
{
// check if the player is not the owner of the lobby (client)
if (LobbyController.Lobby != null && !LobbyController.IsOwner)
{
// set the current wave on the client to original cybergrind singleton to sync with the server
__instance.currentWave = CyberGrind.Instance.CurrentWave;
// set death enemies to prevent start new wave on the client to sync it
___anw.deadEnemies = -999;
}
return true;
}

// use postfix to change object after original object is changed
static void Postfix(ref Text ___enemiesLeftText)
{
// check if the player is not the owner of the lobby (client)
if (LobbyController.Lobby != null && !LobbyController.IsOwner)
{
// we broke original death enemies count, so we need to create new counter based on EnemyTracker
var enemies = EnemyTracker.Instance.enemies;
// remove dead enemies or null enemies from the list
enemies.RemoveAll(e => e.dead || e is null);
// set enemies left text on the client, replacing original
___enemiesLeftText.text = EnemyTracker.Instance.enemies.Count.ToString();
}
// change y position of cybergrind grid deathzone when lobby created or not to prevent enemies randomly dying
var dz = CyberGrind.Instance.GridDeathZoneInstance;
if (dz != null) dz.transform.position = dz.transform.position with { y = LobbyController.Lobby != null ? -10 : 0.5f };
}
}
2 changes: 2 additions & 0 deletions net/end-points/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public override void Load()
Listen(PacketType.ActivateObject, r => World.Instance.ActivateObject(r.Int()));

Listen(PacketType.CinemaAction, r => Cinema.Play(r.String()));

Listen(PacketType.CybergrindAction, r => CyberGrind.Instance.LoadPattern(r.Int(), r.String()));
}

public override void Update()
Expand Down
110 changes: 110 additions & 0 deletions world/CyberGrind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
namespace Jaket.World;

using HarmonyLib;
using Jaket.Content;
using Jaket.IO;
using Jaket.Net;
using Jaket.UI;
using UnityEngine.UI;

/// <summary> Class responsible for Cybergrind synchronization </summary>
public class CyberGrind : MonoSingleton<CyberGrind>
{
/// <summary> UI Text, used for displaying current wave number. </summary>
public Text WaveNumberTextInstance;
/// <summary> UI Text, used for displaying current enemies left. </summary>
public Text EnemiesLeftTextInstance;
public DeathZone GridDeathZoneInstance;

/// <summary> Current wave count used for sync. </summary>
public int CurrentWave;
/// <summary> How many times pattern is loaded. Can't be bigger that 1. </summary>
public int LoadTimes;

/// <summary> Current pattern used for sync. </summary>
public ArenaPattern CurrentPattern;
public int LoadCount;

/// <summary> Sends the current arena pattern to all clients. </summary>
/// <param name="pattern"> Pattern to send to clients. </param>
public void SendPattern(ArenaPattern pattern)
{
// serialize pattern to send to clients
var data = SerializePattern(pattern);
// sending to clients
Networking.Redirect(Writer.Write(w =>
{
// wave number
w.Int(EndlessGrid.Instance.currentWave);
// pattern
w.String(data);
}), PacketType.CybergrindAction);
}

/// <summary> Load pattern serialized pattern and wave from server. </summary>
/// <param name="data"> String type represented in <see cref="SerializePattern(ArenaPattern)"/> </param>
public void LoadPattern(int currentWave, string data)
{
// sets current pattern to give it to LoadPattern method of original class
CurrentPattern = DeserializePattern(data);
// set current wave to synced one
CurrentWave = currentWave;
LoadPattern(CurrentPattern);
}

/// <summary> Loads pattern and invoking next wave. </summary>
/// <param name="pattern"> <see cref="ArenaPattern"/> to load. </param>
public void LoadPattern(ArenaPattern pattern)
{
// sets current pattern to give it to LoadPattern method of original class
CurrentPattern = pattern;
// start a new wave with server pattern
AccessTools.Method(typeof(EndlessGrid), "NextWave").Invoke(EndlessGrid.Instance, new object[] { });

// Do not make new wave if it is the first time
if (LoadTimes < 1)
{
LoadTimes++;
return;
}

// Resetting weapon charges (for example, railgun will be charged and etc.)
WeaponCharges.Instance.MaxCharges();

// play cheering sound effect
var cr = CrowdReactions.Instance;
cr.React(cr.cheerLong);

// resetting values after each wave
var nmov = NewMovement.Instance;
if (nmov.hp > 0)
{
nmov.ResetHardDamage();
nmov.exploded = false;
nmov.GetHealth(999, silent: true);
nmov.FullStamina();
}
}

/// <summary> Loads current pattern. </summary>
public void LoadCurrentPattern() => LoadPattern(CurrentPattern);

/// <summary> Deserialize pattern to load it to client. </summary>
/// <param name="data"> String to deserialize to <see cref="ArenaPattern"/>. </param>
public ArenaPattern DeserializePattern(string data)
{
// split a pattern into heights and prefabs.
string[] parts = data.Split('|');
return new ArenaPattern { heights = parts[0], prefabs = parts[1] };
}

/// <summary> Serializes pattern to string to send it to clients. </summary>
/// <param name="arena"> <see cref="ArenaPattern"/> to serialize to string. </param>
public string SerializePattern(ArenaPattern arena) => $"{arena.heights}|{arena.prefabs}";

public static void Load()
{
// initialize the singleton
UI.Object("CyberGrind").AddComponent<CyberGrind>();
}
}

0 comments on commit 428fe0d

Please sign in to comment.