示例#1
0
        /// <summary>
        /// Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
        /// </summary>
        /// <returns>0.5f if all the skills meet the skill requirements exactly, 1.0f if they're way above and 0.0f if way less</returns>
        public float DegreeOfSuccess(Character character, List <Skill> requiredSkills)
        {
            if (requiredSkills.Count == 0)
            {
                return(1.0f);
            }

            if (character == null)
            {
                string errorMsg = "ItemComponent.DegreeOfSuccess failed (character was null).\n" + Environment.StackTrace;
                DebugConsole.ThrowError(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                return(0.0f);
            }

            float skillSuccessSum = 0.0f;

            for (int i = 0; i < requiredSkills.Count; i++)
            {
                float characterLevel = character.GetSkillLevel(requiredSkills[i].Identifier);
                skillSuccessSum += (characterLevel - requiredSkills[i].Level);
            }
            float average = skillSuccessSum / requiredSkills.Count;

            return(((average + 100.0f) / 2.0f) / 100.0f);
        }
示例#2
0
        private void CreateDoorBody()
        {
            Vector2 position = ConvertUnits.ToSimUnits(item.Position + (dockingTarget.door.Item.WorldPosition - item.WorldPosition));

            if (!MathUtils.IsValid(position))
            {
                string errorMsg =
                    "Attempted to create a door body at an invalid position (item pos: " + item.Position
                    + ", item world pos: " + item.WorldPosition
                    + ", docking target world pos: " + DockingTarget.door.Item.WorldPosition + ")\n" + Environment.StackTrace;

                DebugConsole.ThrowError(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce(
                    "DockingPort.CreateDoorBody:InvalidPosition",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
                position = Vector2.Zero;
            }

            doorBody = BodyFactory.CreateRectangle(GameMain.World,
                                                   dockingTarget.door.Body.width,
                                                   dockingTarget.door.Body.height,
                                                   1.0f,
                                                   position,
                                                   dockingTarget.door);

            doorBody.CollisionCategories = Physics.CollisionWall;
            doorBody.BodyType            = BodyType.Static;
        }
        public override void Update(float deltaTime)
        {
            if (!started)
            {
                return;
            }

            if (OnOwnerDetermined != null && OwnerConnection != null)
            {
                OnOwnerDetermined?.Invoke(OwnerConnection);
                OnOwnerDetermined = null;
            }

            //backwards for loop so we can remove elements while iterating
            for (int i = connectedClients.Count - 1; i >= 0; i--)
            {
                SteamP2PConnection conn = connectedClients[i] as SteamP2PConnection;
                conn.Decay(deltaTime);
                if (conn.Timeout < 0.0)
                {
                    Disconnect(conn, "Timed out");
                }
            }

            try
            {
                while (ChildServerRelay.Read(out byte[] incBuf))
                {
                    IReadMessage inc = new ReadOnlyMessage(incBuf, false, 0, incBuf.Length, OwnerConnection);

                    HandleDataMessage(inc);
                }
            }

            catch (Exception e)
            {
                string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace;
                GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
#if DEBUG
                DebugConsole.ThrowError(errorMsg);
#else
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
#endif
            }

            for (int i = 0; i < pendingClients.Count; i++)
            {
                PendingClient pendingClient = pendingClients[i];
                UpdatePendingClient(pendingClient);
                if (i >= pendingClients.Count || pendingClients[i] != pendingClient)
                {
                    i--;
                }
            }
        }
示例#4
0
        /// <summary>
        /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
        /// </summary>
        protected void Write(IWriteMessage msg, List <NetEntityEvent> eventsToSync, out List <NetEntityEvent> sentEvents, Client recipient = null)
        {
            //write into a temporary buffer so we can write the number of events before the actual data
            IWriteMessage tempBuffer = new WriteOnlyMessage();

            sentEvents = new List <NetEntityEvent>();

            int eventCount = 0;

            foreach (NetEntityEvent e in eventsToSync)
            {
                //write into a temporary buffer so we can write the length before the actual data
                IWriteMessage tempEventBuffer = new WriteOnlyMessage();
                try
                {
                    WriteEvent(tempEventBuffer, e, recipient);
                }

                catch (Exception exception)
                {
                    DebugConsole.ThrowError("Failed to write an event for the entity \"" + e.Entity + "\"", exception);
                    GameAnalyticsManager.AddErrorEventOnce("NetEntityEventManager.Write:WriteFailed" + e.Entity.ToString(),
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to write an event for the entity \"" + e.Entity + "\"\n" + exception.StackTrace.CleanupStackTrace());

                    //write an empty event to avoid messing up IDs
                    //(otherwise the clients might read the next event in the message and think its ID
                    //is consecutive to the previous one, even though we skipped over this broken event)
                    tempBuffer.Write(Entity.NullEntityID);
                    tempBuffer.WritePadBits();
                    eventCount++;
                    continue;
                }

                if (eventCount > 0 &&
                    msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength)
                {
                    //no more room in this packet
                    break;
                }

                tempBuffer.Write(e.EntityID);
                tempBuffer.WriteVariableUInt32((uint)tempEventBuffer.LengthBytes);
                tempBuffer.Write(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
                tempBuffer.WritePadBits();
                sentEvents.Add(e);

                eventCount++;
            }

            if (eventCount > 0)
            {
                msg.Write(eventsToSync[0].ID);
                msg.Write((byte)eventCount);
                msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            }
        }
示例#5
0
        public void ServerAdminWrite(IWriteMessage outMsg, Client c)
        {
            try
            {
                if (outMsg == null)
                {
                    throw new ArgumentException("OutMsg was null");
                }
                if (GameMain.Server == null)
                {
                    throw new Exception("GameMain.Server was null");
                }

                if (!c.HasPermission(ClientPermissions.Ban))
                {
                    outMsg.Write(false); outMsg.WritePadBits();
                    return;
                }

                outMsg.Write(true);
                outMsg.Write(c.Connection == GameMain.Server.OwnerConnection);

                outMsg.WritePadBits();
                outMsg.WriteVariableUInt32((UInt32)bannedPlayers.Count);
                for (int i = 0; i < bannedPlayers.Count; i++)
                {
                    BannedPlayer bannedPlayer = bannedPlayers[i];

                    outMsg.Write(bannedPlayer.Name);
                    outMsg.Write(bannedPlayer.UniqueIdentifier);
                    outMsg.Write(bannedPlayer.IsRangeBan);
                    outMsg.Write(bannedPlayer.ExpirationTime != null);
                    outMsg.WritePadBits();
                    if (bannedPlayer.ExpirationTime != null)
                    {
                        double hoursFromNow = (bannedPlayer.ExpirationTime.Value - DateTime.Now).TotalHours;
                        outMsg.Write(hoursFromNow);
                    }

                    outMsg.Write(bannedPlayer.Reason ?? "");

                    if (c.Connection == GameMain.Server.OwnerConnection)
                    {
                        outMsg.Write(bannedPlayer.EndPoint);
                        outMsg.Write(bannedPlayer.SteamID);
                    }
                }
            }

            catch (Exception e)
            {
                string errorMsg = "Error while writing banlist. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
                GameAnalyticsManager.AddErrorEventOnce("Banlist.ServerAdminWrite", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                throw;
            }
        }
示例#6
0
        private void PushCharactersAway()
        {
            if (!MathUtils.IsValid(item.SimPosition))
            {
                DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ")");
                GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:DoorPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       "Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ").");
                return;
            }

            Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y));

            Vector2 currSize = IsHorizontal ?
                               new Vector2(item.Rect.Width * (1.0f - openState), doorSprite.size.Y * item.Scale) :
                               new Vector2(doorSprite.size.X * item.Scale, item.Rect.Height * (1.0f - openState));

            Vector2 simSize = ConvertUnits.ToSimUnits(currSize);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled)
                {
                    continue;
                }
                if (!MathUtils.IsValid(c.SimPosition))
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")");
                    }
                    GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")." +
                                                           " Removed: " + c.Removed +
                                                           " Remoteplayer: " + c.IsRemotePlayer);
                    continue;
                }
                int dir = IsHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);

                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered)
                    {
                        continue;
                    }
                    if (PushBodyOutOfDoorway(c, limb.body, dir, simPos, simSize) && damageSoundCooldown <= 0.0f)
                    {
#if CLIENT
                        SoundPlayer.PlayDamageSound("LimbBlunt", 1.0f, limb.body);
#endif
                        damageSoundCooldown = 0.5f;
                    }
                }
                PushBodyOutOfDoorway(c, c.AnimController.Collider, dir, simPos, simSize);
            }
        }
示例#7
0
 public void Awake()
 {
     //Check if there is an existing instance of this object
     if (instance && instance != this)
     {
         DestroyImmediate(gameObject); //Delete duplicate
     }
     else
     {
         instance = this;               //Make this object the only instance
         DontDestroyOnLoad(gameObject); //Set as do not destroy
     }
 }
示例#8
0
        public void SetContainedItemPositions()
        {
            Vector2 simPos          = item.SimPosition;
            Vector2 displayPos      = item.Position;
            float   currentRotation = itemRotation;

            if (item.body != null)
            {
                currentRotation += item.body.Rotation;
            }

            foreach (Item contained in Inventory.Items)
            {
                if (contained == null)
                {
                    continue;
                }
                if (contained.body != null)
                {
                    try
                    {
                        contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, currentRotation);
                        contained.body.SetPrevTransform(contained.body.SimPosition, contained.body.Rotation);
                        contained.body.UpdateDrawPosition();
                    }
                    catch (Exception e)
                    {
                        DebugConsole.Log("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace);
                        GameAnalyticsManager.AddErrorEventOnce("ItemContainer.SetContainedItemPositions.InvalidPosition:" + contained.Name,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                               "SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace);
                    }
                    contained.body.Submarine = item.Submarine;
                }

                contained.Rect =
                    new Rectangle(
                        (int)(displayPos.X - contained.Rect.Width / 2.0f),
                        (int)(displayPos.Y + contained.Rect.Height / 2.0f),
                        contained.Rect.Width, contained.Rect.Height);

                contained.Submarine   = item.Submarine;
                contained.CurrentHull = item.CurrentHull;

                contained.SetContainedItemPositions();
            }
        }
示例#9
0
        private float GetSoundVolume(ItemSound sound)
        {
            if (sound == null)
            {
                return(0.0f);
            }
            if (sound.VolumeProperty == "")
            {
                return(sound.VolumeMultiplier);
            }

            if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
            {
                float newVolume = 0.0f;
                try
                {
                    newVolume = (float)property.GetValue(this);
                }
                catch
                {
                    return(0.0f);
                }
                newVolume *= sound.VolumeMultiplier;

                if (!MathUtils.IsValid(newVolume))
                {
                    DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
                    GameAnalyticsManager.AddErrorEventOnce(
                        "ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
                    return(0.0f);
                }

                return(MathHelper.Clamp(newVolume, 0.0f, 1.0f));
            }

            return(0.0f);
        }
示例#10
0
        public static void Check(string extraErrorMsg = "")
        {
            ALError error;

            if ((error = AL.GetError()) != ALError.NoError)
            {
                string errorMsg = "OpenAL error: " + AL.GetErrorString(error);
                if (!string.IsNullOrEmpty(extraErrorMsg))
                {
                    errorMsg += " {" + extraErrorMsg + "} ";
                }
                errorMsg += "\n" + Environment.StackTrace;

#if DEBUG
                DebugConsole.ThrowError(errorMsg);
#else
                DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
#endif
                GameAnalyticsManager.AddErrorEventOnce(
                    "OggStream.Check:" + AL.GetErrorString(error) + extraErrorMsg,
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
            }
        }
示例#11
0
        private void CreateDoorBody()
        {
            if (doorBody != null)
            {
                GameMain.World.Remove(doorBody);
                doorBody = null;
            }

            Vector2 position = ConvertUnits.ToSimUnits(item.Position + (DockingTarget.Door.Item.WorldPosition - item.WorldPosition));

            if (!MathUtils.IsValid(position))
            {
                string errorMsg =
                    "Attempted to create a door body at an invalid position (item pos: " + item.Position
                    + ", item world pos: " + item.WorldPosition
                    + ", docking target world pos: " + DockingTarget.Door.Item.WorldPosition + ")\n" + Environment.StackTrace.CleanupStackTrace();

                DebugConsole.ThrowError(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce(
                    "DockingPort.CreateDoorBody:InvalidPosition",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
                position = Vector2.Zero;
            }

            System.Diagnostics.Debug.Assert(doorBody == null);

            doorBody = GameMain.World.CreateRectangle(
                DockingTarget.Door.Body.width,
                DockingTarget.Door.Body.height,
                1.0f,
                position);
            doorBody.UserData            = DockingTarget.Door;
            doorBody.CollisionCategories = Physics.CollisionWall;
            doorBody.BodyType            = BodyType.Static;
        }
        /// <summary>
        /// Read the events from the message, ignoring ones we've already received
        /// </summary>
        public void Read(ServerNetObject type, NetIncomingMessage msg, float sendingTime, List <IServerSerializable> entities)
        {
            UInt16 unreceivedEntityEventCount = 0;

            if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
            {
                unreceivedEntityEventCount = msg.ReadUInt16();
                firstNewID = msg.ReadUInt16();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage(
                        "received midround syncing msg, unreceived: " + unreceivedEntityEventCount +
                        ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow);
                }
            }
            else if (firstNewID != null)
            {
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1),
                                            Microsoft.Xna.Framework.Color.Yellow);
                }

                lastReceivedID = (UInt16)(firstNewID - 1);
                firstNewID     = null;
            }

            entities.Clear();

            UInt16 firstEventID = msg.ReadUInt16();
            int    eventCount   = msg.ReadByte();

            for (int i = 0; i < eventCount; i++)
            {
                UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
                UInt16 entityID    = msg.ReadUInt16();

                if (entityID == 0)
                {
                    msg.ReadPadBits();
                    if (thisEventID == (UInt16)(lastReceivedID + 1))
                    {
                        lastReceivedID++;
                    }
                    continue;
                }

                byte msgLength = msg.ReadByte();

                IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
                entities.Add(entity);

                //skip the event if we've already received it or if the entity isn't found
                if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        if (thisEventID != (UInt16)(lastReceivedID + 1))
                        {
                            DebugConsole.NewMessage(
                                "received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
                                thisEventID < lastReceivedID + 1
                                    ? Microsoft.Xna.Framework.Color.Yellow
                                    : Microsoft.Xna.Framework.Color.Red);
                        }
                        else if (entity == null)
                        {
                            DebugConsole.NewMessage(
                                "received msg " + thisEventID + ", entity " + entityID + " not found",
                                Microsoft.Xna.Framework.Color.Red);
                        }
                    }
                    msg.Position += msgLength * 8;
                }
                else
                {
                    long msgPosition = msg.Position;
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
                                                Microsoft.Xna.Framework.Color.Green);
                    }
                    lastReceivedID++;
                    try
                    {
                        ReadEvent(msg, entity, sendingTime);
                    }

                    catch (Exception e)
                    {
                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
                        }
                        GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                               "Failed to read event for entity \"" + entity.ToString() + "\"!\n" + e.StackTrace);
                        msg.Position = msgPosition + msgLength * 8;
                    }
                }
                msg.ReadPadBits();
            }
        }
示例#13
0
        private VoipCapture(string deviceName) : base(GameMain.Client?.ID ?? 0, true, false)
        {
            Disconnected = false;

            VoipConfig.SetupEncoding();

            //set up capture device
            captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 5);

            if (captureDevice == IntPtr.Zero)
            {
                DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(), Color.Orange);
                //attempt using a smaller buffer size
                captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2);
            }

            if (captureDevice == IntPtr.Zero)
            {
                DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 2 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(), Color.Orange);
                //attempt using the default device
                captureDevice = Alc.CaptureOpenDevice("", VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2);
            }

            if (captureDevice == IntPtr.Zero)
            {
                string errorCode = Alc.GetError(IntPtr.Zero).ToString();
                if (!GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "capturedevicenotfound"))
                {
                    GUI.SettingsMenuOpen = false;
                    new GUIMessageBox(TextManager.Get("Error"),
                                      (TextManager.Get("VoipCaptureDeviceNotFound", returnNull: true) ?? "Could not start voice capture, suitable capture device not found.") + " (" + errorCode + ")")
                    {
                        UserData = "capturedevicenotfound"
                    };
                }
                GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpenFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       "Alc.CaptureDeviceOpen(" + deviceName + ") failed. Error code: " + errorCode);
                GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled;
                Instance?.Dispose();
                Instance = null;
                return;
            }

            int alError  = Al.GetError();
            int alcError = Alc.GetError(captureDevice);

            if (alcError != Alc.NoError)
            {
                throw new Exception("Failed to open capture device: " + alcError.ToString() + " (ALC)");
            }
            if (alError != Al.NoError)
            {
                throw new Exception("Failed to open capture device: " + alError.ToString() + " (AL)");
            }

            CanDetectDisconnect = Alc.IsExtensionPresent(captureDevice, "ALC_EXT_disconnect");
            alcError            = Alc.GetError(captureDevice);
            if (alcError != Alc.NoError)
            {
                throw new Exception("Error determining if disconnect can be detected: " + alcError.ToString());
            }

            Alc.CaptureStart(captureDevice);
            alcError = Alc.GetError(captureDevice);
            if (alcError != Alc.NoError)
            {
                throw new Exception("Failed to start capturing: " + alcError.ToString());
            }

            capturing     = true;
            captureThread = new Thread(UpdateCapture)
            {
                IsBackground = true,
                Name         = "VoipCapture"
            };
            captureThread.Start();
        }
示例#14
0
文件: Door.cs 项目: lexuv2/Barotrauma
        private bool PushBodyOutOfDoorway(Character c, PhysicsBody body, int dir, Vector2 doorRectSimPos, Vector2 doorRectSimSize)
        {
            if (!MathUtils.IsValid(body.SimPosition))
            {
                DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
                GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:LimbPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       "Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + body.SimPosition + ")." +
                                                       " Removed: " + c.Removed +
                                                       " Remoteplayer: " + c.IsRemotePlayer);
                return(false);
            }

            float diff;

            if (IsHorizontal)
            {
                if (body.SimPosition.X < doorRectSimPos.X || body.SimPosition.X > doorRectSimPos.X + doorRectSimSize.X)
                {
                    return(false);
                }
                diff = body.SimPosition.Y - item.SimPosition.Y;
            }
            else
            {
                if (body.SimPosition.Y > doorRectSimPos.Y || body.SimPosition.Y < doorRectSimPos.Y - doorRectSimSize.Y)
                {
                    return(false);
                }
                diff = body.SimPosition.X - item.SimPosition.X;
            }

            //if the limb is at a different side of the door than the character (collider),
            //immediately teleport it to the correct side
            if (Math.Sign(diff) != dir)
            {
                if (IsHorizontal)
                {
                    body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f), body.Rotation);
                }
                else
                {
                    body.SetTransform(new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
                }
            }

            //apply an impulse to push the limb further from the door
            if (IsHorizontal)
            {
                if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > doorRectSimSize.Y * 0.5f)
                {
                    return(false);
                }
                body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
            }
            else
            {
                if (Math.Abs(body.SimPosition.X - item.SimPosition.X) > doorRectSimSize.X * 0.5f)
                {
                    return(false);
                }
                body.ApplyLinearImpulse(new Vector2(dir * 2.0f, isOpen ? 0.0f : -1.0f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
            }

            //don't stun if the door was broken a moment ago
            //otherwise enabling the door's collider and pushing the character away will interrupt repairing
            if (lastBrokenTime < Timing.TotalTime - 1.0f)
            {
                c.SetStun(0.2f);
            }
            return(true);
        }
示例#15
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            bool isDocked = msg.ReadBoolean();

            for (int i = 0; i < 2; i++)
            {
                if (hulls[i] == null)
                {
                    continue;
                }
                item.linkedTo.Remove(hulls[i]);
                hulls[i].Remove();
                hulls[i] = null;
            }

            if (gap != null)
            {
                item.linkedTo.Remove(gap);
                gap.Remove();
                gap = null;
            }

            if (isDocked)
            {
                ushort dockingTargetID = msg.ReadUInt16();

                bool isLocked = msg.ReadBoolean();

                Entity targetEntity = Entity.FindEntityByID(dockingTargetID);
                if (targetEntity == null || !(targetEntity is Item))
                {
                    DebugConsole.ThrowError("Invalid docking port network event (can't dock to " + targetEntity?.ToString() ?? "null" + ")");
                    return;
                }

                DockingTarget = (targetEntity as Item).GetComponent <DockingPort>();
                if (DockingTarget == null)
                {
                    DebugConsole.ThrowError("Invalid docking port network event (" + targetEntity + " doesn't have a docking port component)");
                    return;
                }

                Dock(DockingTarget);
                if (joint == null)
                {
                    string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." +
                                      " Submarine: " + (item.Submarine?.Name ?? "null") +
                                      ", target submarine: " + (DockingTarget.item.Submarine?.Name ?? "null");
                    if (item.Submarine?.DockedTo.Contains(DockingTarget.item.Submarine) ?? false)
                    {
                        errorMsg += "\nAlready docked.";
                    }
                    if (item.Submarine == DockingTarget.item.Submarine)
                    {
                        errorMsg += "\nTrying to dock the submarine to itself.";
                    }
                    GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                }

                if (isLocked)
                {
                    Lock(isNetworkMessage: true, forcePosition: true);
                }
            }
            else
            {
                Undock();
            }
        }
示例#16
0
        /// <summary>
        /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
        /// </summary>
        protected void Write(NetOutgoingMessage msg, List <NetEntityEvent> eventsToSync, out List <NetEntityEvent> sentEvents, Client recipient = null)
        {
            //write into a temporary buffer so we can write the number of events before the actual data
            NetBuffer tempBuffer = new NetBuffer();

            sentEvents = new List <NetEntityEvent>();

            int eventCount = 0;

            foreach (NetEntityEvent e in eventsToSync)
            {
                //write into a temporary buffer so we can write the length before the actual data
                NetBuffer tempEventBuffer = new NetBuffer();
                try
                {
                    WriteEvent(tempEventBuffer, e, recipient);
                }

                catch (Exception exception)
                {
                    DebugConsole.ThrowError("Failed to write an event for the entity \"" + e.Entity + "\"", exception);
                    GameAnalyticsManager.AddErrorEventOnce("NetEntityEventManager.Write:WriteFailed" + e.Entity.ToString(),
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to write an event for the entity \"" + e.Entity + "\"\n" + exception.StackTrace);

                    //write an empty event to avoid messing up IDs
                    //(otherwise the clients might read the next event in the message and think its ID
                    //is consecutive to the previous one, even though we skipped over this broken event)
                    tempBuffer.Write(Entity.NullEntityID);
                    tempBuffer.WritePadBits();
                    eventCount++;
                    continue;
                }

                //the length of the data is written as a byte, so the data needs to be less than 255 bytes long
                if (tempEventBuffer.LengthBytes > 255)
                {
                    DebugConsole.ThrowError("Too much data in network event for entity \"" + e.Entity.ToString() + "\" (" + tempEventBuffer.LengthBytes + " bytes");
                    GameAnalyticsManager.AddErrorEventOnce("NetEntityEventManager.Write:TooLong" + e.Entity.ToString(),
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Too much data in network event for entity \"" + e.Entity.ToString() + "\" (" + tempEventBuffer.LengthBytes + " bytes");

                    //write an empty event to prevent breaking the event syncing
                    tempBuffer.Write(Entity.NullEntityID);
                    tempBuffer.WritePadBits();
                    eventCount++;
                    continue;
                }

                if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength)
                {
                    //no more room in this packet
                    break;
                }

                tempBuffer.Write(e.EntityID);
                tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
                tempBuffer.Write(tempEventBuffer);
                tempBuffer.WritePadBits();
                sentEvents.Add(e);

                eventCount++;
            }

            if (eventCount > 0)
            {
                msg.Write(eventsToSync[0].ID);
                msg.Write((byte)eventCount);
                msg.Write(tempBuffer);
            }
        }
示例#17
0
        /// <summary>
        /// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
        /// </summary>
        public bool Read(ServerNetObject type, NetIncomingMessage msg, float sendingTime, List <IServerSerializable> entities)
        {
            UInt16 unreceivedEntityEventCount = 0;

            if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
            {
                unreceivedEntityEventCount = msg.ReadUInt16();
                firstNewID = msg.ReadUInt16();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage(
                        "received midround syncing msg, unreceived: " + unreceivedEntityEventCount +
                        ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow);
                }
            }
            else if (firstNewID != null)
            {
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1),
                                            Microsoft.Xna.Framework.Color.Yellow);
                }

                lastReceivedID = (UInt16)(firstNewID - 1);
                firstNewID     = null;
            }

            entities.Clear();

            UInt16 firstEventID = msg.ReadUInt16();
            int    eventCount   = msg.ReadByte();

            for (int i = 0; i < eventCount; i++)
            {
                UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
                UInt16 entityID    = msg.ReadUInt16();

                if (entityID == Entity.NullEntityID)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
                                                Microsoft.Xna.Framework.Color.Orange);
                    }
                    msg.ReadPadBits();
                    entities.Add(null);
                    if (thisEventID == (UInt16)(lastReceivedID + 1))
                    {
                        lastReceivedID++;
                    }
                    continue;
                }

                byte msgLength = msg.ReadByte();

                IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
                entities.Add(entity);

                //skip the event if we've already received it or if the entity isn't found
                if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
                {
                    if (thisEventID != (UInt16)(lastReceivedID + 1))
                    {
                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.NewMessage(
                                "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
                                NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
                                    ? Microsoft.Xna.Framework.Color.Red
                                    : Microsoft.Xna.Framework.Color.Yellow);
                        }
                    }
                    else if (entity == null)
                    {
                        DebugConsole.NewMessage(
                            "Received msg " + thisEventID + ", entity " + entityID + " not found",
                            Microsoft.Xna.Framework.Color.Red);
                        GameMain.Client.ReportError(ClientNetError.MISSING_ENTITY, eventID: thisEventID, entityID: entityID);
                        return(false);
                    }

                    msg.Position += msgLength * 8;
                }
                else
                {
                    long msgPosition = msg.Position;
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
                                                Microsoft.Xna.Framework.Color.Green);
                    }
                    lastReceivedID++;
                    try
                    {
                        ReadEvent(msg, entity, sendingTime);
                    }

                    catch (Exception e)
                    {
                        string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace;
                        errorMsg += "\nPrevious entities:";
                        for (int j = entities.Count - 2; j >= 0; j--)
                        {
                            errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                        }

                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
                        }
                        GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                        msg.Position = msgPosition + msgLength * 8;
                    }
                }
                msg.ReadPadBits();
            }
            return(true);
        }
        public override void Update(float deltaTime)
        {
            if (netServer == null)
            {
                return;
            }

            if (OnOwnerDetermined != null && OwnerConnection != null)
            {
                OnOwnerDetermined?.Invoke(OwnerConnection);
                OnOwnerDetermined = null;
            }

            netServer.ReadMessages(incomingLidgrenMessages);

            //process incoming connections first
            foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType == NetIncomingMessageType.ConnectionApproval))
            {
                HandleConnection(inc);
            }

            try
            {
                //after processing connections, go ahead with the rest of the messages
                foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType != NetIncomingMessageType.ConnectionApproval))
                {
                    switch (inc.MessageType)
                    {
                    case NetIncomingMessageType.Data:
                        HandleDataMessage(inc);
                        break;

                    case NetIncomingMessageType.StatusChanged:
                        HandleStatusChanged(inc);
                        break;
                    }
                }
            }

            catch (Exception e)
            {
                string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace.CleanupStackTrace();
                GameAnalyticsManager.AddErrorEventOnce("LidgrenServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
#if DEBUG
                DebugConsole.ThrowError(errorMsg);
#else
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
#endif
            }

            for (int i = 0; i < pendingClients.Count; i++)
            {
                PendingClient pendingClient = pendingClients[i];

                var connection = pendingClient.Connection as LidgrenConnection;
                if (connection.NetConnection.Status == NetConnectionStatus.InitiatedConnect ||
                    connection.NetConnection.Status == NetConnectionStatus.ReceivedInitiation ||
                    connection.NetConnection.Status == NetConnectionStatus.RespondedAwaitingApproval ||
                    connection.NetConnection.Status == NetConnectionStatus.RespondedConnect)
                {
                    continue;
                }
                UpdatePendingClient(pendingClient);
                if (i >= pendingClients.Count || pendingClients[i] != pendingClient)
                {
                    i--;
                }
            }

            incomingLidgrenMessages.Clear();
        }
示例#19
0
        // TODO: Consider using generics, interfaces, or inheritance instead of reflection -> would be easier to debug when something changes/goes wrong.
        // For example, currently we can edit the constructors but they will fail in runtime because the parameters are not changed here.
        // It's also painful to find where the constructors are used, because the references exist only at runtime.
        public static ItemComponent Load(XElement element, Item item, string file, bool errorMessages = true)
        {
            Type   t;
            string type = element.Name.ToString().ToLowerInvariant();

            try
            {
                // Get the type of a specified class.
                t = Type.GetType("Barotrauma.Items.Components." + type + "", false, true);
                if (t == null)
                {
                    if (errorMessages)
                    {
                        DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + file + ")");
                    }
                    return(null);
                }
            }
            catch (Exception e)
            {
                if (errorMessages)
                {
                    DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + file + ")", e);
                }
                return(null);
            }

            ConstructorInfo constructor;

            try
            {
                if (t != typeof(ItemComponent) && !t.IsSubclassOf(typeof(ItemComponent)))
                {
                    return(null);
                }
                constructor = t.GetConstructor(new Type[] { typeof(Item), typeof(XElement) });
                if (constructor == null)
                {
                    DebugConsole.ThrowError("Could not find the constructor of the component \"" + type + "\" (" + file + ")");
                    return(null);
                }
            }
            catch (Exception e)
            {
                DebugConsole.ThrowError("Could not find the constructor of the component \"" + type + "\" (" + file + ")", e);
                return(null);
            }
            ItemComponent ic = null;

            try
            {
                object[] lobject   = new object[] { item, element };
                object   component = constructor.Invoke(lobject);
                ic      = (ItemComponent)component;
                ic.name = element.Name.ToString();
            }
            catch (TargetInvocationException e)
            {
                DebugConsole.ThrowError("Error while loading entity of the type " + t + ".", e.InnerException);
                GameAnalyticsManager.AddErrorEventOnce("ItemComponent.Load:TargetInvocationException" + item.Name + element.Name,
                                                       GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       "Error while loading entity of the type " + t + " (" + e.InnerException + ")\n" + Environment.StackTrace.CleanupStackTrace());
            }

            return(ic);
        }
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsIncapacitated &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
                        GameServer.Log(errorMsg + "\n" + e.StackTrace.CleanupStackTrace(), ServerLog.MessageType.Error);
                        DebugConsole.ThrowError(errorMsg, e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace.CleanupStackTrace());
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAnyone = inGameClients[0].LastRecvEntityEventID;
                lastSentToAll    = inGameClients[0].LastRecvEntityEventID;

                if (server.OwnerConnection != null)
                {
                    var owner = clients.Find(c => c.Connection == server.OwnerConnection);
                    if (owner != null)
                    {
                        lastSentToAll = owner.LastRecvEntityEventID;
                    }
                }
                inGameClients.ForEach(c =>
                {
                    if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID))
                    {
                        lastSentToAll = c.LastRecvEntityEventID;
                    }
                    if (NetIdUtils.IdMoreRecent(c.LastRecvEntityEventID, lastSentToAnyone))
                    {
                        lastSentToAnyone = c.LastRecvEntityEventID;
                    }
                });
                lastSentToAnyoneTime = events.Find(e => e.ID == lastSentToAnyone)?.CreateTime ?? Timing.TotalTime;

                if (Timing.TotalTime - lastWarningTime > 5.0 &&
                    Timing.TotalTime - lastSentToAnyoneTime > 10.0 &&
                    Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration)
                {
                    lastWarningTime = Timing.TotalTime;
                    GameServer.Log("WARNING: ServerEntityEventManager is lagging behind! Last sent id: " + lastSentToAnyone.ToString() + ", latest create id: " + ID.ToString(), ServerLog.MessageType.ServerMessage);
                    events.ForEach(e => e.ResetCreateTime());
                    //TODO: reset clients if this happens, maybe do it if a majority are behind rather than all of them?
                }

                clients.Where(c => c.NeedsMidRoundSync).ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.FirstNewEventID))
                                                                       {
                                                                           lastSentToAll = (ushort)(c.FirstNewEventID - 1);
                                                                       }
                                                                });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null &&
                    Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration &&
                    ((lastSentToAnyoneTime - firstEventToResend.CreateTime) > NetConfig.OldReceivedEventKickTime || (Timing.TotalTime - firstEventToResend.CreateTime) > NetConfig.OldEventKickTime))
                {
                    //  This event is 10 seconds older than the last one we've successfully sent,
                    //  kick everyone that hasn't received it yet, this is way too old
                    //  UNLESS the event was created when the client was still midround syncing,
                    //  in which case we'll wait until the timeout runs out before kicking the client
                    List <Client> toKick = inGameClients.FindAll(c =>
                                                                 NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID) &&
                                                                 (firstEventToResend.CreateTime > c.MidRoundSyncTimeOut || lastSentToAnyoneTime > c.MidRoundSyncTimeOut || Timing.TotalTime > c.MidRoundSyncTimeOut + 10.0));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a very old network event (" + (c.LastRecvEntityEventID + 1).ToString() + ")", Color.Red);
                        GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a very old network event ("
                                       + (c.LastRecvEntityEventID + 1).ToString() +
                                       " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago, " +
                                       (lastSentToAnyoneTime - firstEventToResend.CreateTime).ToString("0.##") + " s older than last event sent to anyone)" +
                                       " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncOldEvent + "/ServerMessage.ExcessiveDesyncOldEvent");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
                        GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncRemovedEvent + "/ServerMessage.ExcessiveDesyncRemovedEvent");
                    });
                }
            }

            var timedOutClients = clients.FindAll(c => c.Connection != GameMain.Server.OwnerConnection && c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            foreach (Client timedOutClient in timedOutClients)
            {
                GameServer.Log("Disconnecting client " + GameServer.ClientLogName(timedOutClient) + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
                GameMain.Server.DisconnectClient(timedOutClient, "", DisconnectReason.SyncTimeout + "/ServerMessage.SyncTimeout");
            }

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
示例#21
0
        private void PushCharactersAway()
        {
            if (!MathUtils.IsValid(item.SimPosition))
            {
                DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ")");
                GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:DoorPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       "Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ").");
                return;
            }

            //push characters out of the doorway when the door is closing/opening
            Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y));

            Vector2 currSize = isHorizontal ?
                               new Vector2(item.Rect.Width * (1.0f - openState), doorSprite.size.Y) :
                               new Vector2(doorSprite.size.X, item.Rect.Height * (1.0f - openState));

            Vector2 simSize = ConvertUnits.ToSimUnits(currSize);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled)
                {
                    continue;
                }
                if (!MathUtils.IsValid(c.SimPosition))
                {
                    DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")");
                    GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")." +
                                                           " Removed: " + c.Removed +
                                                           " Remoteplayer: " + c.IsRemotePlayer);
                    continue;
                }
                int dir = isHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);

                List <PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList();
                bodies.Add(c.AnimController.Collider);

                foreach (PhysicsBody body in bodies)
                {
                    float diff = 0.0f;
                    if (!MathUtils.IsValid(body.SimPosition))
                    {
                        DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
                        GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:LimbPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                               "Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + body.SimPosition + ")." +
                                                               " Removed: " + c.Removed +
                                                               " Remoteplayer: " + c.IsRemotePlayer);
                        continue;
                    }

                    if (isHorizontal)
                    {
                        if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X)
                        {
                            continue;
                        }
                        diff = body.SimPosition.Y - item.SimPosition.Y;
                    }
                    else
                    {
                        if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y)
                        {
                            continue;
                        }
                        diff = body.SimPosition.X - item.SimPosition.X;
                    }

                    if (Math.Sign(diff) != dir)
                    {
#if CLIENT
                        SoundPlayer.PlayDamageSound("LimbBlunt", 1.0f, body);
#endif

                        if (isHorizontal)
                        {
                            body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * simSize.Y * 2.0f), body.Rotation);
                            body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f));
                        }
                        else
                        {
                            body.SetTransform(new Vector2(item.SimPosition.X + dir * simSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
                            body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f));
                        }
                    }

                    if (isHorizontal)
                    {
                        if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > simSize.Y * 0.5f)
                        {
                            continue;
                        }

                        body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 0.5f));
                    }
                    else
                    {
                        if (Math.Abs(body.SimPosition.X - item.SimPosition.X) > simSize.X * 0.5f)
                        {
                            continue;
                        }

                        body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f));
                    }

                    c.SetStun(0.2f);
                }
            }
        }
示例#22
0
        /// <summary>
        /// Creates a copy of the specified workshop item in the staging folder and an editor that can be used to edit and update the item
        /// </summary>
        public static void CreateWorkshopItemStaging(Workshop.Item existingItem, out Workshop.Editor itemEditor, out ContentPackage contentPackage)
        {
            if (!existingItem.Installed)
            {
                itemEditor     = null;
                contentPackage = null;
                DebugConsole.ThrowError("Cannot edit the workshop item \"" + existingItem.Title + "\" because it has not been installed.");
                return;
            }

            var stagingFolder = new DirectoryInfo(WorkshopItemStagingFolder);

            if (stagingFolder.Exists)
            {
                SaveUtil.ClearFolder(stagingFolder.FullName);
            }
            else
            {
                stagingFolder.Create();
            }

            itemEditor                     = instance.client.Workshop.EditItem(existingItem.Id);
            itemEditor.Visibility          = Workshop.Editor.VisibilityType.Public;
            itemEditor.Title               = existingItem.Title;
            itemEditor.Tags                = existingItem.Tags.ToList();
            itemEditor.Description         = existingItem.Description;
            itemEditor.WorkshopUploadAppId = AppID;
            itemEditor.Folder              = stagingFolder.FullName;

            string previewImagePath = Path.GetFullPath(Path.Combine(itemEditor.Folder, PreviewImageName));

            itemEditor.PreviewImage = previewImagePath;

            try
            {
                if (File.Exists(previewImagePath))
                {
                    File.Delete(previewImagePath);
                }

                Uri    baseAddress = new Uri(existingItem.PreviewImageUrl);
                Uri    directory   = new Uri(baseAddress, "."); // "." == current dir, like MS-DOS
                string fileName    = Path.GetFileName(baseAddress.LocalPath);

                IRestClient client   = new RestClient(directory);
                var         request  = new RestRequest(fileName, Method.GET);
                var         response = client.Execute(request);

                if (response.ResponseStatus == ResponseStatus.Completed)
                {
                    File.WriteAllBytes(previewImagePath, response.RawBytes);
                }
            }

            catch (Exception e)
            {
                string errorMsg = "Failed to save workshop item preview image to \"" + previewImagePath + "\" when creating workshop item staging folder.";
                GameAnalyticsManager.AddErrorEventOnce("SteamManager.CreateWorkshopItemStaging:WriteAllBytesFailed" + previewImagePath,
                                                       GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message);
            }

            ContentPackage tempContentPackage = new ContentPackage(Path.Combine(existingItem.Directory.FullName, MetadataFileName));

            //item already installed, copy it from the game folder
            if (existingItem != null && CheckWorkshopItemEnabled(existingItem, checkContentFiles: false))
            {
                string installedItemPath = GetWorkshopItemContentPackagePath(tempContentPackage);
                if (File.Exists(installedItemPath))
                {
                    tempContentPackage = new ContentPackage(installedItemPath);
                }
            }
            if (File.Exists(tempContentPackage.Path))
            {
                string newContentPackagePath = Path.Combine(WorkshopItemStagingFolder, MetadataFileName);
                File.Copy(tempContentPackage.Path, newContentPackagePath, overwrite: true);
                contentPackage = new ContentPackage(newContentPackagePath);
                foreach (ContentFile contentFile in tempContentPackage.Files)
                {
                    string sourceFile;
                    if (contentFile.Type == ContentType.Submarine && File.Exists(contentFile.Path))
                    {
                        sourceFile = contentFile.Path;
                    }
                    else
                    {
                        sourceFile = Path.Combine(existingItem.Directory.FullName, contentFile.Path);
                    }
                    if (!File.Exists(sourceFile))
                    {
                        continue;
                    }
                    //make sure the destination directory exists
                    string destinationPath = Path.Combine(WorkshopItemStagingFolder, contentFile.Path);
                    Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
                    File.Copy(sourceFile, destinationPath, overwrite: true);
                    contentPackage.AddFile(contentFile.Path, contentFile.Type);
                }
            }
            else
            {
                contentPackage = ContentPackage.CreatePackage(existingItem.Title, Path.Combine(WorkshopItemStagingFolder, MetadataFileName), false);
                contentPackage.Save(contentPackage.Path);
            }
        }
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsUnconscious &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.ThrowError("Failed to read server event for entity \"" + entityName + "\"!", e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace);
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAll = inGameClients[0].LastRecvEntityEventID;
                inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID))
                                             {
                                                 lastSentToAll = c.LastRecvEntityEventID;
                                             }
                                      });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null && (Timing.TotalTime - firstEventToResend.CreateTime) > 10.0f)
                {
                    //it's been 10 seconds since this event was created
                    //kick everyone that hasn't received it yet, this is way too old
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected old event " + c.LastRecvEntityEventID.ToString() + ")", Microsoft.Xna.Framework.Color.Red);
                        server.DisconnectClient(c, "", "You have been disconnected because of excessive desync");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected " + c.LastRecvEntityEventID.ToString() + ", last available is " + events[0].ID.ToString() + ")", Microsoft.Xna.Framework.Color.Red);
                        server.DisconnectClient(c, "", "You have been disconnected because of excessive desync");
                    }
                                   );
                }
            }

            var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            timedOutClients.ForEach(c => GameMain.Server.DisconnectClient(c, "", "You have been disconnected because syncing your client with the server took too long."));

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
示例#24
0
        public void SetContainedItemPositions()
        {
            Vector2 transformedItemPos                = ItemPos * item.Scale;
            Vector2 transformedItemInterval           = ItemInterval * item.Scale;
            Vector2 transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
            Vector2 transformedItemIntervalVertical   = new Vector2(0.0f, transformedItemInterval.Y);

            if (item.body == null)
            {
                if (item.FlippedX)
                {
                    transformedItemPos.X                = -transformedItemPos.X;
                    transformedItemPos.X               += item.Rect.Width;
                    transformedItemInterval.X           = -transformedItemInterval.X;
                    transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
                }
                if (item.FlippedY)
                {
                    transformedItemPos.Y              = -transformedItemPos.Y;
                    transformedItemPos.Y             -= item.Rect.Height;
                    transformedItemInterval.Y         = -transformedItemInterval.Y;
                    transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
                }
                transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
                if (Math.Abs(item.Rotation) > 0.01f)
                {
                    Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
                    transformedItemPos                = Vector2.Transform(transformedItemPos, transform);
                    transformedItemInterval           = Vector2.Transform(transformedItemInterval, transform);
                    transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
                    transformedItemIntervalVertical   = Vector2.Transform(transformedItemIntervalVertical, transform);
                }
            }
            else
            {
                Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
                if (item.body.Dir == -1.0f)
                {
                    transformedItemPos.X                = -transformedItemPos.X;
                    transformedItemInterval.X           = -transformedItemInterval.X;
                    transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
                }
                transformedItemPos                = Vector2.Transform(transformedItemPos, transform);
                transformedItemInterval           = Vector2.Transform(transformedItemInterval, transform);
                transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
                transformedItemPos               += item.Position;
            }

            float currentRotation = itemRotation;

            if (item.body != null)
            {
                currentRotation += item.body.Rotation;
            }

            int     i = 0;
            Vector2 currentItemPos = transformedItemPos;

            foreach (Item contained in Inventory.AllItems)
            {
                if (contained.body != null)
                {
                    try
                    {
                        Vector2 simPos = ConvertUnits.ToSimUnits(currentItemPos);
                        contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, currentRotation);
                        contained.body.SetPrevTransform(contained.body.SimPosition, contained.body.Rotation);
                        contained.body.UpdateDrawPosition();
                    }
                    catch (Exception e)
                    {
                        DebugConsole.Log("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace.CleanupStackTrace());
                        GameAnalyticsManager.AddErrorEventOnce("ItemContainer.SetContainedItemPositions.InvalidPosition:" + contained.Name,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                               "SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace.CleanupStackTrace());
                    }
                    contained.body.Submarine = item.Submarine;
                }

                contained.Rect =
                    new Rectangle(
                        (int)(currentItemPos.X - contained.Rect.Width / 2.0f),
                        (int)(currentItemPos.Y + contained.Rect.Height / 2.0f),
                        contained.Rect.Width, contained.Rect.Height);

                contained.Submarine   = item.Submarine;
                contained.CurrentHull = item.CurrentHull;
                contained.SetContainedItemPositions();

                i++;
                if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
                {
                    //interval set on both axes -> use a grid layout
                    currentItemPos += transformedItemIntervalHorizontal;
                    if (i % ItemsPerRow == 0)
                    {
                        currentItemPos  = transformedItemPos;
                        currentItemPos += transformedItemIntervalVertical * (i / ItemsPerRow);
                    }
                }
                else
                {
                    currentItemPos += transformedItemInterval;
                }
            }
        }
        public override void Update(float deltaTime)
        {
            if (netServer == null)
            {
                return;
            }

            if (OnOwnerDetermined != null && OwnerConnection != null)
            {
                OnOwnerDetermined?.Invoke(OwnerConnection);
                OnOwnerDetermined = null;
            }

            netServer.ReadMessages(incomingLidgrenMessages);

            //backwards for loop so we can remove elements while iterating
            for (int i = connectedClients.Count - 1; i >= 0; i--)
            {
                connectedClients[i].Decay(deltaTime);
                if (connectedClients[i].Timeout < 0.0)
                {
                    Disconnect(connectedClients[i], "Timed out");
                }
            }

            //process incoming connections first
            foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType == NetIncomingMessageType.ConnectionApproval))
            {
                HandleConnection(inc);
            }


            try
            {
                //after processing connections, go ahead with the rest of the messages
                foreach (NetIncomingMessage inc in incomingLidgrenMessages.Where(m => m.MessageType != NetIncomingMessageType.ConnectionApproval))
                {
                    switch (inc.MessageType)
                    {
                    case NetIncomingMessageType.Data:
                        HandleDataMessage(inc);
                        break;

                    case NetIncomingMessageType.StatusChanged:
                        HandleStatusChanged(inc);
                        break;
                    }
                }
            }

            catch (Exception e)
            {
                string errorMsg = "Server failed to read an incoming message. {" + e + "}\n" + e.StackTrace;
                GameAnalyticsManager.AddErrorEventOnce("SteamP2PServerPeer.Update:ClientReadException" + e.TargetSite.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
#if DEBUG
                DebugConsole.ThrowError(errorMsg);
#else
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
#endif
            }

            for (int i = 0; i < pendingClients.Count; i++)
            {
                PendingClient pendingClient = pendingClients[i];
                UpdatePendingClient(pendingClient);
                if (i >= pendingClients.Count || pendingClients[i] != pendingClient)
                {
                    i--;
                }
            }

            incomingLidgrenMessages.Clear();
        }
示例#26
0
        public void Update(float deltaTime)
        {
            activeTransfers.RemoveAll(t => t.Connection.Status != NetworkConnectionStatus.Connected);

            var endedTransfers = activeTransfers.FindAll(t =>
                                                         t.Connection.Status != NetworkConnectionStatus.Connected ||
                                                         t.Status == FileTransferStatus.Finished ||
                                                         t.Status == FileTransferStatus.Canceled ||
                                                         t.Status == FileTransferStatus.Error);

            foreach (FileTransferOut transfer in endedTransfers)
            {
                activeTransfers.Remove(transfer);
                OnEnded(transfer);
            }

            foreach (FileTransferOut transfer in activeTransfers)
            {
                transfer.WaitTimer -= deltaTime;
                if (transfer.WaitTimer > 0.0f)
                {
                    continue;
                }

                transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime;

                // send another part of the file
                long remaining     = transfer.Data.Length - transfer.SentOffset;
                int  sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);

                IWriteMessage message;

                try
                {
                    //first message; send length, file name etc
                    //wait for acknowledgement before sending data
                    if (!transfer.Acknowledged)
                    {
                        message = new WriteOnlyMessage();
                        message.Write((byte)ServerPacketHeader.FILE_TRANSFER);

                        //if the recipient is the owner of the server (= a client running the server from the main exe)
                        //we don't need to send anything, the client can just read the file directly
                        if (transfer.Connection == GameMain.Server.OwnerConnection)
                        {
                            message.Write((byte)FileTransferMessageType.TransferOnSameMachine);
                            message.Write((byte)transfer.ID);
                            message.Write((byte)transfer.FileType);
                            message.Write(transfer.FilePath);
                            peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
                            transfer.Status = FileTransferStatus.Finished;
                        }
                        else
                        {
                            message.Write((byte)FileTransferMessageType.Initiate);
                            message.Write((byte)transfer.ID);
                            message.Write((byte)transfer.FileType);
                            //message.Write((ushort)chunkLen);
                            message.Write(transfer.Data.Length);
                            message.Write(transfer.FileName);
                            peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);

                            transfer.Status = FileTransferStatus.Sending;

                            if (GameSettings.VerboseLogging)
                            {
                                DebugConsole.Log("Sending file transfer initiation message: ");
                                DebugConsole.Log("  File: " + transfer.FileName);
                                DebugConsole.Log("  Size: " + transfer.Data.Length);
                                DebugConsole.Log("  ID: " + transfer.ID);
                            }
                        }
                        return;
                    }

                    message = new WriteOnlyMessage();
                    message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
                    message.Write((byte)FileTransferMessageType.Data);

                    message.Write((byte)transfer.ID);
                    message.Write(transfer.SentOffset);

                    byte[] sendBytes = new byte[sendByteCount];
                    Array.Copy(transfer.Data, transfer.SentOffset, sendBytes, 0, sendByteCount);

                    message.Write((ushort)sendByteCount);
                    message.Write(sendBytes, 0, sendByteCount);

                    transfer.SentOffset += sendByteCount;
                    if (transfer.SentOffset > transfer.KnownReceivedOffset + chunkLen * 5 ||
                        transfer.SentOffset >= transfer.Data.Length)
                    {
                        transfer.SentOffset = transfer.KnownReceivedOffset;
                    }

                    peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
                }

                catch (Exception e)
                {
                    DebugConsole.ThrowError("FileSender threw an exception when trying to send data", e);
                    GameAnalyticsManager.AddErrorEventOnce(
                        "FileSender.Update:Exception",
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace);
                    transfer.Status = FileTransferStatus.Error;
                    break;
                }

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.Log("Sending " + sendByteCount + " bytes of the file " + transfer.FileName + " (" + transfer.SentOffset + "/" + transfer.Data.Length + " sent)");
                }
            }
        }
        private void Send(FileTransferOut transfer)
        {
            // send another part of the file
            long remaining     = transfer.Data.Length - transfer.SentOffset;
            int  sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);

            IWriteMessage message;

            try
            {
                //first message; send length, file name etc
                //wait for acknowledgement before sending data
                if (!transfer.Acknowledged)
                {
                    message = new WriteOnlyMessage();
                    message.Write((byte)ServerPacketHeader.FILE_TRANSFER);

                    //if the recipient is the owner of the server (= a client running the server from the main exe)
                    //we don't need to send anything, the client can just read the file directly
                    if (transfer.Connection == GameMain.Server.OwnerConnection)
                    {
                        message.Write((byte)FileTransferMessageType.TransferOnSameMachine);
                        message.Write((byte)transfer.ID);
                        message.Write((byte)transfer.FileType);
                        message.Write(transfer.FilePath);
                        peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
                        transfer.Status = FileTransferStatus.Finished;
                    }
                    else
                    {
                        message.Write((byte)FileTransferMessageType.Initiate);
                        message.Write((byte)transfer.ID);
                        message.Write((byte)transfer.FileType);
                        //message.Write((ushort)chunkLen);
                        message.Write(transfer.Data.Length);
                        message.Write(transfer.FileName);
                        peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);

                        transfer.Status = FileTransferStatus.Sending;

                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.Log("Sending file transfer initiation message: ");
                            DebugConsole.Log("  File: " + transfer.FileName);
                            DebugConsole.Log("  Size: " + transfer.Data.Length);
                            DebugConsole.Log("  ID: " + transfer.ID);
                        }
                    }
                    transfer.WaitTimer = 0.1f;
                    return;
                }

                message = new WriteOnlyMessage();
                message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
                message.Write((byte)FileTransferMessageType.Data);

                message.Write((byte)transfer.ID);
                message.Write(transfer.SentOffset);

                byte[] sendBytes = new byte[sendByteCount];
                Array.Copy(transfer.Data, transfer.SentOffset, sendBytes, 0, sendByteCount);

                message.Write((ushort)sendByteCount);
                message.Write(sendBytes, 0, sendByteCount);

                transfer.SentOffset += sendByteCount;
                if (transfer.SentOffset > transfer.KnownReceivedOffset + chunkLen * 10 ||
                    transfer.SentOffset >= transfer.Data.Length)
                {
                    transfer.SentOffset = transfer.KnownReceivedOffset;
                    transfer.WaitTimer  = 0.5f;
                }

                peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
            }

            catch (Exception e)
            {
                DebugConsole.ThrowError("FileSender threw an exception when trying to send data", e);
                GameAnalyticsManager.AddErrorEventOnce(
                    "FileSender.Update:Exception",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    "FileSender threw an exception when trying to send data:\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
                transfer.Status = FileTransferStatus.Error;
                return;
            }

            if (GameSettings.VerboseLogging)
            {
                DebugConsole.Log($"Sending {sendByteCount} bytes of the file {transfer.FileName} ({transfer.SentOffset / 1000}/{transfer.Data.Length / 1000} kB sent)");
            }
        }
        /// <summary>
        /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
        /// </summary>
        protected void Write(NetOutgoingMessage msg, List <NetEntityEvent> eventsToSync, Client recipient = null)
        {
            //write into a temporary buffer so we can write the number of events before the actual data
            NetBuffer tempBuffer = new NetBuffer();

            int eventCount = 0;

            foreach (NetEntityEvent e in eventsToSync)
            {
                //write into a temporary buffer so we can write the length before the actual data
                NetBuffer tempEventBuffer = new NetBuffer();
                try
                {
                    WriteEvent(tempEventBuffer, e, recipient);
                }

                catch (Exception exception)
                {
                    DebugConsole.ThrowError("Failed to write an event for the entity \"" + e.Entity + "\"", exception);
                    GameAnalyticsManager.AddErrorEventOnce("NetEntityEventManager.Write:WriteFailed" + e.Entity.ToString(),
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to write an event for the entity \"" + e.Entity + "\"\n" + exception.StackTrace);

                    //write an empty event to avoid messing up IDs
                    //(otherwise the clients might read the next event in the message and think its ID
                    //is consecutive to the previous one, even though we skipped over this broken event)
                    tempBuffer.Write((UInt16)0);
                    tempBuffer.WritePadBits();
                    eventCount++;
                    continue;
                }

                if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > NetPeerConfiguration.kDefaultMTU - 20)
                {
                    //no more room in this packet
                    break;
                }

                if (tempEventBuffer.LengthBytes > 255)
                {
                    DebugConsole.ThrowError("Too much data in network event for entity \"" + e.Entity.ToString() + "\" (" + tempEventBuffer.LengthBytes + " bytes");
                    GameAnalyticsManager.AddErrorEventOnce("NetEntityEventManager.Write:TooLong" + e.Entity.ToString(),
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Too much data in network event for entity \"" + e.Entity.ToString() + "\" (" + tempEventBuffer.LengthBytes + " bytes");

                    //write an empty event breaking the event syncing
                    tempBuffer.Write((UInt16)0);
                    tempBuffer.WritePadBits();
                    eventCount++;
                    continue;
                }

                //the ID has been taken by another entity (the original entity has been removed) -> write an empty event
                else if (Entity.FindEntityByID(e.Entity.ID) != e.Entity || e.Entity.IdFreed)
                {
                    //technically the clients don't have any use for these, but removing events and shifting the IDs of all
                    //consecutive ones is so error-prone that I think this is a safer option
                    tempBuffer.Write((UInt16)0);
                    tempBuffer.WritePadBits();
                }
                else
                {
                    tempBuffer.Write((UInt16)e.Entity.ID);
                    tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
                    tempBuffer.Write(tempEventBuffer);
                    tempBuffer.WritePadBits();
                }

                eventCount++;
            }

            if (eventCount > 0)
            {
                msg.Write(eventsToSync[0].ID);
                msg.Write((byte)eventCount);
                msg.Write(tempBuffer);
            }
        }
        /// <summary>
        /// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
        /// </summary>
        public bool Read(ServerNetObject type, IReadMessage msg, float sendingTime, List <IServerSerializable> entities)
        {
            UInt16 unreceivedEntityEventCount = 0;

            if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
            {
                unreceivedEntityEventCount = msg.ReadUInt16();
                firstNewID = msg.ReadUInt16();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage(
                        "received midround syncing msg, unreceived: " + unreceivedEntityEventCount +
                        ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow);
                }
            }
            else
            {
                MidRoundSyncingDone = true;
                if (firstNewID != null)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1),
                                                Microsoft.Xna.Framework.Color.Yellow);
                    }
                    lastReceivedID = (UInt16)(firstNewID - 1);
                    firstNewID     = null;
                }
            }

            entities.Clear();

            UInt16 firstEventID = msg.ReadUInt16();
            int    eventCount   = msg.ReadByte();

            for (int i = 0; i < eventCount; i++)
            {
                //16 = entity ID, 8 = msg length
                if (msg.BitPosition + 16 + 8 > msg.LengthBits)
                {
                    string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
                    errorMsg += "\nPrevious entities:";
                    for (int j = entities.Count - 1; j >= 0; j--)
                    {
                        errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                    }
                    DebugConsole.ThrowError(errorMsg);
                    return(false);
                }

                UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
                UInt16 entityID    = msg.ReadUInt16();

                if (entityID == Entity.NullEntityID)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
                                                Microsoft.Xna.Framework.Color.Orange);
                    }
                    msg.ReadPadBits();
                    entities.Add(null);
                    if (thisEventID == (UInt16)(lastReceivedID + 1))
                    {
                        lastReceivedID++;
                    }
                    continue;
                }

                int msgLength = (int)msg.ReadVariableUInt32();

                IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
                entities.Add(entity);

                //skip the event if we've already received it or if the entity isn't found
                if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
                {
                    if (thisEventID != (UInt16)(lastReceivedID + 1))
                    {
                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.NewMessage(
                                "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
                                NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
                                    ? GUI.Style.Red
                                    : Microsoft.Xna.Framework.Color.Yellow);
                        }
                    }
                    else if (entity == null)
                    {
                        DebugConsole.NewMessage(
                            "Received msg " + thisEventID + ", entity " + entityID + " not found",
                            GUI.Style.Red);
                        GameMain.Client.ReportError(ClientNetError.MISSING_ENTITY, eventID: thisEventID, entityID: entityID);
                        return(false);
                    }

                    msg.BitPosition += msgLength * 8;
                    msg.ReadPadBits();
                }
                else
                {
                    long msgPosition = msg.BitPosition;
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
                                                Microsoft.Xna.Framework.Color.Green);
                    }
                    lastReceivedID++;
                    try
                    {
                        ReadEvent(msg, entity, sendingTime);
                        msg.ReadPadBits();

                        if (msg.BitPosition != msgPosition + msgLength * 8)
                        {
                            string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
                                              + "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
#if DEBUG
                            DebugConsole.ThrowError(errorMsg);
#endif
                            GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);

                            //TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
                            //msg.BitPosition = (int)(msgPosition + msgLength * 8);
                        }
                    }

                    catch (Exception e)
                    {
                        string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace.CleanupStackTrace();
                        errorMsg += "\nPrevious entities:";
                        for (int j = entities.Count - 2; j >= 0; j--)
                        {
                            errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                        }

                        DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);

                        GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                        msg.BitPosition = (int)(msgPosition + msgLength * 8);
                        msg.ReadPadBits();
                    }
                }
            }
            return(true);
        }
示例#30
0
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsUnconscious &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
                        GameServer.Log(errorMsg + "\n" + e.StackTrace, ServerLog.MessageType.Error);
                        DebugConsole.ThrowError(errorMsg, e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace);
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAll = inGameClients[0].LastRecvEntityEventID;
                if (server.OwnerConnection != null)
                {
                    var owner = clients.Find(c => c.Connection == server.OwnerConnection);
                    if (owner != null)
                    {
                        lastSentToAll = owner.LastRecvEntityEventID;
                    }
                }
                inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID))
                                             {
                                                 lastSentToAll = c.LastRecvEntityEventID;
                                             }
                                      });

                clients.Where(c => c.NeedsMidRoundSync).ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.FirstNewEventID))
                                                                       {
                                                                           lastSentToAll = (ushort)(c.FirstNewEventID - 1);
                                                                       }
                                                                });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null && (Timing.TotalTime - firstEventToResend.CreateTime) > 10.0f)
                {
                    //it's been 10 seconds since this event was created, kick everyone that hasn't received it yet, this is way too old
                    //  UNLESS the event was created when the client was still midround syncing, in which case we'll wait until the timeout
                    //  runs out before kicking the client
                    List <Client> toKick = inGameClients.FindAll(c =>
                                                                 NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID) &&
                                                                 (firstEventToResend.CreateTime > c.MidRoundSyncTimeOut || Timing.TotalTime > c.MidRoundSyncTimeOut));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected old event " + (c.LastRecvEntityEventID + 1).ToString() + ")", Color.Red);
                        GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
                                       + (c.LastRecvEntityEventID + 1).ToString() +
                                       " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
                                       " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
                        GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
                    });
                }
            }

            var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            foreach (Client timedOutClient in timedOutClients)
            {
                GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
                GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout");
            }

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }