예제 #1
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="netManager">The network manager owning this player.</param>
        /// <param name="netWorld">Network world owning this player.</param>
        /// <param name="steamId">The steam id of the player.</param>
        public NetLocalPlayer(NetManager netManager, NetWorld netWorld, Steamworks.CSteamID steamId) : base(netManager, netWorld, steamId)
        {
            GameDoorsManager.Instance.onDoorsOpen = () => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.open = true;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameDoorsManager.Instance.onDoorsClose = () => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.open = false;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameCallbacks.onObjectPickup = (GameObject gameObj) => {
                Messages.PickupObjectMessage msg = new Messages.PickupObjectMessage();
                msg.netId = netWorld.GetPickupableNetId(gameObj);
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameCallbacks.onObjectRelease = (bool drop) => {
                Messages.ReleaseObjectMessage msg = new Messages.ReleaseObjectMessage();
                msg.drop = drop;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };
        }
예제 #2
0
        public NetManager()
        {
            statistics             = new NetStatistics(this);
            netManagerCreationTime = DateTime.UtcNow;
            netMessageHandler      = new NetMessageHandler(this);
            netWorld = new NetWorld(this);

            p2pSessionRequestCallback =
                Steamworks.Callback <Steamworks.P2PSessionRequest_t> .Create(
                    OnP2PSessionRequest);

            p2pConnectFailCallback =
                Steamworks.Callback <Steamworks.P2PSessionConnectFail_t> .Create(
                    OnP2PConnectFail);

            gameLobbyJoinRequestedCallback =
                Steamworks.Callback <Steamworks.GameLobbyJoinRequested_t> .Create(
                    OnGameLobbyJoinRequested);

            lobbyCreatedCallResult =
                new Steamworks.CallResult <Steamworks.LobbyCreated_t>(OnLobbyCreated);
            lobbyEnterCallResult =
                new Steamworks.CallResult <Steamworks.LobbyEnter_t>(OnLobbyEnter);

            Instance = this;

            RegisterProtocolMessagesHandlers();
        }
예제 #3
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="netManager">The network manager owning this player.</param>
        /// <param name="netWorld">Network world owning this player.</param>
        /// <param name="steamId">The steam id of the player.</param>
        public NetLocalPlayer(
            NetManager netManager, NetWorld netWorld, Steamworks.CSteamID steamId)
            : base(netManager, netWorld, steamId)
        {
            Instance = this;
            steamID  = steamId;

            GameDoorsManager.Instance.onDoorsOpen = (GameObject door) => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.position = Utils.GameVec3ToNet(door.transform.position);
                msg.open     = true;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameDoorsManager.Instance.onDoorsClose = (GameObject door) => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.position = Utils.GameVec3ToNet(door.transform.position);
                msg.open     = false;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            LightSwitchManager.Instance.onLightSwitchUsed =
                (GameObject lswitch, bool turnedOn) => {
                Messages.LightSwitchMessage msg = new Messages.LightSwitchMessage();
                msg.pos    = Utils.GameVec3ToNet(lswitch.transform.position);
                msg.toggle = turnedOn;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            if (animManager == null)
            {
                animManager = new PlayerAnimManager();
            }
        }
예제 #4
0
        public NetManager()
        {
            Instance               = this;
            statistics             = new NetStatistics(this);
            netManagerCreationTime = DateTime.UtcNow;
            netMessageHandler      = new NetMessageHandler(this);
            netWorld               = new NetWorld(this);

            // Hopefully this will fix people playing with different mod versions!
            string versionFull = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
            int    buildNumber = Convert.ToInt32(versionFull.Substring(versionFull.LastIndexOf('.') + 1));

            PROTOCOL_VERSION = buildNumber;
            Logger.Log("Build version is: " + PROTOCOL_VERSION);

            p2pSessionRequestCallback = Steamworks.Callback <Steamworks.P2PSessionRequest_t> .Create(OnP2PSessionRequest);

            p2pConnectFailCallback = Steamworks.Callback <Steamworks.P2PSessionConnectFail_t> .Create(OnP2PConnectFail);

            gameLobbyJoinRequestedCallback = Steamworks.Callback <Steamworks.GameLobbyJoinRequested_t> .Create(OnGameLobbyJoinRequested);

            lobbyCreatedCallResult = new Steamworks.CallResult <Steamworks.LobbyCreated_t>(OnLobbyCreated);
            lobbyEnterCallResult   = new Steamworks.CallResult <Steamworks.LobbyEnter_t>(OnLobbyEnter);
            lobbyListResult        = new Steamworks.CallResult <Steamworks.LobbyMatchList_t>(OnLobbyList);
            lobbyDataResult        = new Steamworks.CallResult <Steamworks.LobbyDataUpdate_t>(OnGetLobbyInfo);

            RegisterProtocolMessagesHandlers();

            RequestLobbies();
        }
예제 #5
0
        public NetManager()
        {
            this.netManagerCreationTime = DateTime.UtcNow;
            netMessageHandler           = new NetMessageHandler(this);
            netWorld = new NetWorld(this);

            p2pSessionRequestCallback = Steamworks.Callback <Steamworks.P2PSessionRequest_t> .Create((Steamworks.P2PSessionRequest_t result) => {
                if (!Steamworks.SteamNetworking.AcceptP2PSessionWithUser(result.m_steamIDRemote))
                {
                    Logger.Log("Accepted p2p session with " + result.m_steamIDRemote.ToString());
                }
            });

            gameLobbyJoinRequestedCallback = Steamworks.Callback <Steamworks.GameLobbyJoinRequested_t> .Create(OnGameLobbyJoinRequested);

            lobbyCreatedCallResult = new Steamworks.CallResult <Steamworks.LobbyCreated_t>((Steamworks.LobbyCreated_t result, bool ioFailure) => {
                if (result.m_eResult != Steamworks.EResult.k_EResultOK)
                {
                    Logger.Log("Oh my f*****g god i failed to create a lobby for you. Please forgive me. (result: " + result.m_eResult + ")");

                    MPGUI.Instance.ShowMessageBox("Failed to create lobby due to steam error.\n" + result.m_eResult, () => {
                        MPController.Instance.LoadLevel("MainMenu");
                    });
                    return;
                }

                Logger.Log("Hey you! I have lobby id for you! " + result.m_ulSteamIDLobby);

                // Setup local player.
                players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID());

                mode           = Mode.Host;
                state          = State.Playing;
                currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby);
            });

            lobbyEnterCallResult = new Steamworks.CallResult <Steamworks.LobbyEnter_t>((Steamworks.LobbyEnter_t result, bool ioFailure) => {
                if (result.m_EChatRoomEnterResponse != (uint)Steamworks.EChatRoomEnterResponse.k_EChatRoomEnterResponseSuccess)
                {
                    Logger.Log("Oh my f*****g god i failed to join the lobby for you. Please forgive me. (reponse: " + result.m_EChatRoomEnterResponse + ")");

                    players[1] = null;
                    return;
                }

                // Setup local player.
                players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID());

                Logger.Log("Oh hello! " + result.m_ulSteamIDLobby);

                mode           = Mode.Player;
                state          = State.LoadingGameWorld;
                currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby);

                ShowLoadingScreen(true);
                SendHandshake();
            });

            RegisterProtocolMessagesHandlers();
        }
예제 #6
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="netManager">Network manager managing connection to the
 /// player.</param> <param name="netWorld">Network world owning this
 /// player.</param> <param name="steamId">Player's steam id.</param>
 public NetPlayer(
     NetManager netManager, NetWorld netWorld, Steamworks.CSteamID steamId)
 {
     this.netManager = netManager;
     this.netWorld   = netWorld;
     this.steamId    = steamId;
 }
예제 #7
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="netManager">The network manager owning this player.</param>
        /// <param name="netWorld">Network world owning this player.</param>
        /// <param name="steamId">The steam id of the player.</param>
        public NetLocalPlayer(NetManager netManager, NetWorld netWorld, Steamworks.CSteamID steamId) : base(netManager, netWorld, steamId)
        {
            GameDoorsManager.Instance.onDoorsOpen = (GameObject door) => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.position = Utils.GameVec3ToNet(door.transform.position);
                msg.open     = true;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameDoorsManager.Instance.onDoorsClose = (GameObject door) => {
                Messages.OpenDoorsMessage msg = new Messages.OpenDoorsMessage();
                msg.position = Utils.GameVec3ToNet(door.transform.position);
                msg.open     = false;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameCallbacks.onObjectPickup = (GameObject gameObj) => {
                Messages.PickupObjectMessage msg = new Messages.PickupObjectMessage();
                msg.netId = netWorld.GetPickupableNetId(gameObj);
                Client.Assert(msg.netId != NetPickupable.INVALID_ID, "Tried to pickup not network pickupable.");
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            GameCallbacks.onObjectRelease = (bool drop) => {
                Messages.ReleaseObjectMessage msg = new Messages.ReleaseObjectMessage();
                msg.drop = drop;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            BeerCaseManager.Instance.onBottleConsumed = (GameObject bcase) => {
                Messages.RemoveBottleMessage msg = new Messages.RemoveBottleMessage();
                msg.netId = netWorld.GetPickupableNetId(bcase);
                Client.Assert(msg.netId != NetPickupable.INVALID_ID, "Tried to drink from not network beercase.");
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            LightSwitchManager.Instance.onLightSwitchUsed = (GameObject lswitch, bool turnedOn) => {
                Messages.LightSwitchMessage msg = new Messages.LightSwitchMessage();
                msg.pos    = Utils.GameVec3ToNet(lswitch.transform.position);
                msg.toggle = turnedOn;
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            if (animManager == null)
            {
                animManager = new PlayerAnimManager();
            }
        }
예제 #8
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="netManager">Network manager owning this network world.</param>
        public NetWorld(NetManager netManager)
        {
            this.netManager = netManager;
            Instance        = this;

            GameCallbacks.onWorldUnload += () => { OnGameWorldUnload(); };

            GameCallbacks.onWorldLoad += () => { OnGameWorldLoad(); };

            GameCallbacks.onPlayMakerObjectCreate += (GameObject instance,
                                                      GameObject prefab) => {
                if (!Game.GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                var metaData =
                    prefab.GetComponent <Game.Components.PickupableMetaDataComponent>();
                Client.Assert(metaData != null,
                              "Tried to spawn pickupable that has no meta data assigned.");
                RegisterPickupable(instance);

                Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage();
                msg.prefabId           = metaData.prefabId;
                msg.transform.position = Utils.GameVec3ToNet(instance.transform.position);
                msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation);
                msg.active             = instance.activeSelf;

                // Check for multiple sync components from prefab
                ObjectSyncComponent oscOld = prefab.GetComponent <ObjectSyncComponent>();
                if (instance.GetComponents <ObjectSyncComponent>().Length > 1)
                {
                    foreach (ObjectSyncComponent osc in instance
                             .GetComponents <ObjectSyncComponent>())
                    {
                        if (osc.ObjectID == oscOld.ObjectID)
                        {
                            GameObject.Destroy(osc);
                        }
                        else
                        {
                            msg.id = osc.ObjectID;
                        }
                    }
                }
                else
                {
                    msg.id = instance.GetComponent <ObjectSyncComponent>().ObjectID;
                }

                // Determine if object should be spawned on remote client.
                // (Helps to avoid duplicate objects spawning)
                bool sendToRemote = false;
                if (NetManager.Instance.IsHost)
                {
                    Logger.Debug("Sending new object data to client!");
                    sendToRemote = true;
                }
                else
                {
                    if (instance.name.StartsWith("BottleBeerFly"))
                    {
                        sendToRemote = true;
                    }
                }

                if (sendToRemote)
                {
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                    Logger.Debug("Sending new object data to client!");
                }
            };

            GameCallbacks.onPlayMakerObjectActivate += (GameObject instance,
                                                        bool activate) => {
                if (playerIsLoading)
                {
                    return;
                }

                if (activate == instance.activeSelf)
                {
                    return;
                }

                if (!GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(instance);
                if (pickupable == null)
                {
                    return;
                }

                if (activate)
                {
                    var metaData =
                        pickupable.gameObject.GetComponent <PickupableMetaDataComponent>();

                    Messages.PickupableSpawnMessage msg =
                        new Messages.PickupableSpawnMessage();
                    msg.id                 = pickupable.ObjectID;
                    msg.prefabId           = metaData.prefabId;
                    msg.transform.position = Utils.GameVec3ToNet(instance.transform.position);
                    msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation);
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                }
                else
                {
                    Messages.PickupableActivateMessage msg =
                        new Messages.PickupableActivateMessage();
                    msg.id       = pickupable.ObjectID;
                    msg.activate = false;
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                }
            };

            GameCallbacks.onPlayMakerObjectDestroy += (GameObject instance) => {
                if (!Game.GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(instance);
                if (pickupable == null)
                {
                    Logger.Debug(
                        $"Pickupable {instance.name} has been destroyed however it is not registered, skipping removal.");
                    return;
                }

                HandlePickupableDestroy(instance);
            };

            GameCallbacks.onPlayMakerSetPosition +=
                (GameObject gameObject, Vector3 position, Space space) => {
                if (!Game.GamePickupableDatabase.IsPickupable(gameObject))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(gameObject);
                if (pickupable == null)
                {
                    return;
                }

                if (space == Space.Self)
                {
                    position += gameObject.transform.position;
                }

                Messages.PickupableSetPositionMessage msg =
                    new Messages.PickupableSetPositionMessage();
                msg.id       = pickupable.ObjectID;
                msg.position = Utils.GameVec3ToNet(position);
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            RegisterNetworkMessagesHandlers(netManager.MessageHandler);
        }
예제 #9
0
        public NetManager()
        {
            this.netManagerCreationTime = DateTime.UtcNow;
            netWorld = new NetWorld(this);

            // Setup local player.
            players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID());

            p2pSessionRequestCallback = Steamworks.Callback <Steamworks.P2PSessionRequest_t> .Create((Steamworks.P2PSessionRequest_t result) => {
                if (!Steamworks.SteamNetworking.AcceptP2PSessionWithUser(result.m_steamIDRemote))
                {
                    Logger.Log("Accepted p2p session with " + result.m_steamIDRemote.ToString());
                }
            });

            gameLobbyJoinRequestedCallback = Steamworks.Callback <Steamworks.GameLobbyJoinRequested_t> .Create(OnGameLobbyJoinRequested);

            lobbyCreatedCallResult = new Steamworks.CallResult <Steamworks.LobbyCreated_t>((Steamworks.LobbyCreated_t result, bool ioFailure) => {
                if (result.m_eResult != Steamworks.EResult.k_EResultOK)
                {
                    Logger.Log("Oh my f*****g god i failed to create a lobby for you. Please forgive me. (result: " + result.m_eResult + ")");

                    MPGUI.Instance.ShowMessageBox("Failed to create lobby due to steam error.\n" + result.m_eResult, () => {
                        MPController.Instance.LoadLevel("MainMenu");
                    });
                    return;
                }

                Logger.Log("Hey you! I have lobby id for you! " + result.m_ulSteamIDLobby);

                mode           = Mode.Host;
                state          = State.Playing;
                currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby);

                netWorld.RegisterPickupables();
            });

            lobbyEnterCallResult = new Steamworks.CallResult <Steamworks.LobbyEnter_t>((Steamworks.LobbyEnter_t result, bool ioFailure) => {
                if (result.m_EChatRoomEnterResponse != (uint)Steamworks.EChatRoomEnterResponse.k_EChatRoomEnterResponseSuccess)
                {
                    Logger.Log("Oh my f*****g god i failed to join the lobby for you. Please forgive me. (reponse: " + result.m_EChatRoomEnterResponse + ")");

                    players[1] = null;
                    return;
                }

                Logger.Log("Oh hello! " + result.m_ulSteamIDLobby);

                mode           = Mode.Player;
                state          = State.LoadingGameWorld;
                currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby);

                SendHandshake();
            });

            // TODO: Move message handlers to some class.

            BindMessageHandler((Steamworks.CSteamID sender, Messages.HandshakeMessage msg) => {
                remoteClock = msg.clock;
                HandleHandshake(sender, msg);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerSyncMessage msg) => {
                if (players[1] == null)
                {
                    Logger.Log("Received synchronization packet but no remote player is currently connected.");
                    return;
                }

                players[1].HandleSynchronize(msg);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatMessage msg) => {
                var message         = new Messages.HeartbeatResponseMessage();
                message.clientClock = msg.clientClock;
                message.clock       = GetNetworkClock();
                BroadcastMessage(message, Steamworks.EP2PSend.k_EP2PSendReliable);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatResponseMessage msg) => {
                ping = (uint)(GetNetworkClock() - msg.clientClock);

                // TODO: Some smart lag compensation.
                remoteClock = msg.clock;

                timeSinceLastHeartbeat = 0.0f;
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.DisconnectMessage msg) => {
                HandleDisconnect(false);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.OpenDoorsMessage msg) => {
                NetPlayer player = players[1];
                // 1.5 is a length of the ray used to check interaction with doors in game scripts.
                Vector3 interactionPosition = player.GetPosition() + player.GetRotation() * Vector3.forward * 1.5f;
                Game.Objects.GameDoor doors = Game.GameDoorsManager.Instance.FindGameDoors(interactionPosition);
                if (doors == null)
                {
                    Logger.Log($"Player tried to open doors however he is not close to any: {interactionPosition}.");
                    return;
                }
                doors.Open(msg.open);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.FullWorldSyncMessage msg) => {
                netWorld.HandleFullWorldSync(msg);

                // Now spawn player.

                if (players[1] != null)
                {
                    players[1].Spawn();
                }

                // World loaded we are playing!

                state = State.Playing;
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.AskForWorldStateMessage msg) => {
                var msgF = new Messages.FullWorldSyncMessage();
                netWorld.WriteFullWorldSync(msgF);
                BroadcastMessage(msgF, Steamworks.EP2PSend.k_EP2PSendReliable);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleEnterMessage msg) => {
                NetPlayer player = players[1];
                if (player == null)
                {
                    return;
                }

                NetVehicle vehicle = netWorld.GetVehicle(msg.vehicleId);
                if (vehicle == null)
                {
                    Logger.Log("Player " + player.SteamId + " tried to enter vehicle " + msg.vehicleId + " but there is no vehicle with such id.");
                    return;
                }

                player.EnterVehicle(vehicle, msg.passenger);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleLeaveMessage msg) => {
                NetPlayer player = players[1];
                if (player == null)
                {
                    return;
                }
                player.LeaveVehicle();
            });


            BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleSyncMessage msg) => {
                NetPlayer player = players[1];
                if (player == null)
                {
                    return;
                }

                player.HandleVehicleSync(msg);
            });


            BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupObjectMessage msg) => {
                NetPlayer player = players[1];
                if (player == null)
                {
                    return;
                }
                player.PickupObject(msg.netId);
            });

            BindMessageHandler((Steamworks.CSteamID sender, Messages.ReleaseObjectMessage msg) => {
                NetPlayer player = players[1];
                if (player == null)
                {
                    return;
                }
                player.ReleaseObject(msg.drop);
            });
        }
예제 #10
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="netManager">Network manager owning this network world.</param>
        public NetWorld(NetManager netManager)
        {
            this.netManager = netManager;
            Instance        = this;

            GameCallbacks.onWorldUnload += () => {
                OnGameWorldUnload();
            };

            GameCallbacks.onWorldLoad += () => {
                OnGameWorldLoad();
            };

            GameCallbacks.onPlayMakerObjectCreate += (GameObject instance, GameObject prefab) => {
                if (!GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                var metaData = prefab.GetComponent <PickupableMetaDataComponent>();
                Client.Assert(metaData != null, "Tried to spawn pickupable that has no meta data assigned.");

                Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage();
                msg.prefabId           = metaData.prefabId;
                msg.transform.position = Utils.GameVec3ToNet(instance.transform.position);
                msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation);
                msg.active             = instance.activeSelf;

                // Setup sync component on object.
                Client.Assert(instance.GetComponent <ObjectSyncComponent>(), $"Object created but no ObjectSyncComponent could be found! Object name: {instance.name}");
                ObjectSyncComponent osc = instance.GetComponent <ObjectSyncComponent>();
                msg.id = osc.Setup(osc.ObjectType, ObjectSyncManager.AUTOMATIC_ID);

                // Determine if object should be spawned on remote client.
                // (Helps to avoid duplicate objects spawning)
                bool sendToRemote = false;
                if (NetManager.Instance.IsHost)
                {
                    Logger.Debug("Sending new object data to client!");
                    sendToRemote = true;
                }
                else
                {
                    // This is a hack to workout beer bottles not spawning on the remote client due to items only spawning on the host.
                    // This will be replaced in the future.
                    if (instance.name.StartsWith("BottleBeerFly"))
                    {
                        sendToRemote = true;
                    }
                }

                if (sendToRemote)
                {
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                    Logger.Debug("Sending new object data to client!");
                }
            };

            GameCallbacks.onPlayMakerObjectActivate += (GameObject instance, bool activate) => {
                if (playerIsLoading)
                {
                    return;
                }

                if (activate == instance.activeSelf)
                {
                    return;
                }

                if (!GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(instance);
                if (pickupable == null)
                {
                    return;
                }

                if (activate)
                {
                    var metaData = pickupable.gameObject.GetComponent <PickupableMetaDataComponent>();

                    Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage();
                    msg.id                 = pickupable.ObjectID;
                    msg.prefabId           = metaData.prefabId;
                    msg.transform.position = Utils.GameVec3ToNet(instance.transform.position);
                    msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation);
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                }
                else
                {
                    Messages.PickupableActivateMessage msg = new Messages.PickupableActivateMessage();
                    msg.id       = pickupable.ObjectID;
                    msg.activate = false;
                    netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
                }
            };

            GameCallbacks.onPlayMakerObjectDestroy += (GameObject instance) => {
                if (!GamePickupableDatabase.IsPickupable(instance))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(instance);
                if (pickupable == null)
                {
                    Logger.Debug($"Pickupable {instance.name} has been destroyed however it is not registered, skipping removal.");
                    return;
                }

                HandlePickupableDestroy(instance);
            };

            GameCallbacks.onPlayMakerSetPosition += (GameObject gameObject, Vector3 position, Space space) => {
                if (!GamePickupableDatabase.IsPickupable(gameObject))
                {
                    return;
                }

                ObjectSyncComponent pickupable = GetPickupableByGameObject(gameObject);
                if (pickupable == null)
                {
                    return;
                }


                if (space == Space.Self)
                {
                    position += gameObject.transform.position;
                }

                Messages.PickupableSetPositionMessage msg = new Messages.PickupableSetPositionMessage();
                msg.id       = pickupable.ObjectID;
                msg.position = Utils.GameVec3ToNet(position);
                netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable);
            };

            RegisterNetworkMessagesHandlers(netManager.MessageHandler);
        }