internal void TogglePVP(Player p, bool status) { ushort length = 2; var writer = WriteHeader(length, NetworkRequest.TogglePVP); writer.Write(p.GetSlot()); writer.Write((byte)(status ? 1 : 0)); SendWrittenBytes(); PVPStatusChanged?.Invoke(p, status); }
private void Listen() { while (Thread.CurrentThread.IsAlive) { NetworkRequest type; byte[] message = ReadMessage(); if (message.Length == 0) { continue; } type = (NetworkRequest)message[0]; var payload = new MemoryStream(message.Skip(1).ToArray()); BinaryReader reader = new BinaryReader(payload); switch (type) { case NetworkRequest.FatalError: // Any fatal error that occured lead here reader.ReadByte(); throw new Exception("Fatal error: " + reader.ReadString()); case NetworkRequest.AuthentificationSuccess: // Authentification confirmation { byte slot = reader.ReadByte(); _me = new PlayerSelf(this, _playerInfos.GetName(), slot); _otherPlayers.Add(slot, _me); LogDebug("Player slot is now " + slot); SendPlayerInfoMessage(); // We don't send our health/mana/etc because we keep the default one if (_playerInfos.GetHealth() != 100) { SendPlayerHealth(_playerInfos.GetHealth()); } if (_playerInfos.GetMana() != 20) { SendPlayerMana(_playerInfos.GetMana()); } SendWorldInfoRequest(); } break; case NetworkRequest.CharacterCreation: { byte slot = reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); string name = reader.ReadString(); if (!_otherPlayers.ContainsKey(slot)) { LogInfo("New player with slot " + slot); Player player = new Player(this, name, slot); _otherPlayers.Add(slot, player); NewPlayerJoined?.Invoke(player); } } break; case NetworkRequest.WorldInfoAnswer: // Various basic information about the world if (!_didSpawn) { _didSpawn = true; var blocPixelSize = BlocGroup.blocPixelSize; int time = reader.ReadInt32(); byte moonInfo = reader.ReadByte(); byte moonPhase = reader.ReadByte(); short maxTilesX = reader.ReadInt16(); short maxTilesY = reader.ReadInt16(); short spawnX = reader.ReadInt16(); short spawnY = reader.ReadInt16(); short surfaceY = reader.ReadInt16(); short rockY = reader.ReadInt16(); LogDebug("Current time is " + time); LogDebug(ByteToBool(moonInfo, 1) ? "It's currently day time" : "It's currently night time"); LogDebug(ByteToBool(moonInfo, 2) ? "It's currently the blood moon" : "It's not currently the blood moon"); LogDebug(ByteToBool(moonInfo, 4) ? "It's currently an eclipse" : "It's not currently an eclipse"); LogDebug("The current moon phrase is " + moonPhase); LogDebug("Maximum world value at (" + (maxTilesX * blocPixelSize) + ";" + (maxTilesY * blocPixelSize) + ")"); LogDebug("Spawn world value at (" + (spawnX * blocPixelSize) + ";" + (spawnY * blocPixelSize) + ")"); LogDebug("Surface layer is at heigth of " + surfaceY); LogDebug("Rock layer is at height of " + rockY); _me.SetPosition(new Vector2(spawnX * blocPixelSize, spawnY * blocPixelSize), Vector2.Zero); _tiles = new Tile[maxTilesX, maxTilesY]; for (int i = 0; i < maxTilesX; i++) { for (int y = 0; y < maxTilesY; y++) { _tiles[i, y] = null; } } SendInitialTile(); } break; case NetworkRequest.TileData: // Some information about a row of tile? { MemoryStream stream = new MemoryStream(); if (payload.ReadByte() != 0) { using (DeflateStream deflate = new DeflateStream(payload, CompressionMode.Decompress)) { deflate.CopyTo(stream); deflate.Close(); } stream.Position = 0L; } else { stream = payload; stream.Position = 1L; } using (BinaryReader r = new BinaryReader(stream)) { int xStart = r.ReadInt32(); int yStart = r.ReadInt32(); int width = r.ReadInt16(); int height = r.ReadInt16(); LogDebug("Updating " + width + " x " + height + " tiles beginning at (" + xStart + ";" + yStart + ")"); // I have no idea what I'm doing but it's what Terraria is doing Tile tile = null; int value = 0; for (int y = yStart; y < yStart + height; y++) { for (int x = xStart; x < xStart + width; x++) { if (value != 0) { value--; _tiles[x, y] = (tile == null ? new Tile() : new Tile(tile)); } else { byte b = 0; byte b2 = 0; tile = _tiles[x, y]; _tiles[x, y] = new Tile(); if (tile == null) { tile = _tiles[x, y]; } byte b3 = r.ReadByte(); if ((b3 & 1) == 1) { b2 = r.ReadByte(); if ((b2 & 1) == 1) { b = r.ReadByte(); } } bool flag = tile.IsActive(); byte b4; if ((b3 & 2) == 2) { tile.Activate(true); ushort ttype = tile.GetTileType(); int num2; if ((b3 & 32) == 32) { b4 = r.ReadByte(); num2 = r.ReadByte(); num2 = (num2 << 8 | b4); } else { num2 = r.ReadByte(); } tile.SetTileType((ushort)num2); // num2 < _tileFrameImportant.Length shouldn't be here if (num2 > _tileFrameImportant.Length) { LogError(num2 + " is bigger than _tileFrameImportant length (" + _tileFrameImportant.Length + ")"); } if (num2 < _tileFrameImportant.Length && _tileFrameImportant[num2]) { tile.SetFrames(r.ReadInt16(), r.ReadInt16()); } else if (!flag || tile.GetTileType() != ttype) { tile.SetFrames(-1, -1); } if ((b & 8) == 8) { tile.Color(r.ReadByte()); } } if ((b3 & 4) == 4) { tile.SetWall(r.ReadByte()); if ((b & 16) == 16) { tile.ColorWall(r.ReadByte()); } } b4 = (byte)((b3 & 24) >> 3); if (b4 != 0) { tile.SetLiquid(r.ReadByte()); if (b4 > 1) { // Lava and honey management } } if (b2 > 1) { // Wires and slops management } if (b > 0) { // Some others electrical management } b4 = (byte)((b3 & 192) >> 6); if (b4 == 0) { value = 0; } else if (b4 == 1) { value = r.ReadByte(); } else { value = r.ReadInt16(); } } } } } } break; case NetworkRequest.PlayerControls: { byte slot = reader.ReadByte(); byte movement = reader.ReadByte(); bool up = ByteToBool(movement, 1); bool down = ByteToBool(movement, 2); bool left = ByteToBool(movement, 4); bool right = ByteToBool(movement, 8); bool jump = ByteToBool(movement, 16); bool useItem = ByteToBool(movement, 32); bool direction = ByteToBool(movement, 64); string keyInfo = "Key pressed: " + (up ? "Up " : "") + (down ? ", Down " : "") + (left + ", Left " + "") + (right + ", Right " + "") + (jump + ", Jump " + ""); byte otherMovement = reader.ReadByte(); byte selectedItem = reader.ReadByte(); float posX = reader.ReadSingle(); float posY = reader.ReadSingle(); var player = _otherPlayers[slot]; var newPos = new Vector2(posX, posY); float velX = 0f; float velY = 0f; PlayerPositionUpdate?.Invoke(player, newPos); if (ByteToBool(otherMovement, 4)) { velX = reader.ReadSingle(); velY = reader.ReadSingle(); LogDebug("Player " + slot + " is at (" + posX + ";" + posY + ") with a velocity of (" + velX + ";" + velY + ") " + keyInfo); } player.SetPosition(newPos, new Vector2(velX, velY)); LogDebug("Player " + slot + " is at (" + posX + ";" + posY + ") " + keyInfo); } break; case NetworkRequest.TileEdit: { byte action = reader.ReadByte(); short xPos = reader.ReadInt16(); short yPos = reader.ReadInt16(); short tileId = reader.ReadInt16(); if (action == 0) // Tile destroyed { _tiles[xPos, yPos] = new Tile(); } else { _tiles[xPos, yPos] = new Tile((byte)tileId); } } break; case NetworkRequest.TogglePVP: { byte slot = reader.ReadByte(); bool isPVP = reader.ReadByte() == 1; PVPStatusChanged?.Invoke(_otherPlayers[slot], isPVP); } break; case NetworkRequest.PasswordRequest: // The server need a password to be joined if (_password == "") { throw new ArgumentException("A password is needed to connect to the server."); } else { LogDebug("Sending password to server"); SendStringMessage(NetworkRequest.PasswordAnswer, _password); } break; case NetworkRequest.JoinTeam: { byte slot = reader.ReadByte(); byte team = reader.ReadByte(); TeamStatusChanged?.Invoke(_otherPlayers[slot], (Team)team); } break; case NetworkRequest.SpawnRequest: // When this is received, need to reply with spawn location LogInfo("Sending spawn request at (" + -1 + ";" + -1 + ")"); SendSpawnRequest(); ServerJoined?.Invoke(_me); break; case NetworkRequest.ChatMessage: { ushort id = reader.ReadUInt16(); byte slot = reader.ReadByte(); byte mode = reader.ReadByte(); try { string content = reader.ReadString(); if (mode == 0 && _otherPlayers.ContainsKey(slot)) { ChatMessageReceived?.Invoke(_otherPlayers[slot], content); } else if (mode == 2 && slot == 255) { LogDebug("Message received from server with id " + id + " and mode " + mode + ": " + content); } } catch (EndOfStreamException) // TODO: Need to fix this { } } break; case NetworkRequest.CharacterInventorySlot: case NetworkRequest.Status: case NetworkRequest.RecalculateUV: case NetworkRequest.BlockUpdate: case NetworkRequest.ItemInfo: case NetworkRequest.ItemOwnerInfo: case NetworkRequest.NPCInfo: case NetworkRequest.UpdateProjectile: case NetworkRequest.DeleteProjectile: case NetworkRequest.EvilRatio: case NetworkRequest.DailyAnglerQuestFinished: case NetworkRequest.EightyThree: case NetworkRequest.CharacterStealth: case NetworkRequest.InventoryItemInfo: case NetworkRequest.NinetySix: case NetworkRequest.TowerShieldStrength: break; default: // LogDebug("Unknown message type " + type); break; } } }