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(); }
/// <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(); } }
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); } }
/// <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); } } }
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); } } } }
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()); }
/// <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); }
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; } } }
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()); }
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); }
public void RemoveCreature(Creature toRemove) { World.Remove(toRemove); Remove(toRemove); GameLog.DebugFormat("Removing creature {0} (id {1})", toRemove.Name, toRemove.Id); }
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); }
//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(); }
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); }
public virtual void Shutdown() { GameLog.WarningFormat("{ServerType}: shutting down", this.GetType().ToString()); Listener?.Close(); GameLog.WarningFormat("{ServerType}: shutdown complete", this.GetType().ToString()); }
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}"); } }
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(); }
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; } }
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; } } }
/// <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); }
public void DumpPacket() { // Dump the packet to the console. GameLog.DebugFormat("Dumping packet:"); GameLog.DebugFormat(BitConverter.ToString(Data)); }
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; } } }
public void DumpPacket() { // Dump the packet to the console. GameLog.Debug($"Dumping packet: {Opcode:X2}"); GameLog.Debug(ToString()); }
public void OnThrottleStop(IThrottleTrigger trigger) { GameLog.Debug($"Client {trigger.Id}: throttle expired"); }
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); }
/// <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>()); }
public void OnSquelchStop(IThrottleTrigger trigger) { GameLog.Debug($"Client {trigger.Id}: squelch expired"); }
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); }
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; } } }