Exemple #1
0
        public void SendCallback(IAsyncResult ar)
        {
            ClientState state = (ClientState)ar.AsyncState;
            Client      client;

            GameLog.DebugFormat($"EndSend: SocketConnected: {state.WorkSocket.Connected}, IAsyncResult: Completed: {ar.IsCompleted}, CompletedSynchronously: {ar.CompletedSynchronously}");

            try
            {
                SocketError errorCode;
                var         bytesSent = state.WorkSocket.EndSend(ar, out errorCode);
                if (!GlobalConnectionManifest.ConnectedClients.TryGetValue(state.Id, out client))
                {
                    GameLog.ErrorFormat("Send: socket should not exist: cid {0}", state.Id);
                    state.WorkSocket.Close();
                    state.WorkSocket.Dispose();
                    return;
                }

                if (bytesSent == 0 || errorCode != SocketError.Success)
                {
                    GameLog.ErrorFormat("cid {0}: disconnected");
                    client.Disconnect();
                    throw new SocketException((int)errorCode);
                }
            }
            catch (SocketException e)
            {
                Game.ReportException(e);
                GameLog.Error($"Error Code: {e.ErrorCode}, {e.Message}");
                state.WorkSocket.Close();
            }
            catch (ObjectDisposedException)
            {
                //client.Disconnect();
                GameLog.Error($"ObjectDisposedException");
                state.WorkSocket.Close();
            }
            state.SendComplete.Set();
        }
Exemple #2
0
 /// <summary>
 /// Confirm the exchange. Once both sides confirm, perform the exchange.
 /// </summary>
 /// <returns>Boolean indicating success.</returns>
 public void ConfirmExchange(User requestor)
 {
     if (_source == requestor)
     {
         GameLog.InfoFormat("Exchange: source ({0}) confirmed", _source.Name);
         _sourceConfirmed = true;
         _target.SendExchangeConfirmation(false);
     }
     if (_target == requestor)
     {
         GameLog.InfoFormat("Exchange: target ({0}) confirmed", _target.Name);
         _targetConfirmed = true;
         _source.SendExchangeConfirmation(false);
     }
     if (_sourceConfirmed && _targetConfirmed)
     {
         GameLog.Info("Exchange: Both sides confirmed");
         _source.SendExchangeConfirmation();
         _target.SendExchangeConfirmation();
         PerformExchange();
     }
 }
Exemple #3
0
 public bool AddItem(string itemName, ushort quantity = 1)
 {
     if (CurrentItemCount < ItemLimit)
     {
         if (Items.ContainsKey(itemName))
         {
             Items[itemName] += quantity;
             GameLog.Info($"{itemName} [{quantity}] added to existing item in vault {OwnerUuid}");
         }
         else
         {
             Items.Add(itemName, quantity);
             GameLog.Info($"{itemName} [{quantity}] added as new item in vault {OwnerUuid}");
         }
         return(true);
     }
     else
     {
         GameLog.Info($"Attempt to add {itemName} [{quantity}] to vault {OwnerUuid}, but user doesn't have it?");
         return(false);
     }
 }
Exemple #4
0
        /// <summary>
        /// Toggle a given door's state (open/closed) and send updates to users nearby.
        /// </summary>
        /// <param name="x">The X coordinate of the door.</param>
        /// <param name="y">The Y coordinate of the door.</param>
        /// <returns></returns>
        public void ToggleDoor(byte x, byte y)
        {
            var coords = new Tuple <byte, byte>(x, y);

            GameLog.DebugFormat("Door {0}@{1},{2}: Open: {3}, changing to {4}",
                                Name, x, y, Doors[coords].Closed,
                                !Doors[coords].Closed);

            Doors[coords].Closed = !Doors[coords].Closed;

            // There are several examples of doors in Temuair that trigger graphic
            // changes but do not trigger collision updates (e.g. 3-panel doors in
            // Piet & Undine).
            if (Doors[coords].UpdateCollision)
            {
                GameLog.DebugFormat("Door {0}@{1},{2}: updateCollision is set, collisions are now {3}",
                                    Name, x, y, !Doors[coords].Closed);
                IsWall[x, y] = !IsWall[x, y];
            }

            GameLog.DebugFormat("Toggling door at {0},{1}", x, y);
            GameLog.DebugFormat("Door is now in state: Open: {0} Collision: {1}", Doors[coords].Closed, IsWall[x, y]);

            var updateViewport = GetViewport(x, y);

            foreach (var obj in EntityTree.GetObjects(updateViewport))
            {
                if (obj is User)
                {
                    var user = obj as User;
                    GameLog.DebugFormat("Sending door packet to {0}: X {1}, Y {2}, Open {3}, LR {4}",
                                        user.Name, x, y, Doors[coords].Closed,
                                        Doors[coords].IsLeftRight);

                    user.SendDoorUpdate(x, y, Doors[coords].Closed,
                                        Doors[coords].IsLeftRight);
                }
            }
        }
Exemple #5
0
 public static void DeregisterClient(Client client)
 {
     ((IDictionary)ConnectedClients).Remove(client.ConnectionId);
     GameLog.InfoFormat("Deregistering {0}", client.ConnectionId);
     // Send a control message to clean up after World users; Lobby and Login handle themselves
     if (client.ServerType == ServerTypes.World)
     {
         ((IDictionary)WorldClients).Remove(client.ConnectionId);
         // This will also handle removing the user from WorldClients if necessary
         try
         {
             World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.CleanupUser, client.ConnectionId));
         }
         catch (InvalidOperationException)
         {
             if (!World.ControlMessageQueue.IsCompleted)
             {
                 GameLog.ErrorFormat("Connection {id}: DeregisterClient failed", client.ConnectionId);
             }
         }
     }
 }
Exemple #6
0
        public byte[] GetBytes()
        {
            var buffer = Encoding.ASCII.GetBytes(Name);

            GameLog.DebugFormat("buffer is {0} and Name is {1}", BitConverter.ToString(buffer), Name);

            // X quadrant, offset, Y quadrant, offset, length of the name, the name, plus a 64-bit(?!) ID
            var bytes = new List <Byte>();

            GameLog.DebugFormat("{0}, {1}, {2}, {3}, {4}, mappoint ID is {5}", XQuadrant, XOffset, YQuadrant,
                                YOffset, Name.Length, Id);

            bytes.Add((byte)XQuadrant);
            bytes.Add((byte)XOffset);
            bytes.Add((byte)YQuadrant);
            bytes.Add((byte)YOffset);
            bytes.Add((byte)Name.Length);
            bytes.AddRange(buffer);
            bytes.AddRange(BitConverter.GetBytes(Id));

            return(bytes.ToArray());
        }
Exemple #7
0
        /// <summary>
        /// Determine whether either heartbeat has "expired" (meaning REAP_HEARTBEAT_INTERVAL has
        /// passed since we received a heartbeat response).
        /// </summary>
        /// <returns>True or false, indicating expiration.</returns>
        public bool IsHeartbeatExpired()
        {
            // If we have no record of sending a heartbeat, obviously it hasn't expired
            if (_tickHeartbeatSent == 0 && _byteHeartbeatSent == 0)
            {
                return(false);
            }

            var tickSpan = new TimeSpan(_tickHeartbeatReceived - _tickHeartbeatSent);
            var byteSpan = new TimeSpan(_byteHeartbeatReceived - _byteHeartbeatSent);

            GameLog.DebugFormat("cid {0}: tick heartbeat elapsed seconds {1}, byte heartbeat elapsed seconds {2}",
                                ConnectionId, tickSpan.TotalSeconds, byteSpan.TotalSeconds);

            if (tickSpan.TotalSeconds > Constants.REAP_HEARTBEAT_INTERVAL ||
                byteSpan.TotalSeconds > Constants.REAP_HEARTBEAT_INTERVAL)
            {
                // DON'T FEAR THE REAPER
                GameLog.InfoFormat("cid {0}: heartbeat expired", ConnectionId);
                return(true);
            }
            return(false);
        }
Exemple #8
0
        public void Start()
        {
            var x = 0;

            while (true)
            {
                // Ignore processing if no one is logged in, what's the point

                try
                {
                    foreach (var map in _maps)
                    {
                        if (map.Users.Count == 0)
                        {
                            continue;
                        }

                        foreach (var mob in map.Objects.Where(x => x is Monster).ToList())
                        {
                            Evaluate(mob as Monster, map);
                        }
                    }
                }
                catch (Exception e)
                {
                    GameLog.Fatal("Monolith thread error: {e}", e);
                }
                Thread.Sleep(1000);
                x++;
                // Refresh our list every 15 seconds in case of XML reloading
                if (x == 30)
                {
                    _maps = Game.World.WorldData.Values <Map>().ToList();
                    x     = 0;
                }
            }
        }
Exemple #9
0
        public byte[] GetBytes()
        {
            // Returns the representation of the worldmap as an array of bytes,
            // suitable to passing to a map packet.

            var buffer = Encoding.ASCII.GetBytes(ClientMap);
            var bytes  = new List <Byte> {
                (byte)ClientMap.Length
            };

            bytes.AddRange(buffer);
            bytes.Add((byte)Points.Count);
            bytes.Add(0x00);

            foreach (var mappoint in Points)
            {
                bytes.AddRange(mappoint.GetBytes());
            }

            GameLog.DebugFormat("I am sending the following map packet:");
            GameLog.DebugFormat("{0}", BitConverter.ToString(bytes.ToArray()));

            return(bytes.ToArray());
        }
Exemple #10
0
        public static byte[] RequestEncryptionKey(string endpoint, IPAddress remoteAddress)
        {
            byte[] key;

            try
            {
                var seed = new Seed()
                {
                    Ip = remoteAddress.ToString()
                };

                var webReq = WebRequest.Create(new Uri(endpoint));
                webReq.ContentType = "application/json";
                webReq.Method      = "POST";

                var json = JsonSerializer.Serialize(seed);

                using (var sw = new StreamWriter(webReq.GetRequestStream()))
                {
                    sw.Write(json);
                }

                var response = webReq.GetResponse();
                using (var sr = new StreamReader(response.GetResponseStream()))
                {
                    key = (byte[])JsonSerializer.Deserialize(sr.ReadToEnd(), typeof(byte[]));
                }
            }
            catch (Exception e)
            {
                Game.ReportException(e);
                GameLog.Error("RequestEncryptionKey failure: {e}", e);
                key = Encoding.ASCII.GetBytes("NOTVALID!");
            }
            return(key);
        }
Exemple #11
0
 public void RemoveCreature(Creature toRemove)
 {
     World.Remove(toRemove);
     Remove(toRemove);
     GameLog.DebugFormat("Removing creature {0} (id {1})", toRemove.Name, toRemove.Id);
 }
Exemple #12
0
 public void InsertCreature(Creature toInsert)
 {
     World.Insert(toInsert);
     Insert(toInsert, toInsert.X, toInsert.Y);
     GameLog.DebugFormat("Monster {0} with id {1} spawned.", toInsert.Name, toInsert.Id);
 }
Exemple #13
0
        //public Dictionary<string, Xml.Spawn> Spawns { get; set; }

        /// <summary>
        /// Create a new Hybrasyl map from an XMLMap object.
        /// </summary>
        /// <param name="newMap">An XSD.Map object representing the XML map file.</param>
        /// <param name="theWorld">A world object where the map will be placed</param>
        public Map(Xml.Map newMap, World theWorld)
        {
            Init();
            World      = theWorld;
            SpawnDebug = false;
            //  Spawns = new List<Xml.Spawn>();

            // TODO: refactor Map class to not do this, but be a partial which overlays
            // TODO: XSD.Map
            Id           = newMap.Id;
            X            = newMap.X;
            Y            = newMap.Y;
            Name         = newMap.Name;
            AllowCasting = newMap.AllowCasting;
            EntityTree   = new QuadTree <VisibleObject>(0, 0, X, Y);
            Music        = newMap.Music;

            foreach (var warpElement in newMap.Warps)
            {
                var warp = new Warp(this)
                {
                    X = warpElement.X,
                    Y = warpElement.Y
                };

                if (warpElement.MapTarget != null)
                {
                    // map warp
                    warp.DestinationMapName = warpElement.MapTarget.Value;
                    warp.WarpType           = WarpType.Map;
                    warp.DestinationX       = warpElement.MapTarget.X;
                    warp.DestinationY       = warpElement.MapTarget.Y;
                }
                else if (warpElement.WorldMapTarget != string.Empty)
                {
                    // worldmap warp
                    warp.DestinationMapName = warpElement.WorldMapTarget;
                    warp.WarpType           = WarpType.WorldMap;
                }
                if (warpElement.Restrictions?.Level != null)
                {
                    warp.MinimumLevel = warpElement.Restrictions.Level.Min;
                    warp.MaximumLevel = warpElement.Restrictions.Level.Max;
                }
                if (warpElement.Restrictions?.Ab != null)
                {
                    warp.MinimumAbility = warpElement.Restrictions.Ab.Min;
                    warp.MaximumAbility = warpElement.Restrictions.Ab.Max;
                }
                warp.MobUse = warpElement.Restrictions?.NoMobUse ?? true;
                Warps[new Tuple <byte, byte>(warp.X, warp.Y)] = warp;
            }

            foreach (var npcElement in newMap.Npcs)
            {
                var npcTemplate = World.WorldData.Get <Xml.Npc>(npcElement.Name);
                if (npcTemplate == null)
                {
                    GameLog.Error("map ${Name}: NPC ${npcElement.Name} is missing, will not be loaded");
                    continue;
                }
                var merchant = new Merchant
                {
                    X         = npcElement.X,
                    Y         = npcElement.Y,
                    Name      = npcElement.Name,
                    Sprite    = npcTemplate.Appearance.Sprite,
                    Direction = npcElement.Direction,
                    Portrait  = npcTemplate.Appearance.Portrait,
                    AllowDead = npcTemplate.AllowDead
                };
                if (npcTemplate.Roles != null)
                {
                    if (npcTemplate.Roles.Post != null)
                    {
                        merchant.Jobs ^= MerchantJob.Post;
                    }
                    if (npcTemplate.Roles.Bank != null)
                    {
                        merchant.Jobs ^= MerchantJob.Bank;
                    }
                    if (npcTemplate.Roles.Repair != null)
                    {
                        merchant.Jobs ^= MerchantJob.Repair;
                    }
                    if (npcTemplate.Roles.Train != null)
                    {
                        if (npcTemplate.Roles.Train.Any(x => x.Type == "Skill"))
                        {
                            merchant.Jobs ^= MerchantJob.Skills;
                        }
                        if (npcTemplate.Roles.Train.Any(x => x.Type == "Spell"))
                        {
                            merchant.Jobs ^= MerchantJob.Spells;
                        }
                    }
                    if (npcTemplate.Roles.Vend != null)
                    {
                        merchant.Jobs ^= MerchantJob.Vend;
                    }

                    merchant.Roles = npcTemplate.Roles;
                }
                InsertNpc(merchant);
                // Keep the actual spawned object around in the index for later use
                World.WorldData.Set(merchant.Name, merchant);
            }

            foreach (var reactorElement in newMap.Reactors)
            {
                var reactor = new Reactor(reactorElement.X, reactorElement.Y, this,
                                          reactorElement.Script, reactorElement.Description, reactorElement.Blocking);
                reactor.AllowDead = reactorElement.AllowDead;
                InsertReactor(reactor);
                GameLog.Debug($"{reactor.Id} placed in {reactor.Map.Name}, description was {reactor.Description}");
            }
            foreach (var sign in newMap.Signs)
            {
                Signpost post;
                if (sign.Type == Xml.BoardType.Sign)
                {
                    post = new Signpost(sign.X, sign.Y, sign.Message);
                }
                else
                {
                    post = new Signpost(sign.X, sign.Y, sign.Message, true, sign.BoardKey);
                }
                InsertSignpost(post);
            }
            Load();
        }
Exemple #14
0
        public void ReadCallback(IAsyncResult ar)
        {
            ClientState  state = (ClientState)ar.AsyncState;
            Client       client;
            SocketError  errorCode = SocketError.SocketError;
            int          bytesRead = 0;
            ClientPacket receivedPacket;

            GameLog.Debug($"SocketConnected: {state.WorkSocket.Connected}, IAsyncResult: Completed: {ar.IsCompleted}, CompletedSynchronously: {ar.CompletedSynchronously}, queue size: {state.Buffer.Length}");
            GameLog.Debug("Running read callback");

            if (!GlobalConnectionManifest.ConnectedClients.TryGetValue(state.Id, out client))
            {
                // Is this a redirect?
                Redirect redirect;
                if (!GlobalConnectionManifest.TryGetRedirect(state.Id, out redirect))
                {
                    GameLog.ErrorFormat("Receive: data from unknown client (id {0}, closing connection", state.Id);
                    state.WorkSocket.Close();
                    state.WorkSocket.Dispose();
                    return;
                }
                client             = redirect.Client;
                client.ClientState = state;
                if (client.EncryptionKey == null)
                {
                    client.EncryptionKey = redirect.EncryptionKey;
                }
                GlobalConnectionManifest.RegisterClient(client);
            }

            try
            {
                bytesRead = state.WorkSocket.EndReceive(ar, out errorCode);
                if (bytesRead == 0 || errorCode != SocketError.Success)
                {
                    GameLog.Error($"bytesRead: {bytesRead}, errorCode: {errorCode}");
                    client.Disconnect();
                }
                client.ClientState.BytesReceived += bytesRead;
            }
            catch (Exception e)
            {
                GameLog.Fatal($"EndReceive Error:  {e.Message}");
                Game.ReportException(e);
                client.Disconnect();
            }

            try
            {
                // TODO: improve / refactor
                while (client.ClientState.TryGetPacket(out receivedPacket))
                {
                    client.Enqueue(receivedPacket);
                }
            }
            catch (Exception e)
            {
                Game.ReportException(e);
                GameLog.Error("ReadCallback error: {e}", e);
            }
            ContinueReceiving(state, client);
        }
Exemple #15
0
 public virtual void Shutdown()
 {
     GameLog.WarningFormat("{ServerType}: shutting down", this.GetType().ToString());
     Listener?.Close();
     GameLog.WarningFormat("{ServerType}: shutdown complete", this.GetType().ToString());
 }
Exemple #16
0
        public void FlushSendBuffer()
        {
            MemoryStream buffer        = new MemoryStream();
            int          transmitDelay = 0;

            try
            {
                while (!ClientState.SendBufferEmpty)
                {
                    if (ClientState.SendBufferPeek(out ServerPacket precheck))
                    {
                        if (buffer.Length > 0 && (precheck.TransmitDelay > 0 && transmitDelay == 0))
                        {
                            // If we're dealing with a bunch of packets with delays, batch them together.
                            // Otherwise, send them individually.
                            //GameLog.Warning("TransmitDelay occurring");
                            break;
                        }
                        // Limit outbound transmissions to 65k bytes at a time
                        if (buffer.Length >= 65535)
                        {
                            //GameLog.Warning("Breaking up into chunks");
                            break;
                        }
                    }

                    if (ClientState.SendBufferTake(out ServerPacket packet))
                    {
                        // If no packets, just call the whole thing off
                        if (packet == null)
                        {
                            return;
                        }

                        if (packet.ShouldEncrypt)
                        {
                            ++ServerOrdinal;
                            packet.Ordinal = ServerOrdinal;
                            packet.GenerateFooter();
                            packet.Encrypt(this);
                        }
                        if (packet.TransmitDelay > 0)
                        {
                            transmitDelay = packet.TransmitDelay;
                        }
                        // Write packet to our memory stream
                        buffer.Write(packet.ToArray());
                    }
                }

                if (buffer.Length == 0)
                {
                    return;
                }

                // Background enqueue a send with our memory stream
                Task.Run(async() =>
                {
                    var socketbuf = buffer.ToArray();
                    try
                    {
                        //GameLog.Info($"transmit: {socketbuf.Length} with delay {transmitDelay}");
                        if (transmitDelay > 0)
                        {
                            await Task.Delay(transmitDelay);
                        }
                        Socket.BeginSend(socketbuf, 0, socketbuf.Length, 0, SendCallback, ClientState);
                    }
                    catch (ObjectDisposedException)
                    {
                        ClientState.Dispose();
                    }
                });
            }
            catch (ObjectDisposedException e)
            {
                // Socket is gone, peace out
                ClientState.Dispose();
            }
            catch (Exception e)
            {
                Game.ReportException(e);
                GameLog.Error($"HALP: {e}");
            }
        }
Exemple #17
0
 public void OnDisconnectThreshold(IClientTrigger trigger)
 {
     trigger.Client.SendMessage("You have been automatically disconnected due to server abuse. Goodbye!", MessageTypes.SYSTEM_WITH_OVERHEAD);
     GameLog.Warning($"Client {trigger.Id}: disconnected due to packet spam");
     trigger.Client.Disconnect();
 }
Exemple #18
0
        private void PacketHandler_0x03_Login(Client client, ClientPacket packet)
        {
            var name     = packet.ReadString8();
            var password = packet.ReadString8();

            GameLog.DebugFormat("cid {0}: Login request for {1}", client.ConnectionId, name);

            if (Game.World.WorldData.TryGetAuthInfo(name, out AuthInfo login))
            {
                if (login.VerifyPassword(password))
                {
                    GameLog.DebugFormat("cid {0}: password verified for {1}", client.ConnectionId, name);

                    if (login.CurrentState == UserState.Redirect && login.StateChangeDuration < 1000)
                    {
                        client.LoginMessage("That character is already logging in. Please try again.", 3);
                        return;
                    }

                    if (login.IsLoggedIn)
                    {
                        GameLog.InfoFormat("cid {0}: {1} logging on again, disconnecting previous connection",
                                           client.ConnectionId, name);
                        client.LoginMessage("That character is already online. Please try again.", 3);
                        World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.LogoffUser, name));
                        return;
                    }
                    // Make sure user can be deserialized without errors
                    if (!Game.World.WorldData.TryGetUser(name, out _))
                    {
                        // Something bad has happened
                        client.LoginMessage("An unknown error occurred. Please contact Hybrasyl support.", 3);
                        return;
                    }

                    GameLog.DebugFormat("cid {0} ({1}): logging in", client.ConnectionId, name);
                    client.LoginMessage("\0", 0);
                    client.SendMessage("Welcome to Hybrasyl!", 3);
                    GameLog.DebugFormat("cid {0} ({1}): sending redirect to world", client.ConnectionId, name);

                    var redirect = new Redirect(client, this, Game.World, name, client.EncryptionSeed,
                                                client.EncryptionKey);
                    GameLog.InfoFormat("cid {0} ({1}): login successful, redirecting to world server",
                                       client.ConnectionId, name);
                    login.LastLogin       = DateTime.Now;
                    login.LastLoginFrom   = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString();
                    login.CurrentState    = UserState.Redirect;
                    login.LastStateChange = login.LastLogin;
                    login.Save();
                    client.Redirect(redirect);
                }
                else
                {
                    GameLog.WarningFormat("cid {0} ({1}): password incorrect", client.ConnectionId, name);
                    client.LoginMessage("Incorrect password", 3);
                    login.LastLoginFailure = DateTime.Now;
                    login.LoginFailureCount++;
                    login.LastLoginFailureFrom = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString();
                    login.CurrentState         = UserState.Login;
                    login.LastStateChange      = login.LastLoginFailure;
                    login.Save();
                }
            }
            else
            {
                client.LoginMessage("That character does not exist", 3);
                GameLog.InfoFormat("cid {0}: attempt to login as nonexistent character {1}", client.ConnectionId, name);
                return;
            }
        }
Exemple #19
0
        public void Spawn(Xml.SpawnGroup spawnGroup)
        {
            foreach (var map in spawnGroup.Maps)
            {
                if (map.Disabled)
                {
                    continue;
                }
                try
                {
                    var spawnMap = Game.World.WorldData.Get <Map>(map.Id);
                    GameLog.SpawnDebug("Spawn: calculating {0}", spawnMap.Name);
                    var monsterList  = spawnMap.Objects.OfType <Monster>().ToList();
                    var monsterCount = monsterList.Count;

                    // If there is no limit specified, we want a reasonable limit, which we consider to be 1/10th of total
                    // number of map tiles

                    var spawnLimit = map.Limit == 0 ? (spawnMap.X * spawnMap.Y) / 10 : map.Limit;

                    if (monsterCount > spawnLimit)
                    {
                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, mob count is {monsterCount}, limit is {spawnLimit}");
                        }
                        continue;
                    }

                    var since = DateTime.Now - map.LastSpawn;
                    if (since.TotalSeconds < map.Interval)
                    {
                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, last spawn was {since.TotalSeconds} ago, interval {map.Interval}");
                        }
                        continue;
                    }

                    map.LastSpawn = DateTime.Now;

                    var thisSpawn = _random.Next(map.MinSpawn, map.MaxSpawn + 1);

                    GameLog.SpawnInfo($"Spawn: {map.Name}: spawning {thisSpawn} mobs ");

                    for (var i = 0; i < thisSpawn; i++)
                    {
                        var spawn = spawnGroup.Spawns.PickRandom(true);

                        if (spawn == null)
                        {
                            GameLog.SpawnError("Spawngroup empty, skipping");
                            break;
                        }

                        var creature = _creatures.FirstOrDefault(x => x.Name == spawn.Base);

                        if (creature is default(Xml.Creature))
                        {
                            GameLog.SpawnError($"Base monster {spawn.Base} not found");
                            break;
                        }

                        var newSpawnLoot = LootBox.CalculateLoot(spawn);

                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo("Spawn {name}, map {map}: {Xp} xp, {Gold} gold, items {Items}", spawn.Base, map.Name, newSpawnLoot.Xp, newSpawnLoot.Gold,
                                              string.Join(',', newSpawnLoot.Items));
                        }

                        var baseMob = new Monster(creature, spawn, map.Id, newSpawnLoot);
                        var mob     = (Monster)baseMob.Clone();
                        var xcoord  = 0;
                        var ycoord  = 0;

                        if (map.Coordinates.Count > 0)
                        {
                            // TODO: optimize / improve
                            foreach (var coord in map.Coordinates)
                            {
                                if (spawnMap.EntityTree.GetObjects(new System.Drawing.Rectangle(coord.X, coord.Y, 1, 1)).Where(e => e is Creature).Count() == 0)
                                {
                                    xcoord = coord.X;
                                    ycoord = coord.Y;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            do
                            {
                                xcoord = _random.Next(0, spawnMap.X);
                                ycoord = _random.Next(0, spawnMap.Y);
                            } while (spawnMap.IsWall[xcoord, ycoord]);
                        }
                        mob.X = (byte)xcoord;
                        mob.Y = (byte)ycoord;
                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: spawning {mob.Name} on {spawnMap.Name}");
                        }
                        SpawnMonster(mob, spawnMap);
                    }
                }
                catch (Exception e)
                {
                    Game.ReportException(e);
                    GameLog.SpawnError(e, "Spawngroup {Filename}: disabled map {Name} due to error", spawnGroup.Filename, map.Name);
                    map.Disabled = true;
                    continue;
                }
            }
        }
Exemple #20
0
        /// <summary>
        /// Calculate loot for a given spawn.
        /// </summary>
        /// <param name="spawn">The spawn we will use to calculate Loot.</param>
        /// <returns>A Loot struct with XP, gold and items, if any</returns>
        public static Loot CalculateLoot(Xml.Spawn spawn)
        {
            // Loot calculations are not particularly complex but have a lot of components:
            // Spawns can have loot sets, loot tables, or both.
            // We resolve sets first, then tables. We keep a running tab of gold drops.
            // Lastly, we return a Loot struct with our calculations.

            var loot   = new Loot(0, 0);
            var tables = new List <Xml.LootTable>();

            // Assign base XP
            loot.Xp = spawn.Loot.Xp;
            // Sets
            foreach (var set in spawn.Loot?.Set ?? new List <Xml.LootImport>())
            {
                // Is the set present?
                GameLog.SpawnInfo("Processing loot set {Name}", set.Name);
                if (Game.World.WorldData.TryGetValueByIndex(set.Name, out Xml.LootSet lootset))
                {
                    // Set is present, does it fire?
                    // Chance is implemented as a decimalized percentage, e.g. 0.08 = 8% chance

                    // If rolls == 0, all tables fire
                    if (set.Rolls == 0)
                    {
                        tables.AddRange(lootset.Table);
                        GameLog.SpawnInfo("Processing loot set {Name}: set rolls == 0, looting", set.Name);
                        continue;
                    }

                    for (var x = 0; x < set.Rolls; x++)
                    {
                        if (Roll() <= set.Chance)
                        {
                            GameLog.SpawnInfo("Processing loot set {Name}: set hit, looting", set.Name);

                            // Ok, the set fired. Check the subtables, which can have independent chances.
                            // If no chance is present, we simply award something from each table in the set.
                            // Note that we do table processing last! We just find the tables that fire here.
                            foreach (var setTable in lootset.Table)
                            {
                                // Rolls == 0 (default) means the table is automatically used.
                                if (setTable.Rolls == 0)
                                {
                                    tables.Add(setTable);
                                    GameLog.SpawnInfo("Processing loot set {Name}: setTable rolls == 0, looting", set.Name);
                                    continue;
                                }
                                // Did the subtable hit?
                                for (var y = 0; y < setTable.Rolls; y++)
                                {
                                    if (Roll() <= setTable.Chance)
                                    {
                                        tables.Add(setTable);
                                        GameLog.SpawnInfo("Processing loot set {Name}: set subtable hit, looting ", set.Name);
                                    }
                                }
                            }
                        }
                        else
                        {
                            GameLog.SpawnInfo("Processing loot set {Name}: Set subtable missed", set.Name);
                        }
                    }
                }
                else
                {
                    GameLog.Warning("Spawn {name}: Loot set {name} missing", spawn.Base, set.Name);
                }
            }

            // Now, calculate loot for any tables attached to the spawn
            foreach (var table in spawn.Loot?.Table ?? new List <Xml.LootTable>())
            {
                if (table.Rolls == 0)
                {
                    tables.Add(table);
                    GameLog.SpawnInfo("Processing loot: spawn table for {Name}, rolls == 0, looting", spawn.Base);
                    continue;
                }
                for (var z = 0; z <= table.Rolls; z++)
                {
                    if (Roll() <= table.Chance)
                    {
                        GameLog.SpawnInfo("Processing loot: spawn table for {Name} hit, looting", spawn.Base);
                        tables.Add(table);
                    }
                    else
                    {
                        GameLog.SpawnInfo("Processing loot set {Name}: Spawn subtable missed", spawn.Base);
                    }
                }
            }

            // Now that we have all tables that fired, we need to calculate actual loot

            GameLog.SpawnInfo("Loot for {Name}: tables: {Count}", spawn.Base, tables.Count());
            foreach (var table in tables)
            {
                loot += CalculateTable(table);
            }

            GameLog.SpawnInfo("Final loot for {Name}: {Xp} xp, {Gold} gold, items [{items}]", spawn.Base, loot.Xp, loot.Gold, string.Join(",", loot.Items));
            return(loot);
        }
Exemple #21
0
 public void DumpPacket()
 {
     // Dump the packet to the console.
     GameLog.DebugFormat("Dumping packet:");
     GameLog.DebugFormat(BitConverter.ToString(Data));
 }
Exemple #22
0
        public void FlushReceiveBuffer()
        {
            lock (ClientState.ReceiveLock)
            {
                try
                {
                    ClientPacket packet;
                    while (ClientState.ReceiveBufferTake(out packet))
                    {
                        if (packet.ShouldEncrypt)
                        {
                            packet.Decrypt(this);
                        }

                        if (packet.Opcode == 0x39 || packet.Opcode == 0x3A)
                        {
                            packet.DecryptDialog();
                        }
                        try
                        {
                            if (Server is Lobby)
                            {
                                GameLog.DebugFormat("Lobby: 0x{0:X2}", packet.Opcode);
                                var handler = (Server as Lobby).PacketHandlers[packet.Opcode];
                                handler.Invoke(this, packet);
                                GameLog.DebugFormat("Lobby packet done");
                                UpdateLastReceived();
                            }
                            else if (Server is Login)
                            {
                                GameLog.Debug($"Login: 0x{packet.Opcode:X2}");
                                var handler = (Server as Login).PacketHandlers[packet.Opcode];
                                handler.Invoke(this, packet);
                                GameLog.DebugFormat("Login packet done");
                                UpdateLastReceived();
                            }
                            else
                            {
                                UpdateLastReceived(packet.Opcode != 0x45 &&
                                                   packet.Opcode != 0x75);
                                GameLog.Debug($"Queuing: 0x{packet.Opcode:X2}");
                                //packet.DumpPacket();
                                // Check for throttling
                                var throttleResult = Server.PacketThrottleCheck(this, packet);
                                if (throttleResult == ThrottleResult.OK || throttleResult == ThrottleResult.ThrottleEnd || throttleResult == ThrottleResult.SquelchEnd)
                                {
                                    World.MessageQueue.Add(new HybrasylClientMessage(packet, ConnectionId));
                                }
                                else
                                if (packet.Opcode == 0x06)
                                {
                                    World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.TriggerRefresh, ConnectionId));
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Game.ReportException(e);
                            GameLog.ErrorFormat("EXCEPTION IN HANDLING: 0x{0:X2}: {1}", packet.Opcode, e);
                        }
                    }
                }
                catch (Exception e)
                {
                    Game.ReportException(e);
                    Console.WriteLine(e);
                    throw;
                }
            }
        }
Exemple #23
0
 public void DumpPacket()
 {
     // Dump the packet to the console.
     GameLog.Debug($"Dumping packet: {Opcode:X2}");
     GameLog.Debug(ToString());
 }
Exemple #24
0
 public void OnThrottleStop(IThrottleTrigger trigger)
 {
     GameLog.Debug($"Client {trigger.Id}: throttle expired");
 }
Exemple #25
0
        public bool AddItem(User giver, byte slot, byte quantity = 1)
        {
            ItemObject toAdd;

            // Some sanity checks

            // Check if our "exchange" is full
            if (_sourceItems.IsFull || _targetItems.IsFull)
            {
                _source.SendMessage("Maximum exchange size reached. No more items can be added.", MessageTypes.SYSTEM);
                _target.SendMessage("Maximum exchange size reached. No more items can be added.", MessageTypes.SYSTEM);
                return(false);
            }
            // Check if either participant's inventory would be full as a result of confirmation
            if (_sourceItems.Count == _sourceSize || _targetItems.Count == _targetSize)
            {
                _source.SendMessage("Inventory full.", MessageTypes.SYSTEM);
                _target.SendMessage("Inventory full.", MessageTypes.SYSTEM);
                return(false);
            }

            // OK - we have room, now what?
            var theItem = giver.Inventory[slot];

            // Further checks!
            // Is the ItemObject exchangeable?

            if (!theItem.Exchangeable)
            {
                giver.SendMessage("You can't trade this.", MessageTypes.SYSTEM);
                return(false);
            }

            // Weight check

            if (giver == _source && _targetWeight + theItem.Weight > _target.MaximumWeight)
            {
                _source.SendSystemMessage("It's too heavy.");
                _target.SendSystemMessage("They can't carry any more.");
                return(false);
            }

            if (giver == _target && _sourceWeight + theItem.Weight > _source.MaximumWeight)
            {
                _target.SendSystemMessage("It's too heavy.");
                _source.SendSystemMessage("They can't carry any more.");
                return(false);
            }

            // Is the ItemObject stackable?

            if (theItem.Stackable && theItem.Count > 1)
            {
                var targetItem = giver == _target?_source.Inventory.Find(theItem.Name) : _target.Inventory.Find(theItem.Name);

                // Check to see that giver has sufficient number of whatever, and also that the quantity is a positive number
                if (quantity <= 0)
                {
                    giver.SendSystemMessage("You can't give zero of something, chief.");
                    return(false);
                }

                if (quantity > theItem.Count)
                {
                    giver.SendSystemMessage($"You don't have that many {theItem.Name} to give!");
                    return(false);
                }

                // Check if the recipient already has this ItemObject - if they do, ensure the quantity proposed for trade
                // wouldn't put them over maxcount for the ItemObject in question

                if (targetItem != null && targetItem.Count + quantity > theItem.MaximumStack)
                {
                    if (giver == _target)
                    {
                        _target.SendSystemMessage($"They can't carry any more {theItem.Name}");
                        _source.SendSystemMessage($"You can't carry any more {theItem.Name}.");
                    }
                    else
                    {
                        _source.SendSystemMessage($"They can't carry any more {theItem.Name}");
                        _target.SendSystemMessage($"You can't carry any more {theItem.Name}.");
                    }
                    return(false);
                }
                giver.RemoveItem(theItem.Name, quantity);
                toAdd       = new ItemObject(theItem);
                toAdd.Count = quantity;
            }
            else if (!theItem.Stackable || theItem.Count == 1)
            {
                // ItemObject isn't stackable or is a stack of one
                // Remove the ItemObject entirely from giver
                toAdd = theItem;
                giver.RemoveItem(slot);
            }
            else
            {
                GameLog.WarningFormat("exchange: Hijinx occuring: participants are {0} and {1}",
                                      _source.Name, _target.Name);
                _active = false;
                return(false);
            }

            // Now add the ItemObject to the active exchange and make sure we update weight
            if (giver == _source)
            {
                var exchangeSlot = (byte)_sourceItems.Count;
                _sourceItems.AddItem(toAdd);
                _source.SendExchangeUpdate(toAdd, exchangeSlot);
                _target.SendExchangeUpdate(toAdd, exchangeSlot, false);
                _targetWeight += toAdd.Weight;
            }
            if (giver == _target)
            {
                var exchangeSlot = (byte)_targetItems.Count;
                _targetItems.AddItem(toAdd);
                _target.SendExchangeUpdate(toAdd, exchangeSlot);
                _source.SendExchangeUpdate(toAdd, exchangeSlot, false);
                _sourceWeight += toAdd.Weight;
            }

            return(true);
        }
Exemple #26
0
        /// <summary>
        /// Given a list of items in a loot table, return the items awarded.
        /// </summary>
        /// <param name="list">LootTableItemList containing items</param>
        /// <returns>List of items</returns>
        public static List <string> CalculateItems(Xml.LootTableItemList list)
        {
            // Ordinarily, return one item from the list.
            var rolls    = CalculateSuccessfulRolls(list.Rolls, list.Chance);
            var loot     = new List <Xml.LootItem>();
            var itemList = new List <ItemObject>();

            // First, process any "always" items, which always drop when the container fires
            foreach (var item in list.Item.Where(i => i.Always))
            {
                GameLog.SpawnInfo("Processing loot: added always item {item}", item.Value);
                loot.Add(item);
            }
            var totalRolls = 0;

            // Process the rest of the rolls now
            do
            {
                // Get a random item from the list
                var item = list.Item.Where(i => !i.Always).PickRandom();
                // As soon as we get an item from our table, we've "rolled"; we'll add another roll below if needed
                rolls--;

                // Check uniqueness. If something has already dropped, don't drop it again, and reroll
                if (item.Unique && loot.Contains(item))
                {
                    rolls++;
                    GameLog.SpawnInfo("Processing loot: added duplicate unique item {item}. Rerolling", item.Value);
                    continue;
                }

                // Check max quantity. If it is exceeded, reroll
                if (item.Max > 0 && loot.Where(i => i.Value == item.Value).Count() >= item.Max)
                {
                    rolls++;
                    GameLog.SpawnInfo("Processing loot: added over max quantity for {item}. Rerolling", item.Value);
                    continue;
                }

                // If quantity and uniqueness are good, add the item
                loot.Add(item);
                GameLog.SpawnInfo("Processing loot: added {item}", item.Value);
                totalRolls++;
                // As a check against something incredibly stupid in XML, we only allow a maximum of
                // 100 rolls
                if (totalRolls > 100)
                {
                    GameLog.SpawnInfo("Processing loot: maximum number of rolls exceeded..?");
                    throw new LootRecursionError("Maximum number of rolls (100) exceeded!");
                }
            }while (rolls > 0);

            // Now we have the canonical droplist, which needs resolving into Items

            foreach (var lootitem in loot)
            {
                // Does the base item exist?
                var xmlItemList = Game.World.WorldData.FindItem(lootitem.Value);
                // Don't handle the edge case of multiple genders .... yet
                if (xmlItemList.Count != 0)
                {
                    var xmlItem = xmlItemList.First();
                    // Handle variants.
                    // If multiple variants are specified, we pick one at random
                    if (lootitem.Variants.Count() > 0)
                    {
                        var lootedVariant = lootitem.Variants.PickRandom();
                        if (xmlItem.Variants.TryGetValue(lootedVariant, out List <Xml.Item> variantItems))
                        {
                            itemList.Add(Game.World.CreateItem(variantItems.PickRandom().Id));
                        }
                        else
                        {
                            GameLog.SpawnError("Spawn loot calculation: variant group {name} not found", lootedVariant);
                        }
                    }
                    else
                    {
                        itemList.Add(Game.World.CreateItem(xmlItem.Id));
                    }
                }
                else
                {
                    GameLog.SpawnError("Spawn loot calculation: item {name} not found!", lootitem.Value);
                }
            }
            // We store loot as strings inside mobs to avoid having tens or hundreds of thousands of ItemObjects or
            // Items lying around - they're made into real objects at the time of mob death
            if (itemList.Count > 0)
            {
                return(itemList.Where(x => x != null).Select(y => y.Name).ToList());
            }
            return(new List <String>());
        }
Exemple #27
0
 public void OnSquelchStop(IThrottleTrigger trigger)
 {
     GameLog.Debug($"Client {trigger.Id}: squelch expired");
 }
Exemple #28
0
        public ThrottleResult ProcessThrottle(Client client, ClientPacket packet)
        {
            ThrottleInfo   info;
            ThrottleResult result = ThrottleResult.Error;

            if (!client.ThrottleState.TryGetValue(packet.Opcode, out info))
            {
                client.ThrottleState[packet.Opcode] = new ThrottleInfo();
                info = client.ThrottleState[packet.Opcode];
            }

            Monitor.Enter(info);

            try
            {
                DateTime rightnow = DateTime.Now;
                //GameLog.Warning($"Right now is {rightnow}");
                var transmitInterval = (rightnow - info.LastReceived);
                var acceptedInterval = (rightnow - info.LastAccepted);
                info.PreviousReceived = info.LastReceived;
                info.LastReceived     = rightnow;
                info.TotalReceived++;
                GameLog.Debug($"Begin: PA: {info.PreviousAccepted} LA: {info.LastAccepted} AInterval is {acceptedInterval.TotalMilliseconds} TInterval {transmitInterval.TotalMilliseconds}");

                if (info.Throttled)
                {
                    result = ThrottleResult.Throttled;
                    if (acceptedInterval.TotalMilliseconds >= ThrottleDuration && acceptedInterval.TotalMilliseconds >= Interval)
                    {
                        //GameLog.Error($"Unthrottled: {acceptedInterval.TotalMilliseconds} > {ThrottleDuration} and {Interval}");
                        info.Throttled      = false;
                        info.TotalThrottled = 0;
                        result = ThrottleResult.ThrottleEnd;
                        info.PreviousAccepted = info.LastAccepted;
                        info.LastAccepted     = rightnow;
                        OnThrottleStop(new ClientTrigger(client));
                    }
                    else
                    {
                        info.TotalThrottled++;
                        //GameLog.Error($"Throttled, count is {info.TotalThrottled}");

                        result = ThrottleResult.Throttled;
                        if (ThrottleDisconnectThreshold > 0 && info.TotalThrottled > ThrottleDisconnectThreshold)
                        {
                            result = ThrottleResult.Disconnect;
                            OnDisconnectThreshold(new ClientTrigger(client));
                        }
                    }
                }
                else
                {
                    if (acceptedInterval.TotalMilliseconds <= Interval && info.LastAccepted != DateTime.MinValue)
                    {
                        GameLog.Debug($"TInterval {transmitInterval}, AInterval {acceptedInterval} - maximum is {Interval}, throttled");
                        info.Throttled = true;
                        OnThrottleStart(new ClientTrigger(client));
                        result = ThrottleResult.Throttled;
                    }
                    else
                    {
                        info.PreviousAccepted = info.LastAccepted;
                        info.LastAccepted     = rightnow;
                        info.TotalAccepted++;
                        result = ThrottleResult.OK;
                        GameLog.Debug($"Packet accepted, PA: {info.PreviousAccepted} LA: {info.LastAccepted}");
                    }
                }
            }
            finally
            {
                Monitor.Exit(info);
            }
            return(result);
        }
Exemple #29
0
        public void Spawn(Xml.SpawnGroup spawnGroup)
        {
            foreach (var map in spawnGroup.Maps)
            {
                if (map.Disabled)
                {
                    continue;
                }
                try
                {
                    var spawnMap = Game.World.WorldData.Get <Map>(map.Id);
                    GameLog.SpawnDebug("Spawn: calculating {0}", spawnMap.Name);
                    var monsterList  = spawnMap.Objects.OfType <Monster>().ToList();
                    var monsterCount = monsterList.Count;

                    // If there is no limit specified, we want a reasonable limit, which we consider to be 1/10th of total
                    // number of map tiles

                    var spawnLimit = map.Limit == 0 ? (spawnMap.X * spawnMap.Y) / 10 : map.Limit;

                    if (monsterCount > spawnLimit)
                    {
                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, mob count is {monsterCount}, limit is {spawnLimit}");
                        }
                        continue;
                    }

                    var since = DateTime.Now - map.LastSpawn;
                    if (since.TotalSeconds < map.Interval)
                    {
                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, last spawn was {since.TotalSeconds} ago, interval {map.Interval}");
                        }
                        continue;
                    }

                    map.LastSpawn = DateTime.Now;

                    var thisSpawn = _random.Next(map.MinSpawn, map.MaxSpawn);

                    GameLog.SpawnInfo($"Spawn: {map.Name}: spawning {thisSpawn} mobs ");

                    for (var i = 0; i < thisSpawn; i++)
                    {
                        var idx          = _random.Next(0, spawnGroup.Spawns.Count - 1);
                        var spawn        = spawnGroup.Spawns[idx];
                        var creature     = _creatures.Single(x => x.Name == spawn.Base);
                        var newSpawnLoot = LootBox.CalculateLoot(spawn);

                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo("Spawn {name}, map {map}: {Xp} xp, {Gold} gold, items {Items}", spawn.Base, map.Name, newSpawnLoot.Xp, newSpawnLoot.Gold,
                                              string.Join(',', newSpawnLoot.Items));
                        }
                        var baseMob = new Monster(creature, spawn, map.Id, newSpawnLoot);
                        var mob     = (Monster)baseMob.Clone();

                        var xcoord = 0;
                        var ycoord = 0;
                        do
                        {
                            xcoord = _random.Next(0, spawnMap.X - 1);
                            ycoord = _random.Next(0, spawnMap.Y - 1);
                        } while (spawnMap.IsWall[xcoord, ycoord]);
                        mob.X = (byte)xcoord;
                        mob.Y = (byte)ycoord;

                        if (spawnMap.SpawnDebug)
                        {
                            GameLog.SpawnInfo($"Spawn: spawning {mob.Name} on {spawnMap.Name}");
                        }
                        SpawnMonster(mob, spawnMap);
                    }
                }
                catch (Exception e)
                {
                    GameLog.SpawnError(e, "Spawngroup {Filename}: disabled map {Name} due to error", spawnGroup.Filename, map.Name);
                    map.Disabled = true;
                    continue;
                }
            }
        }