using System; using System.Buffers; using System.Collections.Generic; using System.Text; namespace LightReflectiveMirror { public class RelayHandler { List rooms = new List(); List pendingAuthentication = new List(); ArrayPool sendBuffers; int maxPacketSize = 0; public RelayHandler(int maxPacketSize) { this.maxPacketSize = maxPacketSize; sendBuffers = ArrayPool.Create(maxPacketSize, 50); } public void ClientConnected(int clientId) { pendingAuthentication.Add(clientId); var buffer = sendBuffers.Rent(1); int pos = 0; buffer.WriteByte(ref pos, (byte)OpCodes.AuthenticationRequest); Program.transport.ServerSend(clientId, 0, new ArraySegment(buffer, 0, pos)); sendBuffers.Return(buffer); } public void HandleMessage(int clientId, ArraySegment segmentData, int channel) { try { var data = segmentData.Array; int pos = segmentData.Offset; OpCodes opcode = (OpCodes)data.ReadByte(ref pos); if (pendingAuthentication.Contains(clientId)) { if (opcode == OpCodes.AuthenticationResponse) { string authResponse = data.ReadString(ref pos); if (authResponse == Program.conf.AuthenticationKey) { pendingAuthentication.Remove(clientId); int writePos = 0; var sendBuffer = sendBuffers.Rent(1); sendBuffer.WriteByte(ref writePos, (byte)OpCodes.Authenticated); Program.transport.ServerSend(clientId, 0, new ArraySegment(sendBuffer, 0, writePos)); } } return; } switch (opcode) { case OpCodes.CreateRoom: CreateRoom(clientId, data.ReadInt(ref pos), data.ReadString(ref pos), data.ReadBool(ref pos), data.ReadString(ref pos)); break; case OpCodes.RequestID: SendClientID(clientId); break; case OpCodes.LeaveRoom: LeaveRoom(clientId); break; case OpCodes.JoinServer: JoinRoom(clientId, data.ReadInt(ref pos)); break; case OpCodes.KickPlayer: LeaveRoom(data.ReadInt(ref pos), clientId); break; case OpCodes.SendData: ProcessData(clientId, data.ReadBytes(ref pos), channel, data.ReadInt(ref pos)); break; case OpCodes.RequestServers: SendServerList(clientId); break; case OpCodes.UpdateRoomData: var plyRoom = GetRoomForPlayer(clientId); if (plyRoom == null) return; bool newName = data.ReadBool(ref pos); if (newName) plyRoom.serverName = data.ReadString(ref pos); bool newData = data.ReadBool(ref pos); if (newData) plyRoom.serverData = data.ReadString(ref pos); bool newPublicStatus = data.ReadBool(ref pos); if (newPublicStatus) plyRoom.isPublic = data.ReadBool(ref pos); bool newPlayerCap = data.ReadBool(ref pos); if (newPlayerCap) plyRoom.maxPlayers = data.ReadInt(ref pos); break; } } catch { // Do Nothing. Client probably sent some invalid data. } } public void HandleDisconnect(int clientId) { LeaveRoom(clientId); } void SendServerList(int clientId) { int pos = 0; var buffer = sendBuffers.Rent(500); buffer.WriteByte(ref pos, (byte)OpCodes.ServerListReponse); for(int i = 0; i < rooms.Count; i++) { if (rooms[i].isPublic) { buffer.WriteBool(ref pos, true); buffer.WriteString(ref pos, rooms[i].serverName); buffer.WriteString(ref pos, rooms[i].serverData); buffer.WriteInt(ref pos, rooms[i].serverId); buffer.WriteInt(ref pos, rooms[i].maxPlayers); buffer.WriteInt(ref pos, rooms[i].clients.Count + 1); } } buffer.WriteBool(ref pos, false); Program.transport.ServerSend(clientId, 0, new ArraySegment(buffer, 0, pos)); sendBuffers.Return(buffer); } void ProcessData(int clientId, byte[] clientData, int channel, int sendTo = -1) { Room playersRoom = GetRoomForPlayer(clientId); if(playersRoom != null) { Room room = playersRoom; if(room.hostId == clientId) { if (room.clients.Contains(sendTo)) { int pos = 0; byte[] sendBuffer = sendBuffers.Rent(maxPacketSize); sendBuffer.WriteByte(ref pos, (byte)OpCodes.GetData); sendBuffer.WriteBytes(ref pos, clientData); Program.transport.ServerSend(sendTo, channel, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } } else { // We are not the host, so send the data to the host. int pos = 0; byte[] sendBuffer = sendBuffers.Rent(maxPacketSize); sendBuffer.WriteByte(ref pos, (byte)OpCodes.GetData); sendBuffer.WriteBytes(ref pos, clientData); sendBuffer.WriteInt(ref pos, clientId); Program.transport.ServerSend(room.hostId, channel, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } } } Room GetRoomForPlayer(int clientId) { for(int i = 0; i < rooms.Count; i++) { if (rooms[i].hostId == clientId) return rooms[i]; if (rooms[i].clients.Contains(clientId)) return rooms[i]; } return null; } void JoinRoom(int clientId, int serverId) { LeaveRoom(clientId); for(int i = 0; i < rooms.Count; i++) { if(rooms[i].serverId == serverId) { if(rooms[i].clients.Count < rooms[i].maxPlayers) { rooms[i].clients.Add(clientId); int sendJoinPos = 0; byte[] sendJoinBuffer = sendBuffers.Rent(5); sendJoinBuffer.WriteByte(ref sendJoinPos, (byte)OpCodes.ServerJoined); sendJoinBuffer.WriteInt(ref sendJoinPos, clientId); Program.transport.ServerSend(clientId, 0, new ArraySegment(sendJoinBuffer, 0, sendJoinPos)); Program.transport.ServerSend(rooms[i].hostId, 0, new ArraySegment(sendJoinBuffer, 0, sendJoinPos)); sendBuffers.Return(sendJoinBuffer); return; } } } // If it got to here, then the server was not found, or full. Tell the client. int pos = 0; byte[] sendBuffer = sendBuffers.Rent(1); sendBuffer.WriteByte(ref pos, (byte)OpCodes.ServerLeft); Program.transport.ServerSend(clientId, 0, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } void CreateRoom(int clientId, int maxPlayers, string serverName, bool isPublic, string serverData) { LeaveRoom(clientId); Room room = new Room(); room.hostId = clientId; room.maxPlayers = maxPlayers; room.serverName = serverName; room.isPublic = isPublic; room.serverData = serverData; room.clients = new List(); room.serverId = GetRandomServerID(); rooms.Add(room); int pos = 0; byte[] sendBuffer = sendBuffers.Rent(5); sendBuffer.WriteByte(ref pos, (byte)OpCodes.RoomCreated); sendBuffer.WriteInt(ref pos, clientId); Program.transport.ServerSend(clientId, 0, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } int GetRandomServerID() { Random rand = new Random(); int temp = rand.Next(int.MinValue, int.MaxValue); while (DoesServerIdExist(temp)) temp = rand.Next(int.MinValue, int.MaxValue); return temp; } bool DoesServerIdExist(int id) { for (int i = 0; i < rooms.Count; i++) { if (rooms[i].serverId == id) return true; } return false; } void LeaveRoom(int clientId, int requiredHostId = -1) { for(int i = 0; i < rooms.Count; i++) { if(rooms[i].hostId == clientId) { int pos = 0; byte[] sendBuffer = sendBuffers.Rent(1); sendBuffer.WriteByte(ref pos, (byte)OpCodes.ServerLeft); for(int x = 0; x < rooms[i].clients.Count; x++) Program.transport.ServerSend(rooms[i].clients[x], 0, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); rooms[i].clients.Clear(); rooms.RemoveAt(i); return; } else { if (requiredHostId >= 0 && rooms[i].hostId != requiredHostId) continue; if(rooms[i].clients.RemoveAll(x => x == clientId) > 0) { int pos = 0; byte[] sendBuffer = sendBuffers.Rent(5); sendBuffer.WriteByte(ref pos, (byte)OpCodes.PlayerDisconnected); sendBuffer.WriteInt(ref pos, clientId); Program.transport.ServerSend(rooms[i].hostId, 0, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } } } } void SendClientID(int clientId) { int pos = 0; byte[] sendBuffer = sendBuffers.Rent(5); sendBuffer.WriteByte(ref pos, (byte)OpCodes.GetID); sendBuffer.WriteInt(ref pos, clientId); Program.transport.ServerSend(clientId, 0, new ArraySegment(sendBuffer, 0, pos)); sendBuffers.Return(sendBuffer); } } public enum OpCodes { Default = 0, RequestID = 1, JoinServer = 2, SendData = 3, GetID = 4, ServerJoined = 5, GetData = 6, CreateRoom = 7, ServerLeft = 8, PlayerDisconnected = 9, RoomCreated = 10, LeaveRoom = 11, KickPlayer = 12, AuthenticationRequest = 13, AuthenticationResponse = 14, RequestServers = 15, ServerListReponse = 16, Authenticated = 17, UpdateRoomData = 18, ServerConnectionData = 19 } }