<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterLookHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterLookHandler : BaseCommandHandler<CharacterLook>
    {
        private Firerer firererPrefab;
        private DiContainer container;

        public CharacterLookHandler(IncomingCommandsQueue incomings, Firerer firererPrefab, DiContainer container) : base(incomings)
        {
            this.firererPrefab = firererPrefab;
            this.container = container;
        }

        protected override void OnCommand(CharacterLook command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);

            if (!repository.Character)
            {
                return;
            }

            repository.Character.ShootDirection(command.shootDirection);
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/CommandsHandles/EntityCreateHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class EntityCreateHandler : BaseCommandHandler<EntityCreate>
    {
        private DiContainer container;

        public EntityCreateHandler(IncomingCommandsQueue incomings, DiContainer container) : base(incomings)
        {
            this.container = container;
        }

        protected override void OnCommand(EntityCreate command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);
            repository.SpawnPrefab(command.prefab, command.position, command.forward, command.id);
        }
    }
}


<|start_filename|>assets-folder/Code/UI/RespawnView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class RespawnView : BaseView
    {
        [SerializeField]
        private GameObject awaitTransform;
        [SerializeField]
        private GameObject processTransform;
        [SerializeField]
        private UnityEngine.UI.Text respawnTimer;

        public Action OnRespawn;

        public bool Await
        {
            set
            {
                awaitTransform.SetActive(value);
                processTransform.SetActive(!value);
            }
        }

        public void RespawnButton()
        {
            OnRespawn();
        }

        public void RespawnWait(float time)
        {
            respawnTimer.text = time.ToString("0.00");
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/ClientStartGameLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace LlapiExample
{
    public class ClientStartGameLogic : INetworkLogic, IClientLogic
    {
        [Inject]
        private IncomingCommandsQueue incomings;
        [Inject]
        private ConnectionStatus status;
        [Inject]
        private ConnectionsRepository connections;

        public ClientStartGameLogic()
        {
        }

        public void Update()
        {
        }

        public void Setup()
        {
            incomings.Observable.Subscribe(OnMessageIncome);
            connections.Connections.Subscribe(OnConnected);
            connections.Disconnections.Subscribe(OnDisconnected);
        }

        private void OnConnected(int id)
        {
            status.Remove(ConnectionState.Disconnected);
            status.Remove(ConnectionState.Connecting);
            status.Append(ConnectionState.Connected);
        }

        private void OnDisconnected(int id)
        {
            status.Set(ConnectionState.Disconnected);
        }


        private void OnMessageIncome(ICommand cmd)
        {
            if(cmd is StartGame)
            {
                var task = SceneManager.LoadSceneAsync(1, LoadSceneMode.Additive);
                var time = 0f;
                var timer = new System.Diagnostics.Stopwatch();
                timer.Start();
                while (!task.isDone && time < 1f)
                {
                    time += timer.ElapsedMilliseconds / 1000f;
                }
                timer.Stop();

                SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex(1));

                status.Append(ConnectionState.InGame);
            }
        }
    }
}

<|start_filename|>assets-folder/Code/UI/StatusView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;

namespace LlapiExample
{
    public class StatusView : BaseView
    {
        public UnityEngine.UI.Text StatusText;

        public string Message
        {
            set
            {
                StatusText.text = value;
            }
        }
    }
}

<|start_filename|>assets-folder/Code/UI/ClientConfigView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class ClientConfigView : BaseView
    {
        public Action<int> OnPortChange;
        public Action<string> OnAddressChange;
        public Action OnStart;

        public void AddressChange(string address)
        {
            OnAddressChange(address);
        }

        public void PortChange(string port)
        {
            OnPortChange(int.Parse(port));
        }

        public void StartButton()
        {
            OnStart();
        }
    }
}

<|start_filename|>assets-folder/Code/UI/ServerConfigView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class ServerConfigView : BaseView
    {
        public Action<int> OnPortChange;
        public Action OnStart;

        public void PortChange(string port)
        {
            OnPortChange(int.Parse(port));
        }

        public void StartButton()
        {
            OnStart();
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/CommandsQueu.cs<|end_filename|>
﻿using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;

namespace LlapiExample
{
    public class IncomingCommandsQueue : CommandsQueue { }
    public class OutgoingCommandsQueue : CommandsQueue { }

    public class CommandsQueue : IEnumerable<ICommand>
    {
        private Queue<ICommand> commands;
        private Subject<ICommand> commandsStream;
        public IObservable<ICommand> Observable { get { return commandsStream; } }

        public CommandsQueue()
        {
            commands = new Queue<ICommand>();
            commandsStream = new Subject<ICommand>();
        }

        public void Enqueue(ICommand cmd)
        {
            commands.Enqueue(cmd);
            commandsStream.OnNext(cmd);
        }

        public void Flush()
        {
            commands.Clear();
        }

        public IEnumerator<ICommand> GetEnumerator()
        {
            return commands.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return commands.GetEnumerator();
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/RepositoriesForConnectionLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;

namespace LlapiExample
{
    public class RepositoriesForConnectionLogic : INetworkLogic
    {
        [Inject]
        private ConnectionsRepository connections;

        [Inject]
        private DiContainer container;

        public void Setup()
        {
            connections.Connections.Subscribe(OnConnect);
            connections.Disconnections.Subscribe(OnDisconnect);
        }

        private void OnConnect(int connectionId)
        {
            var instance = container.Instantiate<CommanderStatus>();
            instance.SetOwner(connectionId);
            container.Bind<CommanderStatus>().WithId(connectionId).FromInstance(instance).AsCached();
        }

        private void OnDisconnect(int connectionId)
        {
            container.UnbindId<CommanderStatus>(connectionId);
        }
        
        public void Update()
        {
        }
    }
}


<|start_filename|>assets-folder/Code/Behaviours/Entities/Firerer.cs<|end_filename|>
﻿using UnityEngine;
using UnityEngine.AI;
using System.Collections;
using System;
using Zenject;

namespace LlapiExample
{
    [RequireComponent(typeof(NavMeshAgent))]
    [RequireComponent(typeof(Animator))]
    public partial class Firerer : VitalEntity
    {
        #region Editor Fields
        private NavMeshAgent cachedAgent;
        public NavMeshAgent Agent
        {
            get
            {
                if (!cachedAgent)
                    cachedAgent = GetComponent<NavMeshAgent>();
                return cachedAgent;
            }
        }
        private Animator cachedAnimator;
        public Animator Animator
        {
            get
            {
                if (!cachedAnimator)
                    cachedAnimator = GetComponent<Animator>();
                return cachedAnimator;
            }
        }
        public Transform SpineBone;
        public Transform RootBone;
        public Transform WeaponBone;
        #endregion

        #region Dependencies
        [Inject]
        private DiContainer container;
        [Inject]
        private OutgoingCommandsQueue outgoings;
        #endregion

        public Action OnShoot;
        public Action OnBuild;
        public Action OnDie;
        public Action<Vector3, float> OnBuilt;
        public Action<Vector3, Vector3> OnBullet;

        public enum StateFlags : byte
        {
            Dead        = 0,
            Alive       = 1 << 0,

            Run         = 1 << 1,
            Attack      = 1 << 2,
            Damage      = 1 << 3,
            Build       = 1 << 4
        }


        public bool Dead
        {
            get
            {
                return State == StateFlags.Dead;
            }
            private set
            {
                State = value ? StateFlags.Dead : State;
            }
        }

        public bool IsDamage   { get { return (State & StateFlags.Damage) == StateFlags.Damage; } }
        public bool IsRunning  { get { return (State & StateFlags.Run) == StateFlags.Run; } }
        public bool IsShooting { get { return (State & StateFlags.Attack) == StateFlags.Attack; } }
        public bool IsBuilding { get { return (State & StateFlags.Build) == StateFlags.Build; } }

        public StateFlags State;

        public Vector3 TargetPosition;
        public float MaxSpeed;


        public Vector3 shootDirection;

        private Quaternion spineOrientation;

        private void Awake()
        {
            TargetPosition = transform.position;
            Agent.speed = MaxSpeed;
            spineOrientation = SpineBone.transform.localRotation;
            shootDirection = Vector3.forward;
            State = StateFlags.Alive;
        }

        public override void Update()
        {
            base.Update();
            if (Dead)
            {
                // Dead state
                Agent.Stop();
                Animator.SetFloat("Speed", 0f);
                Animator.SetBool("Fire", false);
                Animator.SetBool("Damage", false);
                RootBone.localRotation = Quaternion.identity;
                SpineBone.localRotation = spineOrientation;
            }
            else if (IsDamage)
            {
                // Taken damage state
                // Prevent running when taken damage
                Agent.speed = 0f;
                Animator.SetFloat("Speed", 0f);
            }
            else
            {
                // allow agent to move with max speed
                Agent.speed = MaxSpeed;

                // calculate angle between forward direction and looking direction
                var angle = Vector3.Dot(transform.forward, shootDirection);
                var side = angle < 0f ? -1 : 1;

                // set correct animation speed
                Animator.SetFloat("Speed", (Agent.velocity.magnitude / MaxSpeed) * side);

                if (angle < 0)
                {
                    SpineBone.localRotation = Quaternion.LookRotation(transform.InverseTransformDirection(Quaternion.Euler(0, 180f, 0) * shootDirection), Vector3.up) * spineOrientation;
                    RootBone.localRotation = Quaternion.Euler(0, 180f, 0);
                } else
                {
                    RootBone.localRotation = Quaternion.Euler(0, 0, 0);
                    SpineBone.localRotation = Quaternion.LookRotation(transform.InverseTransformDirection(shootDirection), Vector3.up) * spineOrientation;
                }

                if (IsShooting)
                {
                    // Shooting state
                }
                else if (IsRunning)
                {
                    // Running state
                    var distanceSqr = (Agent.destination - transform.position).sqrMagnitude;
                    if (distanceSqr < 0.1f)
                        State &= ~StateFlags.Run;
                }

            }
        }

        private void FireEvent()
        {
            Animator.SetBool("Fire", false);
            if (IsShooting)
            {
                if (IsMine)
                {
                    Debug.Log("Spawn bullet " + this);
                    var position = WeaponBone.position + shootDirection * 0.65f;
                    var entity = status.SpawnBullet(position, shootDirection);

                    var cmd = new EntityCreate();
                    cmd.prefab = PrefabType.Bullet;
                    cmd.id = entity.Id;
                    cmd.position = position;
                    cmd.forward = shootDirection;
                    outgoings.Enqueue(cmd);
                }

                State &= ~StateFlags.Attack;
            }
            else if(IsBuilding)
            {
                if (IsMine)
                {
                    Debug.Log("Spawn Crate " + this);
                    var position = transform.position + shootDirection * 1.2f + Vector3.up * 1.5f;
                    var entity = status.SpawnCrate(position);

                    var cmd = new EntityCreate();
                    cmd.prefab = PrefabType.Crate;
                    cmd.id = entity.Id;
                    cmd.position = position;
                    outgoings.Enqueue(cmd);
                }

                State &= ~StateFlags.Build;
            }
            // TODO: Authority
        }

        public override float CurrentHealth
        {
            get
            {
                return health;
            }
        }

        public override float MaxHealth
        {
            get { return 50f; }
        }

        private float health;

        public override void Start()
        {
            base.Start();
            health = MaxHealth;
        }

        public override void DamageReceive(float damage, Vector3 point, Vector3 force)
        {
            health -= damage;
            if (health > 0)
            {
                TakenDamage();
            }
            else
            {
                Die();
            }
        }

        private void DamageEvent()
        {
            Animator.SetBool("Damage", false);
            State &= ~StateFlags.Damage;
        }
        
        public void Move(Vector3 position)
        {
            if (Dead) return;
            State |= StateFlags.Run;
            Agent.destination = position;
        }

        public void Translate(Vector3 position)
        {
            if (Dead) return;
            transform.position = position;
        }

        public void Shoot()
        {
            if (Dead || IsDamage) return;
            if (!IsShooting && !IsBuilding)
            {
                State |= StateFlags.Attack;
                Animator.SetBool("Fire", true);
                if (OnShoot != null) OnShoot();
            }
        }

        public void Build()
        {
            if (Dead || IsDamage) return;
            if (!IsShooting && !IsBuilding)
            {
                State |= StateFlags.Build;
                Animator.SetBool("Fire", true);
                if (OnBuild != null) OnBuild();
            }
        }

        public void ShootDirection(Vector3 direction)
        {
            if (Dead) return;
            shootDirection = direction;
        }

        public void TakenDamage()
        {
            if (Dead) return;
            State |= StateFlags.Damage;
            Animator.SetBool("Damage", true);
        }

        public void Die()
        {
            if (Dead) return;
            State = StateFlags.Dead;
            Animator.SetBool("Die", true);

            if (IsMine)
            {
                if (OnDie != null) OnDie();

                var remove = new EntityRemove();
                remove.id = Id;
                outgoings.Enqueue(remove);
                status.Destroy(this);
            }
        }
        
        private void OnDrawGizmos()
        {
            var tmpColor = Gizmos.color;
            Gizmos.color = new Color(0f, 1f, 0f, 0.55f);
            Gizmos.DrawSphere(Agent.destination, 0.2f);

            Gizmos.color = new Color(0f, 1f, 0f, 1f);
            var start = transform.position;
            foreach(var end in Agent.path.corners)
            {
                Gizmos.DrawLine(start, end);
                start = end;
            }
            Gizmos.DrawLine(start, Agent.destination);

            Gizmos.color = tmpColor;
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/Connections.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
using UniRx;

namespace LlapiExample
{
    public class ConnectionsRepository : IEnumerable<int>
    {
        private HashSet<int> connections;
        private Subject<int> newConnection;
        private Subject<int> newDisconnection;

        public ConnectionsRepository()
        {
            newConnection = new Subject<int>();
            newDisconnection = new Subject<int>();
            connections = new HashSet<int>();
        }

        public IObservable<int> Connections { get { return newConnection;  } }
        public IObservable<int> Disconnections { get { return newDisconnection; } }

        public int Count { get { return connections.Count; } }

        public bool Add(int connection)
        {
            if(connections.Add(connection))
            {
                newConnection.OnNext(connection);
                return true;
            }
            return false;
        }

        public bool Remove(int connection)
        {
            if (connections.Remove(connection))
            {
                newDisconnection.OnNext(connection);
                return true;
            }
            return false;
        }

        public int[] Clone()
        {
            return connections.ToArray();
        }

        public IEnumerator<int> GetEnumerator()
        {
            return connections.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return connections.GetEnumerator();
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/CharacterShoot.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class CharacterShoot : BaseCommand, ICommand
    {
        public CharacterShoot() : base(CommandIds.Character_Shoot)
        {

        }

        public object[] Data()
        {
            return new object[0];
        }

        public void Read(object[] buffer, ref int position)
        {
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterBuiltHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterBuiltHandler : BaseCommandHandler<CharacterBuilt>
    {
        private DiContainer container;

        public CharacterBuiltHandler(IncomingCommandsQueue incomings, DiContainer container) : base(incomings)
        {
            this.container = container;
        }

        protected override void OnCommand(CharacterBuilt command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);

            if (!repository.Character)
            {
                return;
            }

            //repository.SpawnCrate(command.position, command.size);
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterShootHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterShootHandler : BaseCommandHandler<CharacterShoot>
    {
        private DiContainer container;

        public CharacterShootHandler(IncomingCommandsQueue incomings, DiContainer container) : base(incomings)
        {
            this.container = container;
        }

        protected override void OnCommand(CharacterShoot command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);

            if (!repository.Character)
            {
                return;
            }

            repository.Character.Shoot();
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/SyncInputLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class SyncInputLogic : INetworkLogic
    {
        [Inject] private OutgoingCommandsQueue outgoings;
        [Inject(Id = 0)] private CommanderStatus character;
        [Inject] private DiContainer container;

        public SyncInputLogic()
        {

        }

        public void OnShoot()
        {
            outgoings.Enqueue(container.Instantiate<CharacterShoot>());
        }

        public void Setup()
        {
            character.ObservableCharacter.Subscribe(OnCharacter);
        }

        private void OnCharacter(Firerer character)
        {
            if (!character) return;

            character.OnShoot += OnShoot;
            character.OnBuild += OnShoot;
        }

        public void Update()
        {
            if (!character.Character || !character.Character.gameObject)
                return;

            if (character.Character.Dead)
                return;

            outgoings.Enqueue(container.Instantiate<CharacterLook>());
            outgoings.Enqueue(container.Instantiate<CharacterMove>());
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterBulletHandler .cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterBulletHandler : BaseCommandHandler<CharacterBullet>
    {
        private DiContainer container;

        public CharacterBulletHandler(IncomingCommandsQueue incomings, DiContainer container) : base(incomings)
        {
            this.container = container;
        }

        protected override void OnCommand(CharacterBullet command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);

            if (!repository.Character)
            {
                return;
            }

            //repository.Character.SpawnBullet(command.position, command.forward);
        }
    }
}


<|start_filename|>assets-folder/Code/Bootstrap.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using Zenject;
using System;
using UniRx;

namespace LlapiExample
{
    public class Bootstrap : MonoInstaller
    {
        public StatusView Status;
        public SelectRoleView Role;
        public ServerConfigView ServerConfig;
        public ClientConfigView ClientConfig;
        public StartGameView StartGame;
        public RespawnView RespawnView;
        public HealthView HealthBar;

        public Firerer CharacterPrefab;
        public Crate CratePrefab;
        public FirererPlayerInput InputPrefab;
        public GameplayCamera CameraPrefab;
        public Bullet BulletPrefab;

        public Canvas SceneCanvas;

        public override void InstallBindings()
        {
            BindState();
            BindCommands();
            BindLogics();
            BindViews();
            BindHandlers();
            BindPrefabs();

            Container.Bind<CommanderStatus>().WithId(0).To<CommanderStatus>().AsCached();
        }

        private void BindPrefabs()
        {
            Container.Bind<Firerer>().FromInstance(CharacterPrefab).AsSingle();
            Container.Bind<Crate>().FromInstance(CratePrefab).AsSingle();
            Container.Bind<Bullet>().FromInstance(BulletPrefab).AsSingle();

            Container.Bind<FirererPlayerInput>().FromComponentInNewPrefab(InputPrefab).AsSingle();
            Container.Bind<GameplayCamera>().FromComponentInNewPrefab(CameraPrefab).AsSingle();
        }

        private void BindState()
        {
            Container.Bind<ConnectionStatus>().FromInstance(new ConnectionStatus()).AsSingle();
            Container.Bind<PlayStatus>().FromInstance(new PlayStatus()).AsSingle();
            Container.Bind<OwnTable>().FromInstance(new OwnTable()).AsSingle();

            Container.Bind<ConnectionsRepository>().FromInstance(new ConnectionsRepository()).AsSingle();
            Container.Bind<IncomingCommandsQueue>().FromInstance(new IncomingCommandsQueue()).AsSingle();
            Container.Bind<OutgoingCommandsQueue>().FromInstance(new OutgoingCommandsQueue()).AsSingle();
        }

        public override void Start()
        {
            base.Start();
            DontDestroyOnLoad(gameObject);
            Container.InstantiateComponent<NetworkController>(gameObject);
        }

        private void BindHandlers()
        {
        }

        private void BindViews()
        {
            Container.Bind<SelectRoleView>().FromInstance(Role).AsSingle();
            Container.Bind<ServerConfigView>().FromInstance(ServerConfig).AsSingle();
            Container.Bind<ClientConfigView>().FromInstance(ClientConfig).AsSingle();
            Container.Bind<StartGameView>().FromInstance(StartGame).AsSingle();
            Container.Bind<StatusView>().FromInstance(Status).AsSingle();
            Container.Bind<RespawnView>().FromInstance(RespawnView).AsSingle();
            Container.Bind<HealthView>().FromInstance(HealthBar).AsSingle();
            Container.Bind<Canvas>().FromInstance(SceneCanvas).AsSingle();
        }

        private void BindLogics()
        {
            BindLogic<ClientStartGameLogic>("client");
            BindLogic<ServerLogic>("server");
            BindLogic<ServerStartGameLogic>("server");

            BindLogic<RepositoriesForConnectionLogic>("both");
            BindLogic<InitializeGameObjectsLogic>("both");
            BindLogic<UpdateStatusLogic>("both");
            BindLogic<CharacterSpawnLogic>("both");

            BindLogic<SyncInputLogic>("both");

            // commands handlers
            BindLogic<CharacterLookHandler>("both");
            BindLogic<CharacterMoveHandler>("both");
            BindLogic<CharacterShootHandler>("both");

            BindLogic<EntityCreateHandler>("both");
            BindLogic<EntityDamageHandler>("both");
            BindLogic<EntityRemoveHandler>("both");
        }

        private void BindLogic<T>(string id) where T : INetworkLogic
        {
            Container.Bind<INetworkLogic>().WithId(id).To<T>().AsSingle();
        }

        private void BindCommands()
        {
            BindCommand<StartGame>(CommandIds.Game_Start);

            BindCommand<CharacterSpawn>(CommandIds.Character_Spawn);
            BindCommand<CharacterDie>(CommandIds.Character_Die);
            BindCommand<CharacterShoot>(CommandIds.Character_Shoot);
            BindCommand<CharacterLook>(CommandIds.Character_Look);
            BindCommand<CharacterMove>(CommandIds.Character_Move);
            BindCommand<CharacterBuilt>(CommandIds.Character_Built);
            BindCommand<CharacterBullet>(CommandIds.Character_Bullet);

            BindCommand<EntityCreate>(CommandIds.Entity_Create);
            BindCommand<EntityDamage>(CommandIds.Entity_Damage);
            BindCommand<EntityRemove>(CommandIds.Entity_Remove);
        }

        private void BindCommand<T>(byte id) where T : ICommand
        {
            Container.Bind<ICommand>().WithId(id).To<T>().AsCached();
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/ConnectionStatus.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using UniRx;

namespace LlapiExample
{
    public enum ConnectionState : byte
    {
        Disconnected = 1 << 7,           // 1000 0000
        Connecting = 1,                  // 0000 0001
        Connected = 1 << 1,              // 0000 0010
        Server = 1 << 2,                 // 0000 0100
        Client = 1 << 3,                 // 0000 1000
        InGame = 1 << 4,                 // 0001 0000
        Paused = 1 << 5,
    }

    public class ConnectionStatus
    {
        private BehaviorSubject<ConnectionState> state;

        public ConnectionStatus(ConnectionState initial = ConnectionState.Disconnected)
        {
            state = new BehaviorSubject<ConnectionState>(initial);
        }

        public IObservable<ConnectionState> Observable
        {
            get { return state; }
        }

        public ConnectionState Current
        {
            get { return state.Value; }
        }

        public bool IsConnecting
        {
            get { return (Current & ConnectionState.Connecting) == ConnectionState.Connecting; }
        }

        public bool IsConnected { get { return (Current & ConnectionState.Connected) == ConnectionState.Connected; } }
        public bool IsServer { get { return (Current & ConnectionState.Server) == ConnectionState.Server; } }
        public bool IsClient { get { return (Current & ConnectionState.Client) == ConnectionState.Client; } }
        public bool InGame { get { return (Current & ConnectionState.InGame) == ConnectionState.InGame; } }
        public bool IsPaused { get { return (Current & ConnectionState.Paused) == ConnectionState.Paused; } }

        public bool IsReady { get { return IsConnected && (IsServer || IsClient); } }
        public bool IsDefinedRole { get { return IsServer || IsClient; } }

        public void Set(ConnectionState next)
        {
            state.OnNext(next);
        }

        public void Append(ConnectionState flag)
        {
            state.OnNext(Current | flag);
        }

        public void Remove(ConnectionState flag)
        {
            state.OnNext(Current & ~flag);
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/BaseCommandHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public abstract class BaseCommandHandler<T> : INetworkLogic where T : ICommand
    {
        private IncomingCommandsQueue incomings;

        public BaseCommandHandler(IncomingCommandsQueue incomings)
        {
            this.incomings = incomings;
        }

        public virtual void Setup()
        {
            incomings.Observable.Subscribe(Handle);
        }

        private void Handle(ICommand command)
        {
            if(command is T)
            {
                OnCommand((T)command);
            }
        }

        public virtual void Update()
        {
        }

        protected abstract void OnCommand(T command);
    }
}


<|start_filename|>assets-folder/Code/Networking/INetworkLogic.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace LlapiExample
{
    public interface INetworkLogic
    {
        void Setup();
        void Update();
    }

    public interface ICommonLogic { }
    public interface IServerLogic { }
    public interface IClientLogic { }
}

<|start_filename|>assets-folder/Code/Behaviours/Entities/Crate.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections.Generic;

namespace LlapiExample
{
    public class Crate : VitalEntity
    {
        public override float CurrentHealth
        {
            get
            {
                return health;
            }
        }

        public override float MaxHealth
        {
            get { return 30f; }
        }

        private float health;

        [SerializeField]
        private List<Rigidbody> demolitionParts;
        [SerializeField]
        private List<Rigidbody> allParts;

        private const float partsPerDamage = 0.5f;

        public override void Start()
        {
            base.Start();
            health = MaxHealth;
        }

        public override void DamageReceive(float damage, Vector3 point, Vector3 force)
        {
            health -= damage;
            if (health > 0)
            {
                var demolitionCount = Mathf.FloorToInt(damage * partsPerDamage);
                while (demolitionCount > 0)
                {
                    var part = demolitionParts[Random.Range(0, demolitionParts.Count)];
                    demolitionParts.Remove(part);
                    allParts.Remove(part);
                    Demolition(part, force.normalized * 5f, point);
                    demolitionCount--;
                }
            }
            else
            {
                foreach (var part in allParts)
                {
                    Demolition(part, force, point);
                }

                Destroy(gameObject);
            }
        }

        private void Demolition(Rigidbody part, Vector3 force, Vector3 point)
        {
            part.transform.parent = null;
            part.GetComponent<BoxCollider>().enabled = true;
            part.isKinematic = false;
            part.useGravity = true;
            part.gameObject.layer = 11;// "Debris";
            part.AddForceAtPosition(force.normalized * 2f, point, ForceMode.Impulse);
            Destroy(part.gameObject, Random.Range(1.5f, 2f));
        }
    }
}

<|start_filename|>assets-folder/Code/Behaviours/Entities/Bullet.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using Zenject;

namespace LlapiExample
{
    [RequireComponent(typeof(Rigidbody))]
    public class Bullet : NetworkEntity
    {
        [Inject]
        private OutgoingCommandsQueue outgoings;

        [Inject(Id = 0)]
        private CommanderStatus commander;

        private Rigidbody rigidbodyCached;
        protected Rigidbody Rigidbody
        {
            get
            {
                if (!rigidbodyCached) rigidbodyCached = GetComponent<Rigidbody>();
                return rigidbodyCached;
            }
        }

        public AudioSource ImpactPrefab;
        public float Speed;
        public float Damage;

        private void Start()
        {
            Rigidbody.AddForce(transform.forward * Speed, ForceMode.VelocityChange);// * Time.fixedDeltaTime);
        }

        private void OnDestroy()
        {
            var audio = Instantiate(ImpactPrefab.gameObject, transform.position, Quaternion.identity);
            var sound = audio.GetComponent<AudioSource>();
            Destroy(audio, sound.clip.length);
        }

        private void OnCollisionEnter(Collision collision)
        {
            if (IsMine)
            {
                var entity = collision.gameObject.GetComponent<VitalEntity>();
                if (entity)
                {
                    entity.DamageReceive(Damage, collision.contacts[0].point, Rigidbody.velocity);
                    var damage = new EntityDamage();
                    damage.amount = Damage;
                    damage.id = entity.Id;
                    outgoings.Enqueue(damage);
                }

                var remove = new EntityRemove();
                remove.id = Id;
                outgoings.Enqueue(remove);
                commander.Destroy(this);
                //Destroy(gameObject);
            }
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/CharacterBullet.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class CharacterBullet : BaseCommand, ICommand
    {
        public Vector3 position;
        public Vector3 forward;

        public CharacterBullet() : base(CommandIds.Character_Bullet)
        {
        }

        public object[] Data()
        {
            return new object[]
            {
                position,
                forward
            };
        }

        public void Read(object[] buffer, ref int position)
        {
            this.position = (Vector3)buffer[++position];
            forward = (Vector3)buffer[++position];
        }
    }
}

<|start_filename|>assets-folder/Code/UI/StartGameView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class StartGameView : BaseView
    {
        public Action OnStartGame;
        public void StartButton()
        {
            OnStartGame();
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/CommanderStatus.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using Zenject;

namespace LlapiExample
{

    public enum Team : byte
    {
        Unknown,
        TeamA,
        TeamB
    }

    public class CommanderStatus
    {
        private Team team;
        private int id;
        private BehaviorSubject<Firerer> characterInstance;
        private Dictionary<Guid, NetworkEntity> entities;
        private DiContainer container;
        private Firerer firererPrefab;
        private Crate cratePrefab;
        private Bullet bulletPrefab;
        private OutgoingCommandsQueue outgoings;
        private OwnTable table;

        public void SetOwner(int connectionId)
        {
            id = connectionId;
        }

        public Firerer Character
        {
            get { return characterInstance.Value; }
            set { characterInstance.OnNext(value); }
        }

        public Team Team { get { return team; } }

        public IObservable<Firerer> ObservableCharacter { get { return characterInstance; } }

        private static int charStatusCount = 0;

        public CommanderStatus(DiContainer container, Firerer firererPrefab, Crate crate, Bullet bullet, OutgoingCommandsQueue outgoings, OwnTable table)
        {
            this.table = table;
            this.outgoings = outgoings;
            this.firererPrefab = firererPrefab;
            this.cratePrefab = crate;
            this.bulletPrefab = bullet;
            this.container = container;

            characterInstance = new BehaviorSubject<Firerer>(null);

            entities = new Dictionary<Guid, NetworkEntity>();
        }

        internal void SetTeam(Team team)
        {
            this.team = team;
        }

        public void Destroy(NetworkEntity entity)
        {
            Destroy(entity.Id);
        }

        public void Destroy(Guid id)
        {
            if(!entities.ContainsKey(id))
            {
                Debug.LogError("Unknown id: " + id);
                return;
            }

            if (entities[id].gameObject)
                GameObject.Destroy(entities[id].gameObject, 0.1f);

            entities.Remove(id);
            table.Release(id);
        }

        public void Damage(Guid id, float amount, Vector3? point = null, Vector3? force = null)
        {
            if(!entities.ContainsKey(id))
            {
                Debug.LogError("Unknown id: " + id);
                return;
            }

            var entity = entities[id] as VitalEntity;

            if(!entity)
            {
                Debug.LogError("Non vital entity: " + entities[id]);
                return;
            }

            if(!point.HasValue)
            {
                point = entity.transform.position;
            }

            if(!force.HasValue)
            {
                force = Vector3.forward;
            }

            entity.DamageReceive(amount, point.Value, force.Value);
        }

        private NetworkEntity Spawn(NetworkEntity prefab, Vector3 position, Vector3 forward, Guid guid)
        {
            var rotation = Quaternion.LookRotation(forward, Vector3.up);

            Debug.LogFormat("Instantiate from container {0}, on commander {1}", prefab.name, this.id);
            var instance = container.InstantiatePrefab(prefab.gameObject);
            instance.transform.position = position;
            instance.transform.rotation = rotation;
            instance.transform.localScale = Vector3.one;

            var network = instance.GetComponent<NetworkEntity>();

            network.IsMine = id == 0;
            network.CommanderId = id;
            network.Commander = this;
            network.Id = guid;

            entities.Add(network.Id, network);
            table.Obtain(guid, this.id);

            return network;
        }

        public NetworkEntity SpawnPrefab(PrefabType type, Vector3 position, Vector3 forward, Guid id)
        {
            var rotation = Quaternion.LookRotation(forward, Vector3.up);

            NetworkEntity prefab = null;

            switch(type) {
                case PrefabType.Character:
                    prefab = firererPrefab;
                    break;
                case PrefabType.Crate:
                    prefab = cratePrefab;
                    break;
                case PrefabType.Bullet:
                    prefab = bulletPrefab;
                    break;
            }

            var entity = Spawn(prefab, position, forward, id);
            if (type == PrefabType.Character)
            {
                Character = (entity as Firerer);
            }

            return entity;
        }

        public NetworkEntity SpawnCharacter(Vector3? position = null, Vector3? forward = null, Guid? id = null)
        {
            if (!position.HasValue)
                position = Vector3.zero;

            if (!forward.HasValue)
                forward = Vector3.forward;

            if (!id.HasValue)
                id = Guid.NewGuid();

            return SpawnPrefab(PrefabType.Character, position.Value, forward.Value, id.Value);
        }

        public NetworkEntity SpawnCrate(Vector3? position = null, Vector3? forward = null, Guid? id = null)
        {
            if (!position.HasValue)
                position = Vector3.zero;

            if (!forward.HasValue)
                forward = Vector3.forward;

            if (!id.HasValue)
                id = Guid.NewGuid();
            return SpawnPrefab(PrefabType.Crate, position.Value, forward.Value, id.Value);
        }

        public NetworkEntity SpawnBullet(Vector3? position = null, Vector3? forward = null, Guid? id = null)
        {
            if (!position.HasValue)
                position = Vector3.zero;

            if (!forward.HasValue)
                forward = Vector3.forward;

            if (!id.HasValue)
                id = Guid.NewGuid();

            return SpawnPrefab(PrefabType.Bullet, position.Value, forward.Value, id.Value);
        }

        public void Die()
        {
            Character.Die();
            characterInstance.OnNext(null);
        }

    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterMoveHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterMoveHandler : BaseCommandHandler<CharacterMove>
    {
        private Firerer firererPrefab;
        private DiContainer container;

        public CharacterMoveHandler(IncomingCommandsQueue incomings, Firerer firererPrefab, DiContainer container) : base(incomings)
        {
            this.firererPrefab = firererPrefab;
            this.container = container;
        }

        protected override void OnCommand(CharacterMove command)
        {
            var owner = command.Connection;
            var repository = container.ResolveId<CommanderStatus>(command.Connection);

            if (!repository.Character)
            {
                return;
            }

            repository.Character.Translate(command.position);
            repository.Character.Move(command.destination);
        }
    }
}


<|start_filename|>assets-folder/Code/Behaviours/GameplayCamera.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    [CreateAssetMenu(fileName = "CameraSetup.asset", menuName = "Camera Setup")]
    public class GameplayCameraSetup : ScriptableObject
    {
        public float PitchAngle = 45f;
        public float Distance = 20f;
        public float YawAngle = 60f;
    }

    public sealed class GameplayCamera : MonoBehaviour
    {
        #region Editor Links
        [SerializeField]
        private Transform YawTransform;
        [SerializeField]
        private Transform DistanceTransform;
        [SerializeField]
        private Transform PitchTransform;
        [SerializeField]
        private GameplayCameraSetup InitialSetup;
        [SerializeField]
        private Camera CameraObject;
        #endregion

        public float PitchAngle
        {
            get
            {
                return PitchTransform.localRotation.eulerAngles.x;
            }
            set
            {
                var current = PitchTransform.localRotation.eulerAngles;
                current.x = value;
                PitchTransform.localRotation = Quaternion.Euler(current);
            }
        }

        public float Distance
        {
            get
            {
                return -DistanceTransform.localPosition.z;
            }
            set
            {
                var current = DistanceTransform.localPosition;
                current.z = -value;
                DistanceTransform.localPosition = current;
            }
        }
        public float YawAngle
        {
            get
            {
                return YawTransform.localRotation.eulerAngles.y;
            }
            set
            {
                var current = YawTransform.localRotation.eulerAngles;
                current.y = value;
                YawTransform.localRotation = Quaternion.Euler(current);
            }
        }
        public Vector3 Position
        {
            get
            {
                return transform.position;
            }
            set
            {
                transform.position = value;
            }
        }
        public Camera UnityCamera
        {
            get { return CameraObject; }
        }

        private void Start()
        {
            // Check object state
            if (!PitchTransform)
                throw new NullReferenceException("PitchTransform cannot be null! Set it in editor");
            if(!YawTransform)
                throw new NullReferenceException("YawTransform cannot be null! Set it in editor");
            if(!DistanceTransform)
                throw new NullReferenceException("DistanceTransform cannot be null! Set it in editor");
            if(!CameraObject)
                throw new NullReferenceException("CameraObject cannot be null! Set it in editor");
            if (!InitialSetup)
                throw new NullReferenceException("InitialSetup cannot be null! Set it in editor");

            // Set initial values
            PitchAngle = InitialSetup.PitchAngle;
            YawAngle = InitialSetup.YawAngle;
            Distance = InitialSetup.Distance;
        }
    }
}

<|start_filename|>setup.bat<|end_filename|>
@echo off
echo "Clone dependencies"
echo "UniRx"
call git clone https://github.com/neuecc/UniRx.git unirx
echo "Zenject"
call git clone https://github.com/modesttree/Zenject.git zenject
echo "Setup symlinks"
call mklink /D assets-folder\Zenject ..\zenject\UnityProject\Assets\Zenject
call mklink /D assets-folder\Plugins\UniRx ..\..\unirx\Assets\Plugins\UniRx
call mklink /D llapi-client\Assets ..\assets-folder
call mklink /D llapi-server\Assets ..\assets-folder
call mklink /D llapi-client\ProjectSettings ..\project-settings
call mklink /D llapi-server\ProjectSettings ..\project-settings
echo "Done"

<|start_filename|>assets-folder/Code/Networking/Commands/CharacterLook.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;
using Zenject;

namespace LlapiExample
{
    public class CharacterLook : BaseCommand, ICommand
    {
        public Vector3 shootDirection;
        [Inject(Id = 0)] private CommanderStatus status;

        public CharacterLook() : base(CommandIds.Character_Look)
        {
        }

        public object[] Data()
        {
            return new object[]
            {
                status.Character.shootDirection
            };
        }

        public void Read(object[] buffer, ref int position)
        {
            shootDirection = (Vector3)buffer[++position];
        }
    }
}

<|start_filename|>assets-folder/Code/UI/BaseView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;

namespace LlapiExample
{
    public abstract class BaseView : MonoBehaviour
    {
        public virtual void Hide()
        {
            gameObject.SetActive(false);
        }

        public virtual void Show()
        {
            gameObject.SetActive(true);
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/CharacterSpawnHadler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterSpawnHadler : BaseCommandHandler<CharacterSpawn>
    {
        private Firerer firererPrefab;
        private DiContainer container;

        public CharacterSpawnHadler(IncomingCommandsQueue incomings, Firerer firererPrefab, DiContainer container) : base(incomings)
        {
            this.firererPrefab = firererPrefab;
            this.container = container;
        }

        protected override void OnCommand(CharacterSpawn command)
        {
            // TODO: :D
            //var position = command.Team == Team.TeamA ? new Vector3(0, 0, 23f) : new Vector3(0, 0, -21f);

            //var characterGo = container.InstantiatePrefab(firererPrefab);
            //var character = characterGo.GetComponent<Firerer>();
            //var repository = container.ResolveId<CommanderStatus>(command.Connection);
            //repository.Character = character;
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/Commands/CharacterMove.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;
using Zenject;

namespace LlapiExample
{
    public class CharacterMove : BaseCommand, ICommand
    {
        public Vector3 position;
        public Vector3 destination;

        [Inject(Id = 0)] private CommanderStatus status;

        public CharacterMove() : base(CommandIds.Character_Move)
        {

        }

        public object[] Data()
        {
            return new object[] {
                status.Character.transform.position,
                status.Character.Agent.destination
            };
        }

        public void Read(object[] buffer, ref int position)
        {
            this.position = (Vector3)buffer[++position];
            destination =   (Vector3)buffer[++position];

        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/EntityRemove.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class EntityRemove : BaseCommand, ICommand
    {
        public Guid id;

        public EntityRemove() : base(CommandIds.Entity_Remove)
        {

        }

        public object[] Data()
        {
            return new object[] {
                id.ToString()
            };
        }

        public void Read(object[] buffer, ref int position)
        {
            id = new Guid((string)buffer[++position]);
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/CharacterDie.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class CharacterDie : BaseCommand, ICommand
    {
        public CharacterDie() : base(CommandIds.Character_Die)
        {

        }

        public object[] Data()
        {
            return new object[0];
        }

        public void Read(object[] buffer, ref int position)
        {
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/PlayStatus.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using UniRx;

namespace LlapiExample
{
    public enum PlayState : byte
    {
        Await = 1 << 0,         // 0000 0100
        Alive = 1 << 1,
        Dead = 1 << 2,
        Respawn = 1 << 3
    }

    public class PlayStatus
    {
        private BehaviorSubject<PlayState> state;

        public PlayStatus(PlayState initial = PlayState.Await)
        {
            state = new BehaviorSubject<PlayState>(initial);
        }

        public IObservable<PlayState> Observable
        {
            get { return state; }
        }

        public PlayState Current
        {
            get { return state.Value; }
        }
        
        public void Set(PlayState next)
        {
            state.OnNext(next);
        }
    }
}

<|start_filename|>assets-folder/Code/DataTypes/OwnTable.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace LlapiExample
{
    public class OwnTable
    {
        private Dictionary<Guid, int> entityOwners;

        public OwnTable()
        {
            entityOwners = new Dictionary<Guid, int>();
        }

        public void Obtain(Guid id, int owner)
        {
            if (!entityOwners.ContainsKey(id))
                entityOwners.Add(id, -1);

            entityOwners[id] = owner;
        }

        public void Release(Guid id)
        {
            if (entityOwners.ContainsKey(id))
                entityOwners.Remove(id);
        }

        public int Owner(Guid id)
        {
            if (entityOwners.ContainsKey(id))
                return entityOwners[id];
            else
                return -1;
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/CommandsHandles/EntityRemoveHandler.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class EntityRemoveHandler : BaseCommandHandler<EntityRemove>
    {
        private DiContainer container;
        private OwnTable table;

        public EntityRemoveHandler(IncomingCommandsQueue incomings, DiContainer container, OwnTable table) : base(incomings)
        {
            this.container = container;
            this.table = table;
        }

        protected override void OnCommand(EntityRemove command)
        {
            var owner = table.Owner(command.id);
            if (owner == -1)
            {
                Debug.LogError("Owner not defined: " + command.id);
                return;
            }

            var repository = container.ResolveId<CommanderStatus>(owner);
            repository.Destroy(command.id);
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/CharacterSpawnLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class CharacterSpawnLogic : INetworkLogic
    {
        #region Dependencies
        [Inject] private ConnectionStatus status;
        [Inject] private GameplayCamera camera;
        [Inject] private FirererPlayerInput playerInput;
        [Inject] private PlayStatus playStatus;
        [Inject] private RespawnView respawnView;
        [Inject] private OutgoingCommandsQueue outgoings;
        [Inject(Id = 0)] private CommanderStatus characterStatus;
        [Inject] private DiContainer container;
        #endregion

        private float respawnAfter;

        public void Setup()
        {
            UnityEngine.Object.DontDestroyOnLoad(camera.gameObject);
            UnityEngine.Object.DontDestroyOnLoad(playerInput.gameObject);

            OnStatusChange(status.Current);

            status.Observable.Subscribe(OnStatusChange);
            playStatus.Observable.Subscribe(OnPlayStatusChange);

            characterStatus.SetTeam(status.IsClient ? Team.TeamB : Team.TeamA);

            characterStatus.ObservableCharacter.Subscribe(OnCharacter);

            respawnView.OnRespawn += OnRespawnButton;
            playerInput.Camera = camera;

            OnPlayStatusChange(playStatus.Current);
        }

        private void OnRespawnButton()
        {
            playStatus.Set(PlayState.Respawn);
        }

        public void OnCharacter(Firerer character)
        {
            if(character)
            {
                playerInput.Firerer = character;
                character.OnDie += OnDie;
            }
        }

        private void OnDie()
        {
            playStatus.Set(PlayState.Dead);
        }

        public void OnPlayStatusChange(PlayState state)
        {
            if (!status.InGame)
            {
                respawnView.Hide();
                return;
            }

            if (state != PlayState.Alive && state != PlayState.Await)
            {
                respawnView.Show();
                respawnView.Await = state != PlayState.Respawn;
            }
            else
            {
                respawnView.Hide();
            }

            if(state == PlayState.Respawn)
            {
                respawnAfter = 5f;
                respawnView.RespawnWait(respawnAfter);
            }

            if(state == PlayState.Await)
            {
                var x = UnityEngine.Random.Range(-5f, 5f);
                var z = UnityEngine.Random.Range(16f, 22f);
                var side = characterStatus.Team == Team.TeamA ? -1f : 1f;

                var position = new Vector3(x, 0, z * side);
                var entity = characterStatus.SpawnCharacter(position);
                var cmd = new EntityCreate();
                cmd.prefab = PrefabType.Character;
                cmd.id = entity.Id;
                outgoings.Enqueue(cmd);
            }
        }

        public void OnStatusChange(ConnectionState state)
        {
            playerInput.enabled = status.InGame && !status.IsPaused;
            OnPlayStatusChange(playStatus.Current);
        }

        public void Update()
        {
            if (playStatus.Current == PlayState.Respawn)
            {
                respawnAfter -= Time.deltaTime;
                respawnView.RespawnWait(respawnAfter);

                if (respawnAfter <= 0)
                {
                    playStatus.Set(PlayState.Await);
                }
            }
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/InitializeGameObjectsLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;

namespace LlapiExample
{
    public class InitializeGameObjectsLogic : INetworkLogic
    {
        [Inject]
        private ConnectionStatus status;

        [Inject]
        private GameplayCamera camera;

        [Inject]
        private FirererPlayerInput playerInput;

        public void Setup()
        {
            Object.DontDestroyOnLoad(camera.gameObject);
            Object.DontDestroyOnLoad(playerInput.gameObject);
            OnStatusChange(status.Current);
            status.Observable.Subscribe(OnStatusChange);
        }

        public void OnStatusChange(ConnectionState state)
        {
            playerInput.enabled = status.InGame && !status.IsPaused;
        }

        public void Update()
        {
        }
    }
}


<|start_filename|>assets-folder/Code/Networking/UpdateStatusLogic.cs<|end_filename|>
﻿using Zenject;
using UniRx;
using UnityEngine;
using System;

namespace LlapiExample
{
    public class UpdateStatusLogic : INetworkLogic, IClientLogic
    {
        [Inject]
        private StatusView statusView;
        [Inject]
        private ConnectionStatus status;

        public UpdateStatusLogic()
        {
        }

        public void Update()
        {
        }

        public void Setup()
        {
            statusView.Show();
            status.Observable.Subscribe(OnStatusChange);
        }

        private void OnStatusChange(ConnectionState state)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            var values = Enum.GetValues(typeof(ConnectionState));
            foreach (var v in values)
            {
                if ((state & (ConnectionState)v) == (ConnectionState)v)
                    sb.Append(((ConnectionState)v).ToString() + " ");
            }

            statusView.Message = sb.ToString();
        }
    }
}

<|start_filename|>assets-folder/Code/UI/SelectRoleView.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class SelectRoleView : BaseView
    {
        public Action OnServerSelected;
        public Action OnClientSelected;

        public void ServerButton()
        {
            OnServerSelected();
        }

        public void ClientButton()
        {
            OnClientSelected();
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/ServerLogic.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

namespace LlapiExample
{
    public class ServerLogic : INetworkLogic, IServerLogic
    {
        private StartGameView startGame;
        private OutgoingCommandsQueue outgoings;
        private ConnectionsRepository connections;
        private ConnectionStatus status;

        public void Update()
        {
        }

        public void Setup()
        {
        }

        public ServerLogic(
            StartGameView startGame, 
            ConnectionsRepository connections, 
            OutgoingCommandsQueue outgoings, 
            ConnectionStatus status)
        {
            this.startGame = startGame;
            this.connections = connections;
            this.outgoings = outgoings;
            this.status = status;
        }
    }
}

<|start_filename|>dependencies.bat<|end_filename|>
@echo off
echo "Clone dependencies"
echo "UniRx"
call git clone https://github.com/neuecc/UniRx.git unirx
echo "Zenject"
call git clone https://github.com/modesttree/Zenject.git zenject
echo "Setup symlinks"
call mklink /D assets-folder\Zenject ..\zenject\UnityProject\Assets\Zenject
call mklink /D assets-folder\Plugins\UniRx ..\..\unirx\Assets\Plugins\UniRx
echo "Done"

<|start_filename|>assets-folder/Code/Networking/Commands/ICommand.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;

namespace LlapiExample
{
    public interface ICommand
    {
        int Connection { set; get; }
        byte CommandId { get; }
        void Read(object[] buffer, ref int position);
        object[] Data();
    }

    public static class CommandIds
    {
        // Game state commands (0xX)
        public const byte Game_Start        = 0x1;
        public const byte Game_Pause        = 0x2;
        public const byte Game_End          = 0x3;

        /// Character commands (0x0X)
        public const byte Character_Spawn   = 0x10;
        public const byte Character_Die     = 0x11;
        public const byte Character_Move    = 0x12;
        public const byte Character_Shoot   = 0x13;
        public const byte Character_Built   = 0x14;
        public const byte Character_Look    = 0x15;
        public const byte Character_Bullet  = 0x16;

        // Entities commands (0x1X)
        public const byte Entity_Damage     = 0x21;
        public const byte Entity_Create     = 0x22;
        public const byte Entity_Remove     = 0x23;


    }
}

<|start_filename|>assets-folder/Code/Behaviours/Entities/NetworkEntity.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public abstract class NetworkEntity : MonoBehaviour
    {        
        protected CommanderStatus status {  get { return Commander;  } }
        public CommanderStatus Commander{ get; set; }
        public int CommanderId { get; set; }
        public bool IsMine { get; set; }

#if UNITY_EDITOR
        [SerializeField]
        private string debugId;
#endif

        private Guid id;
        public Guid Id
        {
            get { return id; }
            set
            {
                if (id == default(Guid))
                {
                    id = value;
#if UNITY_EDITOR
                    debugId = value.ToString();
#endif
                }
            }
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/EntityCreate.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public enum PrefabType : byte {
        Character,
        Crate,
        Bullet
    }

    public class EntityCreate : BaseCommand, ICommand
    {
        public Guid id;
        public PrefabType prefab;
        public Vector3 position;
        public Vector3 forward;

        public EntityCreate() : base(CommandIds.Entity_Create)
        {

        }

        public object[] Data()
        {
            return new object[] {
                (byte)prefab,
                id.ToString(),
                position,
                forward
            };
        }

        public void Read(object[] buffer, ref int position)
        {
            prefab = (PrefabType)(byte)buffer[++position];
            id = new Guid((string)buffer[++position]);
            this.position = (Vector3)buffer[++position];
            forward = (Vector3)buffer[++position];
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/StartGame.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public class StartGame : BaseCommand, ICommand
    {
        public StartGame() : base(CommandIds.Game_Start)
        {

        }

        public object[] Data()
        {
            return new object[0];
        }

        public void Read(object[] buffer, ref int offset)
        {
        }
    }
}

<|start_filename|>assets-folder/Code/UI/HealthView.cs<|end_filename|>
﻿using UnityEngine;
using UnityEngine.UI;
using System;
using Zenject;

namespace LlapiExample
{
    public class HealthView : BaseView
    {
        [SerializeField]
        private Slider healthSlider;

        [Inject]
        private GameplayCamera camera;

        public float Amount { set { healthSlider.value = Mathf.Clamp01(value); } }

        public Vector3 WorldPosition
        {
            set
            {
                var screenPoint = camera.UnityCamera.WorldToScreenPoint(value);
                (transform as RectTransform).position = screenPoint;
            }
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/ServerStartGameLogic.cs<|end_filename|>
﻿using System;
using System.Linq;
using UniRx;
using UnityEngine.SceneManagement;

namespace LlapiExample
{
    public class ServerStartGameLogic : INetworkLogic, IServerLogic
    {
        private StartGameView startGame;
        private OutgoingCommandsQueue outgoings;
        private ConnectionsRepository connections;
        private ConnectionStatus status;

        public ServerStartGameLogic(
            StartGameView startGame,
            ConnectionsRepository connections,
            OutgoingCommandsQueue outgoings,
            ConnectionStatus status)
        {
            this.startGame = startGame;
            this.connections = connections;
            this.outgoings = outgoings;
            this.status = status;
        }

        private void OnStatusChange(ConnectionState state)
        {
            if(status.IsReady && !status.InGame)
            {
                startGame.Show();
            } else
            {
                startGame.Hide();
            }
        }

        private void OnStartGame()
        {
            outgoings.Enqueue(new StartGame());
            var task = SceneManager.LoadSceneAsync(1, LoadSceneMode.Additive);
            var time = 0f;
            var timer = new System.Diagnostics.Stopwatch();
            timer.Start();
            while(!task.isDone && time < 1f)
            {
                time += timer.ElapsedMilliseconds / 1000f;
            }
            timer.Stop();

            SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex(1));
            status.Append(ConnectionState.InGame);
        }

        public void Update()
        {
        }

        public void Setup()
        {
            startGame.OnStartGame += OnStartGame;
            status.Observable.Subscribe(OnStatusChange);
        }
    }
}

<|start_filename|>assets-folder/Code/Behaviours/Entities/VitalEntity.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using Zenject;

namespace LlapiExample
{
    public abstract class VitalEntity : NetworkEntity
    {
        public abstract float CurrentHealth { get; }
        public abstract float MaxHealth { get; }
        public abstract void DamageReceive(float damage, Vector3 point, Vector3 force);

        [Inject]
        private HealthView healthPrefab;

        [Inject]
        private DiContainer container;

        [Inject]
        private Canvas canvas;

        private HealthView healthBar;

        public virtual void Start()
        {
            var instance = container.InstantiatePrefab(healthPrefab);
            instance.transform.SetParent(canvas.transform, false);
            healthBar = instance.GetComponent<HealthView>();
            healthBar.transform.localScale = Vector3.one;
            healthBar.transform.localRotation = Quaternion.identity;
        }

        public virtual void OnDestroy()
        {
            Destroy(healthBar.gameObject);
        }

        public virtual void Update()
        {
            healthBar.WorldPosition = transform.position + Vector3.up * 2f;
            healthBar.Amount = CurrentHealth / MaxHealth;
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/Commands/BaseCommand.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System;

namespace LlapiExample
{
    public abstract class BaseCommand
    {
        public int Connection { set; get; }
        public byte CommandId { get { return id; } }

        private byte id;

        public BaseCommand(byte id)
        {
            this.id = id;
        }
    }
}

<|start_filename|>assets-folder/Code/Networking/NetworkController.cs<|end_filename|>
﻿using UnityEngine;
using UnityEngine.Networking;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Zenject;

namespace LlapiExample
{
    public class NetworkController : MonoBehaviour
    {
        public bool IsConnecting
        {
            get { return (state & ConnectionState.Connecting) == ConnectionState.Connecting; }
        }

        public bool IsConnected { get { return (state & ConnectionState.Connected) == ConnectionState.Connected; } }
        public bool IsServer { get { return (state & ConnectionState.Server) == ConnectionState.Server; } }
        public bool IsClient { get { return (state & ConnectionState.Client) == ConnectionState.Client; } }
        public bool InGame   { get { return (state & ConnectionState.InGame) == ConnectionState.InGame; } }

        public bool IsReady { get { return IsConnected && (IsServer || IsClient); } }
        public bool IsDefinedRole { get { return IsServer || IsClient; } }

        #region Dependencies
        [Inject] SelectRoleView Role;
        [Inject] ServerConfigView ServerConfig;
        [Inject] ClientConfigView ClientConfig;
        [Inject] DiContainer Container;
        [Inject] ConnectionStatus ConnectionStatus;
        [Inject] ConnectionsRepository Connections;
        [Inject] OutgoingCommandsQueue OutgoingCommands;
        [Inject] IncomingCommandsQueue IncomingCommands;
        #endregion

        string status;

        int unreliableChannel = -1;
        int realiableChannel = -1;
        int hostId = -1;

        string serverIp = "127.0.0.1";
        int outboundPort = 8080;
        int inboundPort = 8081;

        int connectionId = -1;

        List<INetworkLogic> logics;
        ConnectionState state { get { return ConnectionStatus.Current; } }

        private void Start()
        {
            logics = new List<INetworkLogic>();

            // Setup role ui
            Role.Hide();
            Role.OnClientSelected += OnClientRoleSelected;
            Role.OnServerSelected += OnServerRoleSelected;

            // setup server ui
            ServerConfig.Hide();
            ServerConfig.OnStart += OnServerStart;
            ServerConfig.OnPortChange += OnServerPortChange;

            // setup client ui
            ClientConfig.Hide();
            ClientConfig.OnStart += OnClientStart;
            ClientConfig.OnPortChange += OnClientPortChange;
            ClientConfig.OnAddressChange += OnClientAddressChange;

            ConnectionStatus.Set(ConnectionState.Disconnected);

            NetworkTransport.Init();
        }

        private void OnClientAddressChange(string ip)
        {
            if (!IsClient) return;
            serverIp = ip;
        }

        private void OnClientPortChange(int port)
        {
            if (!IsClient) return;
            outboundPort = port;
            inboundPort  = port + 1;
        }

        private void OnClientStart()
        {
            if (!IsClient) return;
            CreateHost();
            Connect();

            ConnectionStatus.Remove(ConnectionState.Connecting);
            ConnectionStatus.Append(ConnectionState.Connected);

            status = string.Format("Waiting for game start", outboundPort, inboundPort);
        }

        private void OnServerStart()
        {
            if (!IsServer) return;

            inboundPort = outboundPort;
            CreateHost();

            ConnectionStatus.Remove(ConnectionState.Disconnected);
            ConnectionStatus.Remove(ConnectionState.Connecting);
            ConnectionStatus.Append(ConnectionState.Connected);
        }

        private void OnServerPortChange(int port)
        {
            if (!IsServer) return;
            inboundPort = port;
        }

        private void OnClientRoleSelected()
        {
            ConnectionStatus.Remove(ConnectionState.Server);
            ConnectionStatus.Append(ConnectionState.Client);

            logics.Clear();
            logics = Container.ResolveIdAll<INetworkLogic>("client").Concat(Container.ResolveIdAll<INetworkLogic>("both")).ToList();

            foreach (var logic in logics)
            {
                logic.Setup();
            }
        }

        private void OnServerRoleSelected()
        {
            ConnectionStatus.Remove(ConnectionState.Client);
            ConnectionStatus.Append(ConnectionState.Server);

            logics.Clear();
            logics = Container.ResolveIdAll<INetworkLogic>("server").Concat(Container.ResolveIdAll<INetworkLogic>("both")).ToList();

            foreach (var logic in logics)
            {
                logic.Setup();
            }
        }

        private void Update()
        {
            if (!IsDefinedRole)
            {
                // Selected connection role
                Role.Show();
            }
            else
            {
                Role.Hide();
            }

            if (!IsConnected && !IsConnecting)
            {
                if (IsClient)
                    ClientConfig.Show();

                if (IsServer)
                    ServerConfig.Show();
            } else
            {
                ClientConfig.Hide();
                ServerConfig.Hide();
            }

            if(IsReady)
            {
                // listen
                ListenHost();
                SendHost();

                foreach (var logic in logics)
                {
                    logic.Update();
                }
            }
        }

        private void SendHost()
        {
            byte error;
            List<object> logicData = new List<object>();

            foreach (var command in OutgoingCommands)
            {
                logicData.Add(command.CommandId);
                logicData.AddRange(command.Data());
            }

            OutgoingCommands.Flush();
            
            if (logicData.Count == 0) return;

            int size;
            var buffer = UnnyNetPacker.PackObject(out size, logicData.ToArray());

            foreach (var connectionId in Connections)
            {
                NetworkTransport.Send(hostId, connectionId, realiableChannel, buffer, size, out error);
            }            
        }

        private void ListenHost()
        {
            int connectionId;
            int channelId;
            byte[] recBuffer = new byte[1024];
            int bufferSize = 1024;
            int dataSize;
            byte error;

            NetworkEventType recData = NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
            switch (recData)
            {
                case NetworkEventType.Nothing:         //1
                    break;
                case NetworkEventType.ConnectEvent:    //2
                    ConnectionEvent(connectionId);
                    break;
                case NetworkEventType.DataEvent:       //3
                    DataEvent(connectionId, channelId, dataSize, recBuffer);
                    break;
                case NetworkEventType.DisconnectEvent: //4
                    DisconnectEvent(connectionId);
                    break;
            }
        }

        private void DisconnectEvent(int connectionId)
        {
            Connections.Remove(connectionId);
        }

        private void DataEvent(int connectionId, int channelId, int dataSize, byte[] recBuffer)
        {
            // read data to commands
            var unpacked = UnnyNetPacker.UnpackObject(recBuffer, dataSize);

            for (int position = 0; position < unpacked.Length; position++)
            {
                var commandId = (byte)unpacked[position];

                var cmd = Container.ResolveId<ICommand>(commandId);
                cmd.Read(unpacked, ref position);
                cmd.Connection = connectionId;
                IncomingCommands.Enqueue(cmd);
            }
        }

        private void ConnectionEvent(int connectionId)
        {
            Connections.Add(connectionId);
        }

        private void CreateHost()
        {
            if (hostId >= 0)
            {
                NetworkTransport.RemoveHost(hostId);
            }

            var config = new ConnectionConfig();
            realiableChannel = config.AddChannel(QosType.Reliable);
            unreliableChannel = config.AddChannel(QosType.UnreliableSequenced);
            var topology = new HostTopology(config, 1);

            hostId = NetworkTransport.AddHost(topology, inboundPort, serverIp);

            ConnectionStatus.Append(ConnectionState.Connecting);
        }

        public void Connect()
        {
            byte error;
            connectionId = NetworkTransport.Connect(hostId, serverIp, outboundPort, 0, out error);
            status = error.ToString();
        }
        private void SendHello()
        {
            byte error;
            byte[] buffer = new byte[1024];
            Stream stream = new MemoryStream(buffer);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, "HelloServer");

            int bufferSize = 1024;

            NetworkTransport.Send(hostId, connectionId, realiableChannel, buffer, bufferSize, out error);
        }
    }
}

<|start_filename|>assets-folder/Code/Utils/UnnyNetPacker.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
using System.IO;

public class UnnyNetPacker {

	enum TypeTags
	{
		IntTag,
        FloatTag,
        Vector3Tag,
		UIntTag,
		LongTag,
		ULongTag,
		ShortTag,
		UShortTag,
		ByteTag,
        SByteTag,
        BoolTag,
		StringTag,
        UShortArraySmall,
        UShortArrayBig,
	}

	public const short HeaderSize = 2 + 1;//netId + cmd id

    static public object[] UnpackObject(byte[] bytes, int bytesCount)
    {
        object[] arg = new object[1];
        arg[0] = 0;
        int size = 0;
        int num = HeaderSize;
        try
        {
            while (num < bytesCount)
            {
                switch ((TypeTags)bytes[num++])
                {
                    case TypeTags.IntTag:
                        {
                            if (num + 4 <= bytesCount)
                            {
                                int intValue = System.BitConverter.ToInt32(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 4;
                            break;
                        }
                    case TypeTags.FloatTag:
                        {
                            if (num + 4 <= bytesCount)
                            {
                                var floatValue = System.BitConverter.ToSingle(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = floatValue;
                            }
                            num += 4;
                            break;
                        }
                    case TypeTags.Vector3Tag:
                        {
                            if (num + 12 <= bytesCount)
                            {
                                var vecValue = new Vector3();
                                for (int axis = 0; axis < 3; axis++)
                                {
                                    vecValue[axis] = System.BitConverter.ToSingle(bytes, num + (4 * axis));
                                }

                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = vecValue;
                            }
                            num += 12;
                            break;
                        }
                    case TypeTags.UIntTag:
                        {
                            if (num + 4 <= bytesCount)
                            {
                                uint intValue = System.BitConverter.ToUInt32(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 4;
                            break;
                        }
                    case TypeTags.LongTag:
                        {
                            if (num + 8 <= bytesCount)
                            {
                                long intValue = System.BitConverter.ToInt64(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 8;
                            break;
                        }
                    case TypeTags.ULongTag:
                        {
                            if (num + 8 <= bytesCount)
                            {
                                ulong intValue = System.BitConverter.ToUInt64(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 8;
                            break;
                        }
                    case TypeTags.ShortTag:
                        {
                            if (num + 2 <= bytesCount)
                            {
                                short intValue = System.BitConverter.ToInt16(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 2;
                            break;
                        }
                    case TypeTags.UShortTag:
                        {
                            if (num + 2 <= bytesCount)
                            {
                                ushort intValue = System.BitConverter.ToUInt16(bytes, num);
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = intValue;
                            }
                            num += 2;
                            break;
                        }
                    case TypeTags.ByteTag:
                        {
                            System.Array.Resize(ref arg, size + 1);
                            arg[size++] = bytes[num++];
                            break;
                        }
                    case TypeTags.SByteTag:
                        {
                            System.Array.Resize(ref arg, size + 1);
                            arg[size++] = (sbyte)bytes[num++];
                            break;
                        }
                    case TypeTags.BoolTag:
                        {
                            System.Array.Resize(ref arg, size + 1);
                            arg[size++] = (bytes[num++] == 1);
                            break;
                        }
                    case TypeTags.StringTag:
                        {
                            if (num + 2 <= bytesCount)
                            {
                                ushort intValue = System.BitConverter.ToUInt16(bytes, num);
                                num += 2;
                                if (num + intValue <= bytesCount)
                                {
                                    System.Array.Resize(ref arg, size + 1);
                                    arg[size++] = System.Text.Encoding.UTF8.GetString(bytes, num, intValue);
                                }
                                num += intValue;
                            }
                            else
                                num += 2;
                            break;
                        }
                    case TypeTags.UShortArraySmall:
                        {
                            byte length = bytes[num++];
                            if (num + 2 * length <= bytesCount)
                            {
                                ushort[] arr = new ushort[length];
                                for (int j = 0; j < length; j++)
                                {
                                    arr[j] = System.BitConverter.ToUInt16(bytes, num);
                                    num += 2;
                                }
                                System.Array.Resize(ref arg, size + 1);
                                arg[size++] = arr;
                            }
                            else
                                num += 2 * length;
                            break;
                        }
                    case TypeTags.UShortArrayBig:
                        {
                            if (num + 2 <= bytesCount)
                            {
                                ushort length = System.BitConverter.ToUInt16(bytes, num);
                                num += 2;

                                if (num + 2 * length <= bytesCount)
                                {
                                    ushort[] arr = new ushort[length];

                                    for (int j = 0; j < length; j++)
                                    {
                                        arr[j] = System.BitConverter.ToUInt16(bytes, num);
                                        num += 2;
                                    }
                                    System.Array.Resize(ref arg, size + 1);
                                    arg[size++] = arr;
                                }
                                else
                                    num += 2 * length;
                            }
                            else
                                num += 2;
                            break;
                        }
                    default:
                        Debug.Log("Couldn't unpack");
                        return arg;
                }
            }
        }
        catch (System.ArgumentException e)
        {
            Debug.Log("e = " + e);
        }
        return arg;
    }

    const int BufferSize = 1024 * 32;
    static byte [] m_Buffer = new byte[BufferSize];

    static public byte[] PackObject(out int size, params object []arg)
	{
        byte [] bytes = m_Buffer;//new byte[HeaderSize];
		size = HeaderSize;
        for (int i = 0; i < arg.Length; i++)
        {
            if (arg[i] is int)
            {
                int intValue = (int)arg[i];
                //				System.Array.Resize(ref bytes, size + 5);
                bytes[size++] = (byte)TypeTags.IntTag;
                bytes[size++] = (byte)intValue;
                bytes[size++] = (byte)(intValue >> 8);
                bytes[size++] = (byte)(intValue >> 16);
                bytes[size++] = (byte)(intValue >> 24);
            }
            else if (arg[i] is float)
            {
                var floatValue = (float)arg[i];
                var floatBytes = System.BitConverter.GetBytes(floatValue);
                //				System.Array.Resize(ref bytes, size + 5);
                bytes[size++] = (byte)TypeTags.FloatTag;
                bytes[size++] = floatBytes[0];
                bytes[size++] = floatBytes[1];
                bytes[size++] = floatBytes[2];
                bytes[size++] = floatBytes[3];
            }
            else if(arg[i] is Vector3)
            {
                var vecValue = (Vector3)arg[i];
                bytes[size++] = (byte)TypeTags.Vector3Tag;
                for (int axis = 0; axis < 3; axis++)
                {
                    var floatBytes = System.BitConverter.GetBytes(vecValue[axis]);
                    bytes[size++] = floatBytes[0];
                    bytes[size++] = floatBytes[1];
                    bytes[size++] = floatBytes[2];
                    bytes[size++] = floatBytes[3];
                }
            }
            else if (arg[i] is uint)
            {
                uint intValue = (uint)arg[i];
                //					System.Array.Resize(ref bytes, size + 5);
                bytes[size++] = (byte)TypeTags.UIntTag;
                bytes[size++] = (byte)intValue;
                bytes[size++] = (byte)(intValue >> 8);
                bytes[size++] = (byte)(intValue >> 16);
                bytes[size++] = (byte)(intValue >> 24);
            }
            else if (arg[i] is short)
            {
                short shortValue = (short)arg[i];
                //						System.Array.Resize(ref bytes, size + 3);
                bytes[size++] = (byte)TypeTags.ShortTag;
                bytes[size++] = (byte)shortValue;
                bytes[size++] = (byte)(shortValue >> 8);
            }
            else if (arg[i] is ushort)
            {
                ushort shortValue = (ushort)arg[i];
                //							System.Array.Resize(ref bytes, size + 3);
                bytes[size++] = (byte)TypeTags.UShortTag;
                bytes[size++] = (byte)shortValue;
                bytes[size++] = (byte)(shortValue >> 8);
            }
            else if (arg[i] is byte)
            {
                //								System.Array.Resize(ref bytes, size + 2);
                bytes[size++] = (byte)TypeTags.ByteTag;
                bytes[size++] = (byte)arg[i];
            }
            else if (arg[i] is sbyte)
            {
                //                                    System.Array.Resize(ref bytes, size + 2);
                bytes[size++] = (byte)TypeTags.SByteTag;
                bytes[size++] = (byte)(sbyte)arg[i];
            }
            else if (arg[i] is long)
            {
                long intValue = (long)arg[i];
                //										System.Array.Resize(ref bytes, size + 1+8);
                bytes[size++] = (byte)TypeTags.LongTag;
                bytes[size++] = (byte)intValue;
                bytes[size++] = (byte)(intValue >> 8);
                bytes[size++] = (byte)(intValue >> 16);
                bytes[size++] = (byte)(intValue >> 24);
                bytes[size++] = (byte)(intValue >> 32);
                bytes[size++] = (byte)(intValue >> 40);
                bytes[size++] = (byte)(intValue >> 48);
                bytes[size++] = (byte)(intValue >> 56);
            }
            else if (arg[i] is ulong)
            {
                ulong intValue = (ulong)arg[i];
                //											System.Array.Resize(ref bytes, size + 1+8);
                bytes[size++] = (byte)TypeTags.ULongTag;
                bytes[size++] = (byte)intValue;
                bytes[size++] = (byte)(intValue >> 8);
                bytes[size++] = (byte)(intValue >> 16);
                bytes[size++] = (byte)(intValue >> 24);
                bytes[size++] = (byte)(intValue >> 32);
                bytes[size++] = (byte)(intValue >> 40);
                bytes[size++] = (byte)(intValue >> 48);
                bytes[size++] = (byte)(intValue >> 56);
            }
            else if (arg[i] is bool)
            {
                //		                                        System.Array.Resize(ref bytes, size + 2);
                bytes[size++] = (byte)TypeTags.BoolTag;
                bytes[size++] = ((bool)arg[i]) ? (byte)1 : (byte)0;
            }
            else if (arg[i] is ushort[])
            {
                ushort[] shortValue = (ushort[])arg[i];
                if (shortValue.Length <= byte.MaxValue)
                {
                    //		                                                System.Array.Resize(ref bytes, size + 1 + 1 + 2*shortValue.Length);
                    bytes[size++] = (byte)TypeTags.UShortArraySmall;
                    bytes[size++] = (byte)shortValue.Length;
                }
                else
                {
                    //		                                                System.Array.Resize(ref bytes, size + 1 + 2 + 2*shortValue.Length);
                    bytes[size++] = (byte)TypeTags.UShortArrayBig;
                    bytes[size++] = (byte)shortValue.Length;
                    bytes[size++] = (byte)(shortValue.Length >> 8);
                }
                for (int j = 0; j < shortValue.Length; j++)
                {
                    bytes[size++] = (byte)shortValue[j];
                    bytes[size++] = (byte)(shortValue[j] >> 8);
                }
            }
            else if (arg[i] is string)
            {
                string str = (string)arg[i];
                byte[] strBytes = System.Text.Encoding.UTF8.GetBytes(str);
                int newSize = strBytes.Length;
                //		                									System.Array.Resize(ref bytes, size + newSize + 3);
                bytes[size++] = (byte)TypeTags.StringTag;
                ushort shortValue = (ushort)newSize;
                bytes[size++] = (byte)shortValue;
                bytes[size++] = (byte)(shortValue >> 8);
                System.Buffer.BlockCopy(strBytes, 0, bytes, size, newSize);
                size += newSize;
            }
        }
		return bytes;
	}
}


<|start_filename|>assets-folder/Code/Behaviours/FirererPlayerInput.cs<|end_filename|>
﻿using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace LlapiExample
{
    public class FirererPlayerInput : MonoBehaviour
    {
        public Firerer Firerer;
        public GameplayCamera Camera;
        private float horizontal;
        private float vertical;

        private Vector3 targetPosition;
        private Vector3 cameraPosition;

        private Vector3 rayPoint;
        private Vector3 shootDirection;
        
        // Update is called once per frame
        void Update()
        {
            // Wait for camera and character
            if(!Firerer || !Camera)
                return;

            horizontal = Input.GetAxis("Horizontal");
            vertical = Input.GetAxis("Vertical");

            var rawDirection = new Vector3(horizontal, 0f, vertical);
            var camRotation = Quaternion.Euler(0, Camera.YawAngle, 0f);
            var camDirection = camRotation * rawDirection;

            targetPosition = Firerer.transform.position + camDirection * 1f;

            Firerer.Move(targetPosition);

            var mouseRay = Camera.UnityCamera.ScreenPointToRay(Input.mousePosition);
            float enter;

            if (new Plane(Vector3.up, Vector3.zero).Raycast(mouseRay, out enter))
            {
                rayPoint = mouseRay.origin + mouseRay.direction * enter;
                shootDirection = (rayPoint - Firerer.transform.position).normalized;
            }

            Firerer.ShootDirection(shootDirection);

            if (Input.GetAxis("Fire1") > 0f)
            {
                Firerer.Shoot();
            }


            if(Input.GetAxis("Jump") > 0f)
            {
                Firerer.Build();
            }


            cameraPosition = Vector3.Lerp((Vector3)(Firerer.transform.position + camDirection * 5f), (Vector3)(Firerer.transform.position + shootDirection * 5f), 0.5f);
            Camera.Position = Vector3.Lerp(Camera.Position, cameraPosition, Time.deltaTime * 3f);
        }

#if UNITY_EDITOR
        private void OnGUI()
        {
            GUI.Label(new Rect(10, 10, 100, 30), string.Format("{0}, {1}", horizontal, vertical));
        }

        private void OnDrawGizmos()
        {
            if (!Firerer || !Camera)
                return;

            var vector = Vector3.right * horizontal + Vector3.forward * vertical;
            vector.Normalize();

            var tmpColor = Handles.color;
            Handles.color = Color.red;
            Handles.DrawLine((Vector3)Firerer.transform.position, (Vector3)(Firerer.transform.position + vector * 5f));


            var cameraVector = Quaternion.Euler(0, Camera.YawAngle, 0f) * vector;
            Handles.color = Color.green;
            Handles.DrawLine((Vector3)Firerer.transform.position, (Vector3)(Firerer.transform.position + cameraVector * 5f));


            Handles.color = new Color(0,1f,0,0.3f);
            var angle = 120f;
            Handles.DrawSolidArc((Vector3)Firerer.transform.position, Vector3.up, (Vector3)(Quaternion.Euler(0, -angle * 0.5f, 0) * Firerer.transform.forward), angle, 2f);

            var spread = angle / 180f;
            if (Vector3.Angle((Vector3)Firerer.transform.forward, shootDirection) > angle / 2f)
                Handles.color = Color.red;
            else
                Handles.color = Color.green;

            Handles.DrawLine((Vector3)Firerer.transform.position, rayPoint);

            Handles.color = tmpColor;
        }
    }
#endif
}