Search Unity

(updated with Scripts) Howdy fellas! A bit stuck and wondering if any of you could help!

Discussion in 'Multiplayer' started by d12duke, Apr 20, 2019.

  1. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    Currently a friend and myself have begun a unique approach to Unet. The code works fine but we're experiencing some strange issues and even at one point had quite a bit of lag (most likely due to input.time) but now that's sorted out.

    I was hoping someone could help me run through the code and maybe see what's causing the malfunctions.

    If you have experience in netcoding with unet and want to help send me a PM.

    **Note, any help given will be documented and credited and This will be free to all users once completed. .. (I would post it here, but it's a total of five scripts and it's a bit overwhelming for a thread).
     
  2. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    Okay, I'm gonna paste the scripts. Perhaps after someone implements this they can help me out.
     
  3. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    I really hate html lol
     
    Last edited: Apr 21, 2019
  4. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    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<NetPlayerController>();
    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();
    }
    }
    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<NetPlayerController>();
    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
    }
    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<NetKeyEvent> incomingInput = new Queue<NetKeyEvent>(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<NetPlayerController>();
    }

    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;
    }
    }
    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);
    }
    }

    /// <summary>
    /// Gets called on client & server and it stores
    /// the key map data for later use
    /// </summary>
    /// <param name="reader"></param>
    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);
    }
    }
    }
     
  5. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    Now if you were just using pure connection information, this would work like a charm with NO problem, but what we are attempting is to create an authoritative server (with movements etc).

    Basically, you send the information to the 'server', the server then tells you if it's okay to do that and then sends all of that information to every player.

    This is barebones. There is no packet compression, no "zoning".. It's just the baremetal basics of an authoritative server.

    So if any of you guys care to take a look and maybe see a mistake, I'd Love to retain any knowledge given!
     
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'm confused. Your OP says you are using Unet, but the first line in most of your scripts is "using LiteNetLib;". I don't get what you're doing with that. Also, I've reread your posts several times and can't determine what specific issues you're asking for help with beyond "malfunctions." State the specific issues so people helping for free know what to look for. If you make people guess they just hit the back button.

    Lastly, format your code so it is readable with code tags, or you're just wasting everyone's time posting code that no one will read.
     
  7. d12duke

    d12duke

    Joined:
    Mar 22, 2016
    Posts:
    37
    That's fair, and apologies. I've never asked for help before. With that said, We got it sorted out and it's working beautifully now sir. Thank you for the post and I'll try to keep that in mind if I post another thread asking for help.
     
    Joe-Censored likes this.