NetServer
using LiteNetLib;
using LiteNetLib.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class SkyWarsServer : MonoBehaviour
{
[Header(“Properties”)]
public ushort server_port = 25565;
public const byte packetsPerSecond = 64;
private NetManager server;
private EventBasedNetListener listener;
public static SkyWarsServer me;
public Dictionary<NetPeer, int> players_ids;
public Dictionary<int, NetPlayerController> players;
private bool runServer;
private bool sharePlayersWithClient;
private int currentId;
public void Awake()
{
// Make sure only one presistent instance of the server is up
if(me != null)
{
Destroy(gameObject);
return;
}
currentId = int.MinValue;
me = this;
players = new Dictionary<int, NetPlayerController>(60);
players_ids = new Dictionary<NetPeer, int>(60);
DontDestroyOnLoad(this);
runServer = Application.isBatchMode || Application.isEditor;
if (!runServer) return;
//Setup the actual server
listener = new EventBasedNetListener();
server = new NetManager(listener);
Time.fixedDeltaTime = 1f / (float)SkyWarsServer.packetsPerSecond;
server.UpdateTime = Mathf.FloorToInt(Time.fixedDeltaTime * 1000);
server.Start(server_port);
Debug.Log(“Server started with " + server.UpdateTime + " ms update rate”);
listener.ConnectionRequestEvent += ConnectionRequest;
listener.PeerConnectedEvent += UserConnected;
listener.PeerDisconnectedEvent += UserDisconnected;
listener.NetworkReceiveEvent += OnDataReceived;
}
private void FixedUpdate()
{
if(runServer)
server.PollEvents();
}
private void OnDataReceived(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod)
{
if (!reader.TryGetByte(out byte pt)) return;
PacketType packetType = (PacketType)pt;
NetDataWriter packet;
int uid;
switch (packetType)
{
case PacketType.PlayerSpawnRequest:
// Spawn the player on the server & registers it
// Then proceeds to notify everyone else of this
HandlePlayerSpawning(peer, reader);
break;
case PacketType.ClientSerializeData:
// Send the player data to everyone and deserialize it
byte[ ] b = reader.RawData;
b[reader.UserDataOffset] = (byte)PacketType.ClientDeserializeData;
server.SendToAll(b, reader.UserDataOffset, reader.UserDataSize, 0, DeliveryMethod.Unreliable, peer);
break;
case PacketType.RequestPlayersFirstTime:
packet = new NetDataWriter(true, 100);
packet.Put((byte)PacketType.DeliverPlayersFirstTime);
packet.Put(players.Count);
foreach (var p in players)
packet.Put(p.Value.playerID);
peer.Send(packet, DeliveryMethod.ReliableOrdered);
break;
}
}
private void HandlePlayerSpawning(NetPeer peer, NetPacketReader reader)
{
Vector3 pos = new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat());
int uid = players_ids[peer];
//Make sure we handle this and send a packet saying player needs to die first
if (players.ContainsKey(uid)) return;
NetPlayerController pl;
pl = Instantiate(SkyWarsClient.me.player, pos, Quaternion.identity).GetComponent();
pl.CustomStart(uid, false);
players.Add(uid, pl);
var packet = new NetDataWriter(true, 20);
packet.Put((byte)PacketType.PlayerSpawnClient);
packet.Put(pos);
packet.Put(uid);
byte[ ] data = packet.Data;
server.SendToAll(data, 0, packet.Length, 0, DeliveryMethod.ReliableOrdered, peer);
packet.Put(true);
peer.Send(packet, DeliveryMethod.ReliableOrdered);
}
private void UserConnected(NetPeer peer)
{
int uid = currentId++;
players_ids.Add(peer, uid);
Debug.Log("Player connected " + uid);
}
private void UserDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
{
int uid = players_ids[peer];
players_ids.Remove(peer);
DestroyPlayer(uid);
NetDataWriter packet = new NetDataWriter(true, 50);
packet.Put((byte)PacketType.PlayerDestroy);
packet.Put(uid);
server.SendToAll(packet, 0, DeliveryMethod.ReliableOrdered, peer);
Debug.Log("Player disconnected " + uid);
}
public void ConnectionRequest(ConnectionRequest request)
{
if (server.PeersCount < 10)
request.AcceptIfKey(“nudes”);
else
request.Reject();
}
public void DestroyPlayer(int uid, bool remove = true)
{
if (!players.ContainsKey(uid)) return;
GameObject player = players[uid].gameObject;
var fpc = player.GetComponent<vp_FPController>();
fpc.Deactivate();
Destroy(player, 1f);
if(remove) players.Remove(uid);
}
private void OnDestroy()
{
if(runServer)
server.Stop();
}
}
NetClient
using LiteNetLib;
using LiteNetLib.Utils;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SkyWarsClient : MonoBehaviour
{
[Header(“Prefabs”)]
public GameObject player;
public GameObject[ ] prefabs;
[Header(“Properties”)]
public string server_ip = “127.0.0.1”;
public ushort server_port = 25565;
[System.NonSerialized]
public bool isConnected;
public EventBasedNetListener listener;
private NetManager client;
public NetPeer mypeer;
public static SkyWarsClient me;
public Dictionary<int, NetPlayerController> players;
private void Awake()
{
if (me != null)
{
Destroy(gameObject);
return;
}
me = this;
players = new Dictionary<int, NetPlayerController>(60);
DontDestroyOnLoad(this);
listener = new EventBasedNetListener();
client = new NetManager(listener);
Time.fixedDeltaTime = 1f / (float)SkyWarsServer.packetsPerSecond;
client.UpdateTime = Mathf.FloorToInt(Time.fixedDeltaTime * 1000);
client.Start();
client.Connect(server_ip, server_port, “nudes”);
listener.PeerConnectedEvent += OnClientConnected;
listener.PeerDisconnectedEvent += OnClientDisconnected;
listener.NetworkReceiveEvent += OnDataReceived;
}
private void Update()
{
client.PollEvents();
}
public NetPlayerController SpawnPlayer(int uid, bool owner, Vector3 pos)
{
var p = Instantiate(player, pos, Quaternion.identity).GetComponent();
p.CustomStart(uid, owner);
if (Application.isEditor)
{
SkyWarsServer.me.DestroyPlayer(uid, false);
SkyWarsServer.me.players[uid] = p;
}
if (players.ContainsKey(uid))
{
DestroyPlayer(uid, false);
players[uid] = p;
}
else players.Add(uid, p);
return p;
}
private void RequestSpawnPlayer(Vector3 pos)
{
NetDataWriter packet = new NetDataWriter(true, 20);
packet.Put((byte)PacketType.PlayerSpawnRequest);
packet.Put(pos.x);
packet.Put(pos.y);
packet.Put(pos.z);
mypeer.Send(packet, DeliveryMethod.ReliableOrdered);
}
private void RequestSpawnedPlayers()
{
NetDataWriter packet = new NetDataWriter(true, 50);
packet.Put((byte)PacketType.RequestPlayersFirstTime);
mypeer.Send(packet, DeliveryMethod.ReliableOrdered);
}
private void OnDataReceived(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod)
{
if (!reader.TryGetByte(out byte pt)) return;
PacketType packetType = (PacketType)pt;
int uid;
switch (packetType)
{
case PacketType.PlayerSpawnClient:
var pos = new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat());
uid = reader.GetInt();
if(reader.TryGetBool(out bool owner))
SpawnPlayer(uid, owner, pos);
else SpawnPlayer(uid, false, pos);
break;
case PacketType.ClientDeserializeData:
uid = reader.GetInt();
if(players.ContainsKey(uid))
players[uid].Deserialize(reader);
break;
case PacketType.DeliverPlayersFirstTime:
int playerCount = reader.GetInt();
for (int i = 0; i < playerCount; i++)
{
uid = reader.GetInt();
SpawnPlayer(uid, false, Vector3.zero);
}
RequestSpawnPlayer(new Vector3(-75.7f, 111f, -538.18f + Random.value * 20f));
break;
case PacketType.PlayerDestroy:
uid = reader.GetInt();
DestroyPlayer(uid);
break;
}
}
private void OnClientConnected(NetPeer peer)
{
isConnected = true;
mypeer = peer;
RequestSpawnedPlayers();
}
private void OnClientDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
{
isConnected = false;
}
public void DestroyPlayer(int uid, bool remove = true)
{
if (!players.ContainsKey(uid)) return;
GameObject player = players[uid].gameObject;
var fpc = player.GetComponent<vp_FPController>();
fpc.Deactivate();
Destroy(player, 1f);
if(remove) players.Remove(uid);
}
private void OnDestroy()
{
if(client != null)
client.Stop();
}
}
public enum PacketType
{
PlayerSpawnRequest,
PlayerSpawnClient,
ClientSerializeData,
ClientDeserializeData,
RequestPlayersFirstTime,
DeliverPlayersFirstTime,
PlayerDestroy
}
NetPlayerInput
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NetPlayerInput : MonoBehaviour
{
[Header(“Input Names”)]
public string[ ] buttons;
public string[ ] axis;
public Dictionary<string, int> keysMapping;
public Dictionary<string, NetKeyStates> keyStates;
private NetPlayerController networkPlayer;
//We should use some other way to sync the time but this will do for now
private float syncedDifference = -1f;
Queue<byte[ ]> recordedInput = new Queue<byte[ ]>(50);
Queue incomingInput = new Queue(50);
private float[ ] previousAxisState;
private void Awake()
{
keysMapping = new Dictionary<string, int>(buttons.Length + axis.Length);
keyStates = new Dictionary<string, NetKeyStates>(buttons.Length + axis.Length);
//Add buttons to the map
for (int b = 0; b < buttons.Length; b++)
{
keysMapping.Add(buttons**, b);**
keyStates.Add(buttons**, new NetKeyStates());**
}
//Add axis to the map
for (int a = 0; a < axis.Length; a++)
{
keysMapping.Add(axis[a], a + buttons.Length);
keyStates.Add(axis[a], new NetKeyStates());
}
previousAxisState = new float[axis.Length];
networkPlayer = GetComponent();
}
private NetKeyEvent nextKey;
void Update()
{
if (networkPlayer.isMine)
{
for (int i = 0; i < buttons.Length; i++)
{
NetKeyEvent keyEvent = null;
if (vp_Input.GetButtonDown(buttons*))
__{
keyEvent = new NetKeyEvent(i, true);
}__
else if (vp_Input.GetButtonUp(buttons))
_{
keyEvent = new NetKeyEvent(i, false);
}
if (keyEvent != null)
recordedInput.Enqueue(keyEvent.ToArray());
}
for (int i = 0; i < axis.Length; i++)
{
int j = i + buttons.Length;
NetKeyEvent keyEvent = null;_
float value = vp_Input.GetAxisRaw(axis);
if (value > 0 && previousAxisState <= 0)
_{
keyEvent = new NetKeyEvent(j, true, true);
}
else if (value < 0 && previousAxisState >= 0)
{
keyEvent = new NetKeyEvent(j, true, false);
}
else if (value == 0 && previousAxisState != 0)
{
keyEvent = new NetKeyEvent(j, false);
}
previousAxisState = value;
if (keyEvent != null)
recordedInput.Enqueue(keyEvent.ToArray());
}
}
else if (incomingInput.Count != 0)
{
if (nextKey == null) nextKey = incomingInput.Dequeue();
if (syncedDifference < 0) syncedDifference = nextKey.input_time;
if (syncedDifference >= nextKey.input_time)
{
syncedDifference = nextKey.input_time;
int id = nextKey.GetKeyID();
if (id < buttons.Length)
{
if (nextKey.IsPressed())
{
var keyState = keyStates[buttons[id]];
keyState.keyDown = true;
keyState.keyPressed = true;
}
else
{
var keyState = keyStates[buttons[id]];
keyState.keyUp = true;
keyState.keyPressed = false;
}
}
else
{
int axisId = id - buttons.Length;
if (nextKey.IsPressed())
{
var keyState = keyStates[axis[axisId]];
keyState.keyPressed = true;
if (nextKey.GetRawAxis() > 0)
keyState.keyUp = true;
else
keyState.keyUp = false;
}
else
{
var keyState = keyStates[axis[axisId]];
keyState.keyPressed = false;
}
}
nextKey = null;
}
}
//This seems to remove lag*
else syncedDifference = -1f;
syncedDifference += Time.deltaTime;
}
private void LateUpdate()
{
int i = 0;
foreach(var key in keyStates)
{
//Just process the buttons & not the axis
if (i >= buttons.Length) break;
key.Value.keyDown = false;
key.Value.keyUp = false;
i++;
}
}
public void ResyncTime()
{
syncedDifference = -1f;
}
public byte[ ] FlushRecordedData()
{
byte[ ] data = new byte[recordedInput.Count * NetKeyEvent.sizeofme];
int i = 0;
while(recordedInput.Count != 0)
{
var input = recordedInput.Dequeue();
System.Array.Copy(input, 0, data, i, 5);
i += NetKeyEvent.sizeofme;
}
return data;
}
public void ReplayInputData(byte[ ] data)
{
int count = data.Length / NetKeyEvent.sizeofme;_
for(int i = 0; i < count; i++)
{
incomingInput.Enqueue(new NetKeyEvent(data, i * NetKeyEvent.sizeofme));
}
}
public float GetAxisRaw(string axis)
{
if (keyStates[axis].keyPressed)
{
return keyStates[axis].keyUp ? 1f : -1f;
}
else return 0f;
}
public bool GetButton(string button)
{
return keyStates[button].keyPressed;
}
public bool GetButtonDown(string button)
{
return keyStates[button].keyDown;
}
public bool GetButtonUp(string button)
{
return keyStates[button].keyUp;
}
}
public class NetKeyStates
{
public bool keyDown, keyPressed, keyUp;
}
class NetKeyEvent
{
public const int sizeofme = 5;
public float input_time;
private byte input_id;
public NetKeyEvent(byte[ ] data, int pos)
{
this.input_time = System.BitConverter.ToSingle(data, pos);
this.input_id = data[pos + sizeofme - 1];
}
public NetKeyEvent(int input_id, bool pressed, bool positive = true)
{
this.input_time = Time.timeSinceLevelLoad;
this.input_id = (byte)input_id;
if(pressed) this.input_id = (byte)(input_id | 0b1000_0000);
if (positive) this.input_id = (byte)(this.input_id | 0b0100_0000);
}
public byte[ ] ToArray()
{
var b = new byte[5];
System.Array.Copy(System.BitConverter.GetBytes(input_time), b, 4);
b[4] = input_id;
return b;
}
public int GetKeyID()
{
return input_id & 0b0011_1111;
}
public float GetRawAxis()
{
return (input_id & 0b0100_0000) != 0 ? 1f : -1f;
}
public bool IsPressed()
{
return (input_id & 0b1000_0000) != 0;
}
}
NetPlayerController
using LiteNetLib;
using LiteNetLib.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NetPlayerController : MonoBehaviour
{
[Header("Player Input ")]
public NetPlayerInput networkInput;
[Header("Client/Server Differences ")]
public Component[ ] componentsToRemove;
public GameObject[ ] objectsToRemove;
public GameObject[ ] objectsToClientSide;
[NonSerialized] public int playerID;
[NonSerialized] public bool ready;
[NonSerialized] public bool isMine;
public void CustomStart(int playerID, bool mine)
{
this.playerID = playerID;
isMine = mine;
ready = true;
if(!isMine)
{
foreach (var c in componentsToRemove)
Destroy(c);
foreach (var c in objectsToRemove)
Destroy(c);
}
else
{
foreach (var c in objectsToClientSide)
Destroy(c);
}
}
public void FixedUpdate()
{
if (!ready) return;
Serialize();
}
public void Serialize()
{
if (isMine)
{
NetDataWriter packet = new NetDataWriter(true, 50);
packet.Put((byte)PacketType.ClientSerializeData);
packet.Put(playerID);
{
packet.Put(transform.position);
packet.Put(transform.rotation);
var inputData = networkInput.FlushRecordedData();
packet.Put(inputData.Length);
packet.Put(inputData);
}
SkyWarsClient.me.mypeer.Send(packet, DeliveryMethod.Unreliable);
}
}
///
/// Gets called on client & server and it stores
/// the key map data for later use
///
///
public void Deserialize(NetDataReader reader)
{
if (!isMine)
{
Vector3 pos = reader.GetVector3();
transform.rotation = reader.GetQuaternion();
int inputLength = reader.GetInt();
byte[ ] inputData = new byte[inputLength];
reader.GetBytes(inputData, inputLength);
networkInput.ReplayInputData(inputData);
}
}
}