/// <summary> /// Default constructor to be used by protobuf. /// </summary> public AddCharacterMessage() { AddingCharacter = new Character(); UserId = Guid.Empty; Location = new Vector3(); TeleportExitId = 0; UseExitOrLocation = ExitOrLocation.Location; }
/// <summary> /// Constructor specifying entering charachter, teleport exit id, and user id. This assumes that the character is entering at a teleport exit id. /// </summary> /// <param name="character">The entering character.</param> /// <param name="teleportexitid">The id of the teleport exit that is being entered upon.</param> /// <param name="userid">The id of the user that owns the character that is entering.</param> public AddCharacterMessage(Character character, ushort teleportexitid, Guid userid) { AddingCharacter = character; UserId = userid; Location = new Vector3(); TeleportExitId = teleportexitid; UseExitOrLocation = ExitOrLocation.Exit; }
/// <summary> /// Constructor specifying entering character, location, and user id. This assumes that the character is entering at a location. /// </summary> /// <param name="character">The character that is entering.</param> /// <param name="location">The location that the character will be at upon entering.</param> /// <param name="userid">The id of the user that owns the character that is entering.</param> public AddCharacterMessage(Character character, Vector3 location, Guid userid) { AddingCharacter = character; UserId = userid; Location = location; TeleportExitId = 0; UseExitOrLocation = ExitOrLocation.Location; }
/// <summary> /// Sends an enter region event to the supplied net connection. /// </summary> /// <param name="netConnection">The net connection that the event is going to.</param> /// <param name="character">The character that is entering.</param> /// <param name="userid">The user id that corresponds to the user that owns the character that is entering.</param> /// <remarks> /// Events have the form: /// Byte 1: Data Type - Event /// Byte 2: ServerEventCode /// Byte 3-n: Data /// </remarks> private void SendEnterRegionEvent(NetConnection netConnection, Character character, Guid userid) { EnterRegionEvent enterregionevent; enterregionevent = new EnterRegionEvent(character, userid); NetOutgoingMessage msg = LidgrenServer.CreateMessage(); msg.Write((byte)DataTypes.Event); msg.Write((byte)ServerEventCodes.EnterRegion); msg.Write(Serialize(enterregionevent)); LidgrenServer.SendMessage(msg, netConnection, NetDeliveryMethod.ReliableUnordered); }
//MESSAGES MESSAGES MESSAGES MESSAGES /// <summary> /// Sends an add character message to the region server corresponding to the region id found in character. /// If teleportexitid is null, then it uses the position to enter at. If the position is null, use the teleportexitid /// to determine where the character should spawn on the region. /// </summary> /// <param name="character">The character that is being added to the region server.</param> /// <param name="position">The character's possible spawn position.</param> /// <param name="teleportexitid">The teleport exit the character could come out of.</param> /// <param name="userid">The user id of the user that owns the character that is entering.</param> /// <remarks> /// Messages have the form: /// Byte 1: Region Message code /// Byte 2-n: Data /// </remarks> /// <exception cref="Exception"> /// Exception is thrown if neither a position nor a teleport exit id is provided. /// </exception> private void AddCharacterMessage(Character character, Vector3 position, ushort? teleportexitid, Guid userid) { AddCharacterMessage addcharactermessage; if (teleportexitid != null) { addcharactermessage = new AddCharacterMessage(character, (ushort)teleportexitid, userid); addcharactermessage.UseExitOrLocation = ExitOrLocation.Exit; Console.WriteLine("Adding Character with teleportexit"); } else if (position != null) { addcharactermessage = new AddCharacterMessage(character, position, userid); addcharactermessage.UseExitOrLocation = ExitOrLocation.Location; Console.WriteLine("Adding Character with position"); } else { throw new Exception("Character must be added to a region using either position or a teleport exit id!"); } NetOutgoingMessage msg = RegionServers[character.RegionId].CreateMessage(); msg.Write((byte)RegionMessages.AddCharacter); msg.Write(Serialize(addcharactermessage)); RegionServers[character.RegionId].SendMessage(msg, NetDeliveryMethod.ReliableOrdered); }
/// <summary> /// Process all information coming from clients, and other servers. /// </summary> /// <remarks> /// Connection Approval is enabled, so when a client connects to this server it must /// provide the correct data. That is a username and id. /// The login server gets a special case, in that its username is "lgn", and its id is Guid.Empty. /// If the master server hasn't been notified by the login server that a user is logging in, it won't /// know about this information and will reject the client. /// </remarks> public void Process() { NetIncomingMessage msg; //Read what incoming messages we have from clients while ((msg = LidgrenServer.ReadMessage()) != null) { switch (msg.MessageType) { //If it is a debug message, warning message, or status changed print the relevant information to the console case NetIncomingMessageType.DebugMessage: Console.WriteLine("Debug message: " + msg.ReadString()); break; case NetIncomingMessageType.WarningMessage: Console.WriteLine("Warning message: " + msg.ReadString()); break; case NetIncomingMessageType.StatusChanged: Console.WriteLine("Status changed for " + msg.SenderEndpoint + " to " + msg.SenderConnection.Status); break; //Check the connection approval for an accepted username and userid case NetIncomingMessageType.ConnectionApproval: //Read the username an userid from the message string username = msg.ReadString(); Guid userid = new Guid(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes)); //If the username is "lgn" and the userid is Guid.Empty, this is the login server's client. Let it connect regardless. //We should probably validate this harder in the future if (username == "lgn" && userid == Guid.Empty) { Console.WriteLine("Login accept"); msg.SenderConnection.Approve(); } //If the user with the userid and username has already been accepted by the login server //and their information has been sent here, let them be accepted. else if (Users.ContainsKey(userid) && Users[userid].Username == username) { Console.WriteLine("User: "******" accept."); Users[userid].Connection = msg.SenderConnection; msg.SenderConnection.Approve(); } //Otherwise, don't let them in here. else { Console.WriteLine("User: "******" with userid " + userid + " reject."); msg.SenderConnection.Deny(); } break; case NetIncomingMessageType.Data: //We are decrypting data. All data sent to a master server is encrypted with key "DERPHERP". msg.Decrypt(new NetXtea("DERPHERP")); //Figure out where this data came from. CallSources source = (CallSources)msg.ReadByte(); Console.WriteLine("Got data from " + source); //If we got the data from a login server if (source == CallSources.LoginServer) { //Figure out what kind of login server request it is. LoginServerRequests request = (LoginServerRequests)msg.ReadByte(); //If a user logged in, deal with it. if (request == LoginServerRequests.Login) { //Deserialize our login message from the login server LoginMessage loginmessage = new LoginMessage(); loginmessage = Serializer.Deserialize<LoginMessage>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); //Create a new user with the relevant information. User u = new User(); u.Connection = msg.SenderConnection; u.Id = loginmessage.LoginUser.Id; u.Username = loginmessage.LoginUser.Username; //Add the user to our master server Users.Add(loginmessage.LoginUser.Id, u); Console.WriteLine(u.Username + " logged in."); } //If a user logged out, double deal with it. else if (request == LoginServerRequests.Logout) { //Deserialize the logout message LogoutMessage logoutmessage = new LogoutMessage(); logoutmessage = Serializer.Deserialize<LogoutMessage>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); Console.WriteLine(Users[logoutmessage.UserId].Username + " logged out."); //If this user had an active character (that would be on a region server) send a message to the //region server to remove the character. if(Users[logoutmessage.UserId].ActiveCharacter != null) SendRemoveCharacterMessage(Users[logoutmessage.UserId].ActiveCharacter.RegionId, logoutmessage.UserId); //Remove the user from the master server. Users.Remove(logoutmessage.UserId); } } //If we got a message from the client else if (source == CallSources.Client) { //It is a remote procedure call, so figure out what kind it is RemoteProcedureCallCodes callcode = (RemoteProcedureCallCodes)msg.ReadByte(); //The user created a character. if (callcode == RemoteProcedureCallCodes.CreateCharacter) { //Deserialize that message CreateCharacterRequest createcharactersrequest = new CreateCharacterRequest(); createcharactersrequest = Serializer.Deserialize<CreateCharacterRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); //See if a character with the name requested already exists. if (!CharacterExists(createcharactersrequest.Name)) { //Create a new character with a new guid. Guid newguid = Guid.NewGuid(); //With arbitrary information about the character for now. Default regionid 0, default pos and rot, default race, height and weight. Character character = new Character(createcharactersrequest.Name, RaceCode.human, 5, 170, newguid, new Vector3(0.0f, 10.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f), 0, new Dictionary<ulong,int>()); //Add the character to be associated with this user id. UserIdToCharacters[createcharactersrequest.UserId].Add(character); //Send a resposne to the client saying the creation was successful SendCreateCharacterResponse(msg.SenderConnection, true); Console.WriteLine("User " + Users[createcharactersrequest.UserId].Username + " created a character named " + createcharactersrequest.Name); } else { //Otherwise send a response to the client saying the creation was unsuccessful SendCreateCharacterResponse(msg.SenderConnection, false); Console.WriteLine("User " + Users[createcharactersrequest.UserId].Username + " couldn't create a character named " + createcharactersrequest.Name); } } //The user needs to get characters, so they can see which one to select else if (callcode == RemoteProcedureCallCodes.GetCharacters) { //Deserialize the get characters request GetCharactersRequest getcharactersrequest = new GetCharactersRequest(); getcharactersrequest = Serializer.Deserialize<GetCharactersRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); //Make sure this user has a character list started if (UserIdToCharacters.ContainsKey(getcharactersrequest.UserId)) { //If so, send the get characters response SendGetCharactersResponse(msg.SenderConnection, getcharactersrequest.UserId); } else { //Else make a new list, and send the get characters response UserIdToCharacters.Add(getcharactersrequest.UserId, new List<Character>()); SendGetCharactersResponse(msg.SenderConnection, getcharactersrequest.UserId); } Console.WriteLine("Getting Characters for " + Users[getcharactersrequest.UserId].Username); } //The user selected a character and is entering the world. else if (callcode == RemoteProcedureCallCodes.EnterWorld) { //Deserialize the enterworld request EnterWorldRequest enterworldrequest = new EnterWorldRequest(); enterworldrequest = Serializer.Deserialize<EnterWorldRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); //Set this users active character to the one selected. Users[enterworldrequest.UserId].ActiveCharacter = UserIdToCharacters[enterworldrequest.UserId].Find(delegate(Character c) { return c.Id == enterworldrequest.CharacterId; }); //Send an Add Character Message to the appropriate region server AddCharacterMessage(UserIdToCharacters[enterworldrequest.UserId].Find(delegate(Character c) { return c.Id == enterworldrequest.CharacterId; }), Users[enterworldrequest.UserId].ActiveCharacter.Position, null, enterworldrequest.UserId); Console.WriteLine("User " + Users[enterworldrequest.UserId].Username + " made their character " + Users[enterworldrequest.UserId].ActiveCharacter.Name + " enter the world into region " + Users[enterworldrequest.UserId].ActiveCharacter.RegionId); } else if (callcode == RemoteProcedureCallCodes.Teleport) { TeleportRequest request = Serializer.Deserialize<TeleportRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); TeleportMessage(Users[request.UserId].ActiveCharacter.RegionId, request.UserId, request.TeleportId); Console.WriteLine("Teleport request."); } //The client sent a character snapshot (position, rotation, velocity) else if (callcode == RemoteProcedureCallCodes.CharacterSnapshot) { CharacterSnapshot snapshot = new CharacterSnapshot(); snapshot = Serializer.Deserialize<CharacterSnapshot>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); SendCharacterSnapshotMessage(Users[snapshot.UserId].ActiveCharacter.RegionId, snapshot); //Console.WriteLine("Snapshot for " + Users[snapshot.UserId].ActiveCharacter.Name); } else if (callcode == RemoteProcedureCallCodes.DropItem) { DropItemRequest request = Serializer.Deserialize<DropItemRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); DropItemMessage(Users[request.UserId].ActiveCharacter.RegionId, request.ItemId, request.UserId); Console.WriteLine("Drop Item request for " + Users[request.UserId].ActiveCharacter.Name); } else if (callcode == RemoteProcedureCallCodes.PickupItem) { PickupItemRequest request = Serializer.Deserialize<PickupItemRequest>(new MemoryStream(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes))); //TODO PickupItemMessage(Users[request.UserId].ActiveCharacter.RegionId, request.UserId, request.ItemSpawnId); } } break; } LidgrenServer.Recycle(msg); } //Or we recieved a message from one of the many region servers foreach (NetClient regionclient in RegionServers.Values) { NetIncomingMessage regionmessage; //Read a message from the queue while ((regionmessage = regionclient.ReadMessage()) != null) { switch (regionmessage.MessageType) { //If it is debug, warning, or status changed output to screen case NetIncomingMessageType.DebugMessage: Console.WriteLine("Region Debug: " + regionmessage.ReadString()); break; case NetIncomingMessageType.WarningMessage: Console.WriteLine("Region Warning: " + regionmessage.ReadString()); break; case NetIncomingMessageType.StatusChanged: Console.WriteLine("Region status changed: " + regionmessage.SenderConnection.Status); break; //Or we got some actual data case NetIncomingMessageType.Data: //Figure out what kind of message it is RegionMessages messagetype = (RegionMessages)regionmessage.ReadByte(); //A character was added to this region if (messagetype == RegionMessages.AddCharacterBroadcast) { Console.WriteLine("Got an Add Character broadcast from a region."); //Deserialize the broadcast AddCharacterBroadcast addbroadcast = Serializer.Deserialize<AddCharacterBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); //Send an event to all the relevant users using their userid foreach (Guid userid in addbroadcast.RecivingUserIds) { SendEnterRegionEvent(Users[userid].Connection, addbroadcast.EnteringCharacter, addbroadcast.UserId); } } //A user asked what characters were in this region, so we relay it back via enter region response else if (messagetype == RegionMessages.AddCharacterResponse) { Console.WriteLine("Relaying characters in region as well as position."); AddCharacterResponseMessage message = Serializer.Deserialize<AddCharacterResponseMessage>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); Console.WriteLine(message.Location.X + " " + message.Location.Y + " " + message.Location.Z); SendEnterRegionResponse(Users[message.MeantForUserId].Connection, message.Characters, message.ItemSpawns, message.Location, message.RegionId); } //A character was removed from this region else if (messagetype == RegionMessages.RemoveCharacterBroadcast) { Console.WriteLine("Got a Remove Character broadcast from a region."); //Deserialize the broadcast RemoveCharacterBroadcast exitregionbroadcast = Serializer.Deserialize<RemoveCharacterBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); //Send it to all relevant user ids foreach (Guid userid in exitregionbroadcast.RecivingUserIds) { SendExitRegionEvent(Users[userid].Connection, exitregionbroadcast.UserId); } } //A character sent a snapshot of their information to the region else if (messagetype == RegionMessages.CharacterSnapshotBroadcast) { Console.WriteLine("Got a character snapshot broadcast from a region."); CharacterSnapshotBroadcast charsnapbroadcast = Serializer.Deserialize<CharacterSnapshotBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); //Send the event to all relevant users foreach (Guid userid in charsnapbroadcast.ReceivingUserIds) SendCharacterSnapshotEvent(Users[userid].Connection, charsnapbroadcast.Snapshot); } else if (messagetype == RegionMessages.TeleportCharacterResponse) { TeleportResponseMessage message = Serializer.Deserialize<TeleportResponseMessage>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); Users[message.UserId].ActiveCharacter.RegionId = message.ExitRegionId; Console.WriteLine("Got teleport character response, and adding character."); AddCharacterMessage(Users[message.UserId].ActiveCharacter, null, message.TeleportExitId, message.UserId); } else if (messagetype == RegionMessages.DropItemResponse) { DropItemResponseMessage message = Serializer.Deserialize<DropItemResponseMessage>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); SendDropItemResponse(Users[message.UserId].Connection, message.Success, message.ItemId); } else if(messagetype == RegionMessages.DropItemBroadcast) { DropItemBroadcast broadcast = Serializer.Deserialize<DropItemBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); foreach (Guid userid in broadcast.ReceivingUserIds) { SendCreateItemSpawnEvent(Users[userid].Connection, broadcast.NewItemSpawn); } } else if(messagetype == RegionMessages.PickupItemResponse) { PickupItemResponseMessage message = Serializer.Deserialize<PickupItemResponseMessage>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); SendTakeItemResponse(Users[message.UserId].Connection, message.Success, message.ItemId); } else if(messagetype == RegionMessages.PickupItemBroadcast) { PickupItemBroadcast broadcast = Serializer.Deserialize<PickupItemBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); foreach(Guid userid in broadcast.Recipients) { SendTakeItemEvent(Users[userid].Connection, broadcast.ItemSpawnId); } } else if (messagetype == RegionMessages.SpawnItemBroadcast) { SpawnItemBroadcast broadcast = Serializer.Deserialize<SpawnItemBroadcast>(new MemoryStream(regionmessage.ReadBytes(regionmessage.LengthBytes - regionmessage.PositionInBytes))); foreach(Guid userid in broadcast.Recipients) { SendSpawnItemEvent(Users[userid].Connection, broadcast.SpawnId, broadcast.ItemId); } } break; } LidgrenServer.Recycle(regionmessage); } } }
/// <summary> /// Broadcast that get's sent to multiple master servers when a character is added to this region. /// </summary> /// <param name="connection">The connection to send the broadcast to.</param> /// <param name="character">The character that was added.</param> /// <param name="userid">The userid of the character that was added.</param> /// <param name="recipients">The recipients that will recieve this on the master server it is going to.</param> /// <remarks> /// Region Message Schema: /// Byte 1: RegionMessage code /// Byte 2-N: Data /// </remarks> private void SendAddCharacterBroadcast(NetConnection connection, Character character, Guid userid, List<Guid> recipients) { AddCharacterBroadcast enterregionbroadcast = new AddCharacterBroadcast() { EnteringCharacter = character, UserId = userid, RecivingUserIds = recipients }; NetOutgoingMessage broadcastmessage = LidgrenServer.CreateMessage(); broadcastmessage.Write((byte)RegionMessages.AddCharacterBroadcast); broadcastmessage.Write(Serialize(enterregionbroadcast)); LidgrenServer.SendMessage(broadcastmessage, connection, NetDeliveryMethod.ReliableUnordered); }
/// <summary> /// Constructor specifying the entering character, the recipients and the user id. /// </summary> /// <param name="character">Character entering the region.</param> /// <param name="recipients">Recipients of this broadcast.</param> /// <param name="userid">The id of the user that owns the character that is entering.</param> public AddCharacterBroadcast(Character character, List<Guid> recipients, Guid userid) { EnteringCharacter = character; RecivingUserIds = recipients; UserId = userid; }
/// <summary> /// Default constructor required by protobuf. /// </summary> public AddCharacterBroadcast() { EnteringCharacter = new Character(); RecivingUserIds = new List<Guid>(); UserId = Guid.Empty; }
/// <summary> /// Constructor specifying the characters and the count of characters. /// </summary> /// <param name="chars">The characters owned by a user.</param> /// <param name="count">The count of characters owned by a user.</param> public GetCharactersResponse(Character[] chars, uint count) { Characters = chars; Count = count; }
/// <summary> /// Constructor letting entering character and user id be defined. /// </summary> /// <param name="c">The character that's entering the region.</param> /// <param name="userid">The userid to the user that owns this character.</param> public EnterRegionEvent(Character c, Guid userid) { EnteringCharacter = c; UserId = userid; }