Removed NioTSO client and server

- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
This commit is contained in:
Tony Bark 2024-05-01 02:55:43 -04:00
parent f12ba1502b
commit 22191ce648
591 changed files with 53264 additions and 3362 deletions

View file

@ -0,0 +1,77 @@
using System;
namespace FSO.Server.Protocol.Aries
{
public enum AriesPacketType
{
Voltron,
Electron,
Gluon,
RequestClientSession,
RequestClientSessionResponse,
RequestChallenge,
RequestChallengeResponse,
AnswerChallenge,
AnswerAccepted,
Unknown
}
public static class AriesPacketTypeUtils
{
public static AriesPacketType FromPacketCode(uint code)
{
switch (code)
{
case 0:
return AriesPacketType.Voltron;
case 1000:
return AriesPacketType.Electron;
case 1001:
return AriesPacketType.Gluon;
case 1002:
return AriesPacketType.RequestChallenge;
case 1003:
return AriesPacketType.RequestChallengeResponse;
case 1004:
return AriesPacketType.AnswerChallenge;
case 1005:
return AriesPacketType.AnswerAccepted;
case 22:
return AriesPacketType.RequestClientSession;
case 21:
return AriesPacketType.RequestClientSessionResponse;
default:
return AriesPacketType.Unknown;
}
}
public static uint GetPacketCode(this AriesPacketType type)
{
switch (type)
{
case AriesPacketType.Voltron:
return 0;
case AriesPacketType.Electron:
return 1000;
case AriesPacketType.Gluon:
return 1001;
case AriesPacketType.RequestChallenge:
return 1002;
case AriesPacketType.RequestChallengeResponse:
return 1003;
case AriesPacketType.AnswerChallenge:
return 1004;
case AriesPacketType.AnswerAccepted:
return 1005;
case AriesPacketType.RequestClientSession:
return 22;
case AriesPacketType.RequestClientSessionResponse:
return 21;
default:
throw new Exception("Unknown aries packet type " + type.ToString());
}
}
}
}

View file

@ -0,0 +1,41 @@
using FSO.Server.Protocol.Aries.Packets;
using System;
using System.Collections.Generic;
namespace FSO.Server.Protocol.Aries
{
public class AriesPackets
{
public static Dictionary<uint, Type> ARIES_PACKET_BY_TYPEID;
public static Type[] ARIES_PACKETS = new Type[] {
typeof(RequestClientSession),
typeof(RequestClientSessionResponse),
typeof(RequestChallenge),
typeof(RequestChallengeResponse),
typeof(AnswerChallenge),
typeof(AnswerAccepted)
};
static AriesPackets()
{
ARIES_PACKET_BY_TYPEID = new Dictionary<uint, Type>();
foreach (Type packetType in ARIES_PACKETS)
{
IAriesPacket packet = (IAriesPacket)Activator.CreateInstance(packetType);
ARIES_PACKET_BY_TYPEID.Add(packet.GetPacketType().GetPacketCode(), packetType);
}
}
public static Type GetByPacketCode(uint code)
{
if (ARIES_PACKET_BY_TYPEID.ContainsKey(code))
{
return ARIES_PACKET_BY_TYPEID[code];
}
else
{
return null;
}
}
}
}

View file

@ -0,0 +1,38 @@
using Mina.Filter.Codec;
using Ninject;
using Mina.Core.Session;
namespace FSO.Server.Protocol.Aries
{
public class AriesProtocol : IProtocolCodecFactory
{
private IKernel Kernel;
public AriesProtocol(IKernel kernel)
{
this.Kernel = kernel;
}
private IProtocolDecoder _Decoder;
public IProtocolDecoder GetDecoder(IoSession session)
{
if (_Decoder == null)
{
_Decoder = Kernel.Get<AriesProtocolDecoder>();
}
return _Decoder;
}
private IProtocolEncoder _Encoder;
public IProtocolEncoder GetEncoder(IoSession session)
{
if(_Encoder == null)
{
_Encoder = Kernel.Get<AriesProtocolEncoder>();
}
return _Encoder;
}
}
}

View file

@ -0,0 +1,111 @@
using Mina.Filter.Codec;
using System;
using Mina.Core.Buffer;
using Mina.Core.Session;
using FSO.Server.Protocol.Voltron;
using FSO.Server.Protocol.Electron;
using FSO.Common.Serialization;
using FSO.Server.Protocol.Utils;
using FSO.Server.Protocol.Gluon;
namespace FSO.Server.Protocol.Aries
{
public class AriesProtocolDecoder : CustomCumulativeProtocolDecoder
{
//private static Logger LOG = LogManager.GetCurrentClassLogger();
private ISerializationContext Context;
public AriesProtocolDecoder(ISerializationContext context)
{
this.Context = context;
}
protected override bool DoDecode(IoSession session, IoBuffer buffer, IProtocolDecoderOutput output)
{
if(buffer.Remaining < 12){
return false;
}
/**
* We expect aries, voltron or electron packets
*/
var startPosition = buffer.Position;
buffer.Order = ByteOrder.LittleEndian;
uint packetType = buffer.GetUInt32();
uint timestamp = buffer.GetUInt32();
uint payloadSize = buffer.GetUInt32();
if (buffer.Remaining < payloadSize)
{
/** Not all here yet **/
buffer.Position = startPosition;
return false;
}
//LOG.Info("[ARIES] " + packetType + " (" + payloadSize + ")");
if(packetType == AriesPacketType.Voltron.GetPacketCode())
{
DecodeVoltronStylePackets(buffer, ref payloadSize, output, VoltronPackets.GetByPacketCode);
}
else if (packetType == AriesPacketType.Electron.GetPacketCode())
{
DecodeVoltronStylePackets(buffer, ref payloadSize, output, ElectronPackets.GetByPacketCode);
}else if(packetType == AriesPacketType.Gluon.GetPacketCode())
{
DecodeVoltronStylePackets(buffer, ref payloadSize, output, GluonPackets.GetByPacketCode);
}
else
{
//Aries
var packetClass = AriesPackets.GetByPacketCode(packetType);
if (packetClass != null)
{
byte[] data = new byte[(int)payloadSize];
buffer.Get(data, 0, (int)payloadSize);
IAriesPacket packet = (IAriesPacket)Activator.CreateInstance(packetClass);
var io = IoBuffer.Wrap(data);
io.Order = ByteOrder.LittleEndian;
packet.Deserialize(io, Context);
output.Write(packet);
payloadSize = 0;
}
else
{
buffer.Skip((int)payloadSize);
payloadSize = 0;
}
}
return true;
}
private void DecodeVoltronStylePackets(IoBuffer buffer, ref uint payloadSize, IProtocolDecoderOutput output, Func<ushort, Type> typeResolver)
{
while (payloadSize > 0)
{
/** Voltron packet **/
buffer.Order = ByteOrder.BigEndian;
ushort type = buffer.GetUInt16();
uint innerPayloadSize = buffer.GetUInt32() - 6;
byte[] data = new byte[(int)innerPayloadSize];
buffer.Get(data, 0, (int)innerPayloadSize);
var packetClass = typeResolver(type);
if (packetClass != null)
{
IoBufferDeserializable packet = (IoBufferDeserializable)Activator.CreateInstance(packetClass);
packet.Deserialize(IoBuffer.Wrap(data), Context);
output.Write(packet);
}
payloadSize -= innerPayloadSize + 6;
}
}
}
}

View file

@ -0,0 +1,166 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron;
using FSO.Server.Protocol.Gluon;
using FSO.Server.Protocol.Voltron;
using Mina.Core.Buffer;
using Mina.Core.Session;
using Mina.Filter.Codec;
using System;
namespace FSO.Server.Protocol.Aries
{
public class AriesProtocolEncoder : IProtocolEncoder
{
//private static Logger LOG = LogManager.GetCurrentClassLogger();
private ISerializationContext Context;
public AriesProtocolEncoder(ISerializationContext context)
{
this.Context = context;
}
public void Dispose(IoSession session)
{
}
public void Encode(IoSession session, object message, IProtocolEncoderOutput output)
{
if (message is IVoltronPacket)
{
EncodeVoltron(session, message, output);
}
else if (message is IElectronPacket)
{
EncodeElectron(session, message, output);
}else if(message is IGluonPacket)
{
EncodeGluon(session, message, output);
}
else if (message is IAriesPacket)
{
EncodeAries(session, message, output);
}
else if (message.GetType().IsArray)
{
object[] arr = (object[])message;
bool allVoltron = true;
for (var i = 0; i < arr.Length; i++)
{
if (!(arr[i] is IVoltronPacket))
{
allVoltron = false;
break;
}
}
//TODO: Chunk these into fewer packets
for (var i = 0; i < arr.Length; i++)
{
Encode(session, arr[i], output);
}
}
}
private void EncodeAries(IoSession session, object message, IProtocolEncoderOutput output)
{
IAriesPacket ariesPacket = (IAriesPacket)message;
AriesPacketType ariesPacketType = ariesPacket.GetPacketType();
var payload = IoBuffer.Allocate(128);
payload.Order = ByteOrder.LittleEndian;
payload.AutoExpand = true;
ariesPacket.Serialize(payload, Context);
payload.Flip();
int payloadSize = payload.Remaining;
IoBuffer headers = IoBuffer.Allocate(12);
headers.Order = ByteOrder.LittleEndian;
/**
* Aries header
* uint32 type
* uint32 timestamp
* uint32 payloadSize
*/
uint timestamp = (uint)TimeSpan.FromTicks(DateTime.Now.Ticks - session.CreationTime.Ticks).TotalMilliseconds;
headers.PutUInt32(ariesPacketType.GetPacketCode());
headers.PutUInt32(timestamp);
headers.PutUInt32((uint)payloadSize);
if (payloadSize > 0)
{
headers.AutoExpand = true;
headers.Put(payload);
}
headers.Flip();
output.Write(headers);
//output.Flush();
}
private void EncodeVoltron(IoSession session, object message, IProtocolEncoderOutput output)
{
IVoltronPacket voltronPacket = (IVoltronPacket)message;
VoltronPacketType voltronPacketType = voltronPacket.GetPacketType();
EncodeVoltronStylePackets(session, output, AriesPacketType.Voltron, voltronPacketType.GetPacketCode(), voltronPacket);
}
private void EncodeElectron(IoSession session, object message, IProtocolEncoderOutput output)
{
IElectronPacket packet = (IElectronPacket)message;
ElectronPacketType packetType = packet.GetPacketType();
EncodeVoltronStylePackets(session, output, AriesPacketType.Electron, packetType.GetPacketCode(), packet);
}
private void EncodeGluon(IoSession session, object message, IProtocolEncoderOutput output)
{
IGluonPacket packet = (IGluonPacket)message;
GluonPacketType packetType = packet.GetPacketType();
EncodeVoltronStylePackets(session, output, AriesPacketType.Gluon, packetType.GetPacketCode(), packet);
}
private void EncodeVoltronStylePackets(IoSession session, IProtocolEncoderOutput output, AriesPacketType ariesType, ushort packetType, IoBufferSerializable message)
{
var payload = IoBuffer.Allocate(512);
payload.Order = ByteOrder.BigEndian;
payload.AutoExpand = true;
message.Serialize(payload, Context);
payload.Flip();
int payloadSize = payload.Remaining;
IoBuffer headers = IoBuffer.Allocate(18);
headers.Order = ByteOrder.LittleEndian;
/**
* Aries header
* uint32 type
* uint32 timestamp
* uint32 payloadSize
*/
uint timestamp = (uint)TimeSpan.FromTicks(DateTime.Now.Ticks - session.CreationTime.Ticks).TotalMilliseconds;
headers.PutUInt32(ariesType.GetPacketCode());
headers.PutUInt32(timestamp);
headers.PutUInt32((uint)payloadSize + 6);
/**
* Voltron header
* uint16 type
* uint32 payloadSize
*/
headers.Order = ByteOrder.BigEndian;
headers.PutUInt16(packetType);
headers.PutUInt32((uint)payloadSize + 6);
if (payloadSize > 0)
{
headers.AutoExpand = true;
headers.Put(payload);
}
headers.Flip();
output.Write(headers);
//output.Flush();
}
}
}

View file

@ -0,0 +1,160 @@
using Mina.Core.Session;
using Mina.Core.Buffer;
using Mina.Core.Filterchain;
using FSO.Server.Common;
using Mina.Core.Write;
using FSO.Server.Protocol.Voltron;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Aries
{
public class AriesProtocolLogger : IoFilterAdapter
{
//private static Logger LOG = LogManager.GetCurrentClassLogger();
private IPacketLogger PacketLogger;
private ISerializationContext Context;
public AriesProtocolLogger(IPacketLogger packetLogger, ISerializationContext context)
{
this.PacketLogger = packetLogger;
this.Context = context;
}
public override void MessageSent(INextFilter nextFilter, IoSession session, IWriteRequest writeRequest)
{
IVoltronPacket voltronPacket = writeRequest.OriginalRequest.Message as IVoltronPacket;
if (voltronPacket != null)
{
var voltronBuffer = IoBuffer.Allocate(512);
voltronBuffer.Order = ByteOrder.BigEndian;
voltronBuffer.AutoExpand = true;
voltronPacket.Serialize(voltronBuffer, Context);
voltronBuffer.Flip();
var byteArray = new byte[voltronBuffer.Remaining];
voltronBuffer.Get(byteArray, 0, voltronBuffer.Remaining);
PacketLogger.OnPacket(new Packet
{
Data = byteArray,
Type = PacketType.VOLTRON,
SubType = voltronPacket.GetPacketType().GetPacketCode(),
Direction = PacketDirection.OUTPUT
});
nextFilter.MessageSent(session, writeRequest);
return;
}
IAriesPacket ariesPacket = writeRequest.OriginalRequest.Message as IAriesPacket;
if(ariesPacket != null)
{
IoBuffer ariesBuffer = IoBuffer.Allocate(128);
ariesBuffer.AutoExpand = true;
ariesBuffer.Order = ByteOrder.LittleEndian;
ariesPacket.Serialize(ariesBuffer, Context);
ariesBuffer.Flip();
var byteArray = new byte[ariesBuffer.Remaining];
ariesBuffer.Get(byteArray, 0, ariesBuffer.Remaining);
PacketLogger.OnPacket(new Packet
{
Data = byteArray,
Type = PacketType.ARIES,
SubType = ariesPacket.GetPacketType().GetPacketCode(),
Direction = PacketDirection.OUTPUT
});
nextFilter.MessageSent(session, writeRequest);
return;
}
IoBuffer buffer = writeRequest.Message as IoBuffer;
if (buffer == null)
{
nextFilter.MessageSent(session, writeRequest);
return;
}
TryParseAriesFrame(buffer, PacketDirection.OUTPUT);
nextFilter.MessageSent(session, writeRequest);
}
public override void MessageReceived(INextFilter nextFilter, IoSession session, object message)
{
IoBuffer buffer = message as IoBuffer;
if (buffer == null)
{
nextFilter.MessageReceived(session, message);
return;
}
TryParseAriesFrame(buffer, PacketDirection.INPUT);
nextFilter.MessageReceived(session, message);
}
private void TryParseAriesFrame(IoBuffer buffer, PacketDirection direction)
{
buffer.Rewind();
if(buffer.Remaining < 12)
{
return;
}
buffer.Order = ByteOrder.LittleEndian;
uint packetType = buffer.GetUInt32();
uint timestamp = buffer.GetUInt32();
uint payloadSize = buffer.GetUInt32();
if (buffer.Remaining < payloadSize)
{
buffer.Skip(-12);
return;
}
while (payloadSize > 0)
{
if (packetType == 0)
{
/** Voltron packet **/
buffer.Order = ByteOrder.BigEndian;
ushort voltronType = buffer.GetUInt16();
uint voltronPayloadSize = buffer.GetUInt32() - 6;
byte[] data = new byte[(int)voltronPayloadSize];
buffer.Get(data, 0, (int)voltronPayloadSize);
PacketLogger.OnPacket(new Packet
{
Data = data,
Type = PacketType.VOLTRON,
SubType = voltronType,
Direction = direction
});
payloadSize -= voltronPayloadSize + 6;
}
else
{
byte[] data = new byte[(int)payloadSize];
buffer.Get(data, 0, (int)payloadSize);
PacketLogger.OnPacket(new Packet
{
Data = data,
Type = PacketType.ARIES,
SubType = packetType,
Direction = direction
});
payloadSize = 0;
}
}
buffer.Rewind();
}
}
}

View file

@ -0,0 +1,14 @@
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Aries
{
public interface IAriesPacket : IoBufferSerializable, IoBufferDeserializable
{
/**
* Get packet type
*
* @return
*/
AriesPacketType GetPacketType();
}
}

View file

@ -0,0 +1,21 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Aries.Packets
{
public class AnswerAccepted : IAriesPacket
{
public void Deserialize(IoBuffer input, ISerializationContext context)
{
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.AnswerAccepted;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
}
}
}

View file

@ -0,0 +1,25 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Aries.Packets
{
public class AnswerChallenge : IAriesPacket
{
public string Answer;
public void Deserialize(IoBuffer input, ISerializationContext context)
{
Answer = input.GetPascalVLCString();
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.AnswerChallenge;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutPascalVLCString(Answer);
}
}
}

View file

@ -0,0 +1,31 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Aries.Packets
{
public class RequestChallenge : IAriesPacket
{
public string CallSign;
public string PublicHost;
public string InternalHost;
public void Deserialize(IoBuffer input, ISerializationContext context)
{
CallSign = input.GetPascalString();
PublicHost = input.GetPascalString();
InternalHost = input.GetPascalString();
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.RequestChallenge;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutPascalString(CallSign);
output.PutPascalString(PublicHost);
output.PutPascalString(InternalHost);
}
}
}

View file

@ -0,0 +1,25 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Aries.Packets
{
public class RequestChallengeResponse : IAriesPacket
{
public string Challenge;
public void Deserialize(IoBuffer input, ISerializationContext context)
{
Challenge = input.GetPascalVLCString();
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.RequestChallengeResponse;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutPascalVLCString(Challenge);
}
}
}

View file

@ -0,0 +1,21 @@
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Aries.Packets
{
public class RequestClientSession : IAriesPacket
{
public void Deserialize(IoBuffer input, ISerializationContext context)
{
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.RequestClientSession;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
}
}
}

View file

@ -0,0 +1,50 @@
using System.Text;
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Aries.Packets
{
public class RequestClientSessionResponse : IAriesPacket
{
public string User { get; set; }
public string AriesVersion { get; set; }
public string Email { get; set; }
public string Authserv { get; set; }
public ushort Product { get; set; }
public byte Unknown { get; set; } = 39;
public string ServiceIdent { get; set; }
public ushort Unknown2 { get; set; } = 4; //1 if re-establishing
public string Password { get; set; }
public void Deserialize(IoBuffer input, ISerializationContext context)
{
this.User = input.GetString(112, Encoding.ASCII);
this.AriesVersion = input.GetString(80, Encoding.ASCII);
this.Email = input.GetString(40, Encoding.ASCII);
this.Authserv = input.GetString(84, Encoding.ASCII);
this.Product = input.GetUInt16();
this.Unknown = input.Get();
this.ServiceIdent = input.GetString(3, Encoding.ASCII);
this.Unknown2 = input.GetUInt16();
this.Password = input.GetString(32, Encoding.ASCII);
}
public AriesPacketType GetPacketType()
{
return AriesPacketType.RequestClientSessionResponse;
}
public void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutString(this.User, 112, Encoding.ASCII);
output.PutString(this.AriesVersion, 80, Encoding.ASCII);
output.PutString(this.Email, 40, Encoding.ASCII);
output.PutString(this.Authserv, 84, Encoding.ASCII);
output.PutUInt16(this.Product);
output.Put(this.Unknown);
output.PutString(this.ServiceIdent, 3, Encoding.ASCII);
output.PutUInt16(this.Unknown2);
output.PutString(this.Password, 32, Encoding.ASCII);
}
}
}

View file

@ -0,0 +1,11 @@
namespace FSO.Server.Protocol.Authorization
{
public class AuthRequest
{
public string Username;
public string Password;
public string ServiceID;
public string Version;
public string ClientID;
}
}

View file

@ -0,0 +1,11 @@
namespace FSO.Server.Protocol.Authorization
{
public class AuthResult
{
public bool Valid;
public string Ticket;
public string ReasonCode;
public string ReasonText;
public string ReasonURL;
}
}

View file

@ -0,0 +1,9 @@
namespace FSO.Server.Protocol.CitySelector
{
public enum AvatarAppearanceType
{
Light = 0,
Medium = 1,
Dark = 2
}
}

View file

@ -0,0 +1,96 @@
using FSO.Common.Utils;
using System;
namespace FSO.Server.Protocol.CitySelector
{
public class AvatarData : IXMLEntity
{
public uint ID;
public string Name;
public string ShardName;
/** Non standard **/
/** Appearance **/
public AvatarAppearanceType AppearanceType { get; set; }
public ulong HeadOutfitID { get; set; }
public ulong BodyOutfitID { get; set; }
public string Description { get; set; }
/** Lot **/
public uint? LotId { get; set; }
public uint? LotLocation { get; set; }
public string LotName { get; set; }
#region IXMLPrinter Members
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
var result = doc.CreateElement("Avatar-Data");
result.AppendTextNode("AvatarID", ID.ToString());
result.AppendTextNode("Name", Name);
result.AppendTextNode("Shard-Name", ShardName);
//NEW: Appearance info
result.AppendTextNode("Head", HeadOutfitID.ToString());
result.AppendTextNode("Body", BodyOutfitID.ToString());
result.AppendTextNode("Appearance", AppearanceType.ToString());
if (LotId.HasValue && LotLocation.HasValue && LotName != null){
result.AppendTextNode("LotId", LotId.Value.ToString());
result.AppendTextNode("LotName", LotName);
result.AppendTextNode("LotLocation", LotLocation.Value.ToString());
}
result.AppendTextNode("Description", Description);
return result;
}
public void Parse(System.Xml.XmlElement element)
{
this.ID = uint.Parse(element.ReadTextNode("AvatarID"));
this.Name = element.ReadTextNode("Name");
this.ShardName = element.ReadTextNode("Shard-Name");
var headString = element.ReadTextNode("Head");
if (headString != null)
{
this.HeadOutfitID = ulong.Parse(headString);
}
var bodyString = element.ReadTextNode("Body");
if (bodyString != null)
{
this.BodyOutfitID = ulong.Parse(bodyString);
}
var apprString = element.ReadTextNode("Appearance");
if (apprString != null)
{
this.AppearanceType = (AvatarAppearanceType)Enum.Parse(typeof(AvatarAppearanceType), apprString);
}
var lotId = element.ReadTextNode("LotId");
if(lotId != null)
{
this.LotId = uint.Parse(lotId);
}
var lotLocation = element.ReadTextNode("LotLocation");
if (lotLocation != null)
{
this.LotLocation = uint.Parse(lotLocation);
}
LotName = element.ReadTextNode("LotName");
var descString = element.ReadTextNode("Description");
this.Description = descString ?? "";
}
#endregion
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.CitySelector
{
public enum AvatarGender
{
Male = 1,
Female = 2
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.CitySelector
{
public class InitialConnectServletRequest
{
public string Ticket;
public string Version;
}
}

View file

@ -0,0 +1,50 @@
using FSO.Common.Utils;
using System;
namespace FSO.Server.Protocol.CitySelector
{
public class InitialConnectServletResult : IXMLEntity
{
public InitialConnectServletResultType Status;
public XMLErrorMessage Error;
public UserAuthorized UserAuthorized;
#region IXMLEntity Members
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
throw new NotImplementedException();
}
public void Parse(System.Xml.XmlElement element)
{
switch (element.Name)
{
case "Error-Message":
Status = InitialConnectServletResultType.Error;
Error = new XMLErrorMessage();
Error.Parse(element);
break;
case "User-Authorized":
Status = InitialConnectServletResultType.Authorized;
UserAuthorized = new UserAuthorized();
UserAuthorized.Parse(element);
break;
case "Patch-Result":
Status = InitialConnectServletResultType.Patch;
break;
}
}
#endregion
}
public enum InitialConnectServletResultType
{
Authorized,
Patch,
Error
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.CitySelector
{
public class ShardSelectorServletRequest
{
public string ShardName;
public string AvatarID;
}
}

View file

@ -0,0 +1,45 @@
using FSO.Common.Utils;
namespace FSO.Server.Protocol.CitySelector
{
public class ShardSelectorServletResponse : IXMLEntity
{
public string Address;
public string Ticket;
public string ConnectionID;
public uint PlayerID;
public string AvatarID;
public bool PreAlpha = false;
#region IXMLPrinter Members
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
var result = doc.CreateElement("Shard-Selection");
result.AppendTextNode("Connection-Address", Address);
result.AppendTextNode("Authorization-Ticket", Ticket);
result.AppendTextNode("PlayerID", PlayerID.ToString());
if (PreAlpha == false)
{
result.AppendTextNode("ConnectionID", ConnectionID);
result.AppendTextNode("EntitlementLevel", "");
}
result.AppendTextNode("AvatarID", AvatarID); //freeso now uses this
return result;
}
public void Parse(System.Xml.XmlElement element)
{
this.Address = element.ReadTextNode("Connection-Address");
this.Ticket = element.ReadTextNode("Authorization-Ticket");
this.PlayerID = uint.Parse(element.ReadTextNode("PlayerID"));
this.AvatarID = element.ReadTextNode("AvatarID");
}
#endregion
}
}

View file

@ -0,0 +1,12 @@
namespace FSO.Server.Protocol.CitySelector
{
public enum ShardStatus
{
Up,
Down,
Busy,
Full,
Closed,
Frontier
}
}

View file

@ -0,0 +1,44 @@
using System;
using FSO.Common.Utils;
namespace FSO.Server.Protocol.CitySelector
{
public class ShardStatusItem : IXMLEntity
{
public string Name;
public int Rank;
public string Map;
public ShardStatus Status;
public int Id;
public string PublicHost;
public string InternalHost;
public string VersionName;
public string VersionNumber;
public int? UpdateID;
public ShardStatusItem()
{
}
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
var result = doc.CreateElement("Shard-Status");
result.AppendTextNode("Location", "public");
result.AppendTextNode("Name", Name);
result.AppendTextNode("Rank", Rank.ToString());
result.AppendTextNode("Map", Map);
result.AppendTextNode("Status", Status.ToString());
result.AppendTextNode("Id", Id.ToString());
return result;
}
public void Parse(System.Xml.XmlElement element)
{
this.Name = element.ReadTextNode("Name");
this.Rank = int.Parse(element.ReadTextNode("Rank"));
this.Map = element.ReadTextNode("Map");
this.Status = (ShardStatus)Enum.Parse(typeof(ShardStatus), element.ReadTextNode("Status"));
this.Id = int.Parse(element.ReadTextNode("Id"));
}
}
}

View file

@ -0,0 +1,30 @@
using FSO.Common.Utils;
namespace FSO.Server.Protocol.CitySelector
{
public class UserAuthorized : IXMLEntity
{
public string FSOVersion;
public string FSOBranch;
public string FSOUpdateUrl;
public string FSOCDNUrl;
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
var element = doc.CreateElement("User-Authorized");
element.AppendTextNode("FSO-Version", FSOVersion);
element.AppendTextNode("FSO-Branch", FSOBranch);
element.AppendTextNode("FSO-UpdateUrl", FSOUpdateUrl);
element.AppendTextNode("FSO-CDNUrl", FSOCDNUrl);
return element;
}
public void Parse(System.Xml.XmlElement element)
{
this.FSOVersion = element.ReadTextNode("FSO-Version");
this.FSOBranch = element.ReadTextNode("FSO-Branch");
this.FSOUpdateUrl = element.ReadTextNode("FSO-UpdateUrl");
this.FSOCDNUrl = element.ReadTextNode("FSO-CDNUrl");
}
}
}

View file

@ -0,0 +1,38 @@
using FSO.Common.Utils;
using System;
namespace FSO.Server.Protocol.CitySelector
{
public class XMLErrorMessage : IXMLEntity
{
public String Code;
public String Message;
public XMLErrorMessage(){
}
public XMLErrorMessage(String code, String message)
{
this.Code = code;
this.Message = message;
}
#region IXMLPrinter Members
public System.Xml.XmlElement Serialize(System.Xml.XmlDocument doc)
{
var element = doc.CreateElement("Error-Message");
element.AppendTextNode("Error-Number", Code);
element.AppendTextNode("Error", Message);
return element;
}
public void Parse(System.Xml.XmlElement element)
{
this.Code = element.ReadTextNode("Error-Number");
this.Message = element.ReadTextNode("Error");
}
#endregion
}
}

View file

@ -0,0 +1,19 @@
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Electron
{
public abstract class AbstractElectronPacket : IElectronPacket
{
public static IoBuffer Allocate(int size)
{
IoBuffer buffer = IoBuffer.Allocate(size);
buffer.Order = ByteOrder.BigEndian;
return buffer;
}
public abstract ElectronPacketType GetPacketType();
public abstract void Deserialize(IoBuffer input, ISerializationContext context);
public abstract void Serialize(IoBuffer output, ISerializationContext context);
}
}

View file

@ -0,0 +1,51 @@
using System;
namespace FSO.Server.Protocol.Electron
{
public enum ElectronPacketType : ushort
{
CreateASimResponse = 1,
PurchaseLotRequest,
PurchaseLotResponse,
InstantMessage,
FindLotRequest,
FindLotResponse,
FSOVMTickBroadcast,
FSOVMDirectToClient,
FSOVMCommand,
FindAvatarRequest,
FindAvatarResponse,
ChangeRoommateRequest,
KeepAlive,
ChangeRoommateResponse,
ModerationRequest,
FSOVMProtocolMessage,
AvatarRetireRequest,
MailRequest,
MailResponse,
NhoodRequest,
NhoodResponse,
NhoodCandidateList,
BulletinRequest,
BulletinResponse,
GlobalTuningUpdate,
Unknown = 0xFFFF
}
public static class ElectronPacketTypeUtils
{
public static ElectronPacketType FromPacketCode(ushort code)
{
var result = (ElectronPacketType)code;
if (Enum.IsDefined(typeof(ElectronPacketType), result))
return result;
else
return ElectronPacketType.Unknown;
}
public static ushort GetPacketCode(this ElectronPacketType type)
{
return (ushort)type;
}
}
}

View file

@ -0,0 +1,60 @@
using FSO.Server.Protocol.Electron.Packets;
using System;
using System.Collections.Generic;
namespace FSO.Server.Protocol.Electron
{
public class ElectronPackets
{
public static Dictionary<ushort, Type> ELECTRON_PACKET_BY_TYPEID;
public static Type[] ELECTRON_PACKETS = new Type[] {
typeof(CreateASimResponse),
typeof(PurchaseLotRequest),
typeof(PurchaseLotResponse),
typeof(InstantMessage),
typeof(FindLotRequest),
typeof(FindLotResponse),
typeof(FSOVMTickBroadcast),
typeof(FSOVMDirectToClient),
typeof(FSOVMCommand),
typeof(FindAvatarRequest),
typeof(FindAvatarResponse),
typeof(ChangeRoommateRequest),
typeof(KeepAlive),
typeof(ChangeRoommateResponse),
typeof(ModerationRequest),
typeof(FSOVMProtocolMessage),
typeof(AvatarRetireRequest),
typeof(MailRequest),
typeof(MailResponse),
typeof(NhoodRequest),
typeof(NhoodResponse),
typeof(NhoodCandidateList),
typeof(BulletinRequest),
typeof(BulletinResponse),
typeof(GlobalTuningUpdate)
};
static ElectronPackets()
{
ELECTRON_PACKET_BY_TYPEID = new Dictionary<ushort, Type>();
foreach (Type packetType in ELECTRON_PACKETS)
{
IElectronPacket packet = (IElectronPacket)Activator.CreateInstance(packetType);
ELECTRON_PACKET_BY_TYPEID.Add(packet.GetPacketType().GetPacketCode(), packetType);
}
}
public static Type GetByPacketCode(ushort code)
{
if (ELECTRON_PACKET_BY_TYPEID.ContainsKey(code))
{
return ELECTRON_PACKET_BY_TYPEID[code];
}
else
{
return null;
}
}
}
}

View file

@ -0,0 +1,9 @@
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Electron
{
public interface IElectronPacket : IoBufferDeserializable, IoBufferSerializable
{
ElectronPacketType GetPacketType();
}
}

View file

@ -0,0 +1,33 @@
namespace FSO.Server.Protocol.Electron.Model
{
public enum ChangeRoommateResponseStatus : byte
{
INVITE_SUCCESS = 0,
//invite codes
ROOMIE_ELSEWHERE = 1,
TOO_MANY_ROOMMATES = 2,
OTHER_INVITE_PENDING = 3,
//shared
YOU_ARE_NOT_OWNER = 4, //everything but move out
//kick or move out
YOU_ARE_NOT_ROOMMATE = 5,
LOT_MUST_BE_CLOSED = 6, //move out of lot with 1 roommate
//invite response
LOT_DOESNT_EXIST = 7,
NO_INVITE_PENDING = 8,
ACCEPT_SUCCESS = 9,
DECLINE_SUCCESS = 10,
KICK_SUCCESS = 11,
SELFKICK_SUCCESS = 12,
ROOMMATE_LEFT = 13,
GOT_KICKED = 14,
UNKNOWN = 255
}
}

View file

@ -0,0 +1,11 @@
namespace FSO.Server.Protocol.Electron.Model
{
public enum ChangeRoommateType : byte
{
INVITE = 0,
KICK = 1,
ACCEPT = 2,
DECLINE = 3,
POLL = 4
}
}

View file

@ -0,0 +1,11 @@
namespace FSO.Server.Protocol.Electron.Model
{
public enum FindAvatarResponseStatus
{
FOUND,
IGNORING_THEM,
IGNORING_YOU,
NOT_ON_LOT,
PRIVACY_ENABLED,
}
}

View file

@ -0,0 +1,14 @@
namespace FSO.Server.Protocol.Electron.Model
{
public enum FindLotResponseStatus
{
FOUND,
NO_SUCH_LOT,
NOT_OPEN,
NOT_PERMITTED_TO_OPEN,
CLAIM_FAILED,
NO_CAPACITY,
NO_ADMIT,
UNKNOWN_ERROR
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.Electron.Model
{
public interface IActionRequest
{
object OType { get; }
bool NeedsValidation { get; }
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.Electron.Model
{
public interface IActionResponse
{
bool Success { get; }
object OCode { get; }
}
}

View file

@ -0,0 +1,9 @@
namespace FSO.Server.Protocol.Electron.Model
{
public enum ModerationRequestType
{
IPBAN_USER,
BAN_USER,
KICK_USER
}
}

View file

@ -0,0 +1,23 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class AvatarRetireRequest : AbstractElectronPacket
{
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.AvatarRetireRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(0);
}
}
}

View file

@ -0,0 +1,69 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class BulletinRequest : AbstractElectronPacket, IActionRequest
{
public BulletinRequestType Type;
public uint TargetNHood; //the bulletin board to use
//post message
public string Title = "";
public string Message = "";
public uint LotID; //0 if empty - optionally reference a lot in this bulletin post
//post message, delete message, promote message
public uint Value; //bulletin type if post, bulletin ID otherwise.
public object OType => Type;
public bool NeedsValidation => false; //the CAN POST items are one off requests, rather than a state machine.
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<BulletinRequestType>();
TargetNHood = input.GetUInt32();
if (Type == BulletinRequestType.POST_MESSAGE || Type == BulletinRequestType.POST_SYSTEM_MESSAGE)
{
Title = input.GetPascalString();
Message = input.GetPascalString();
LotID = input.GetUInt32();
}
Value = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.BulletinRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutUInt32(TargetNHood);
if (Type == BulletinRequestType.POST_MESSAGE || Type == BulletinRequestType.POST_SYSTEM_MESSAGE)
{
output.PutPascalString(Title);
output.PutPascalString(Message);
output.PutUInt32(LotID);
}
output.PutUInt32(Value);
}
}
public enum BulletinRequestType : byte
{
GET_MESSAGES = 0,
POST_MESSAGE,
PROMOTE_MESSAGE, //mayor/admin only.
CAN_POST_MESSAGE,
CAN_POST_SYSTEM_MESSAGE,
//admin
POST_SYSTEM_MESSAGE,
DELETE_MESSAGE,
}
}

View file

@ -0,0 +1,90 @@
using FSO.Common.Serialization;
using FSO.Files.Formats.tsodata;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
using System.IO;
namespace FSO.Server.Protocol.Electron.Packets
{
public class BulletinResponse : AbstractElectronPacket, IActionResponse
{
public BulletinResponseType Type;
public BulletinItem[] Messages = new BulletinItem[0];
public string Message;
public uint BanEndDate;
public bool Success => Type == BulletinResponseType.SUCCESS || Type == BulletinResponseType.SEND_SUCCESS || Type == BulletinResponseType.MESSAGES;
public object OCode => Type;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<BulletinResponseType>();
var numMessages = input.GetInt32();
Messages = new BulletinItem[numMessages];
for (int j = 0; j < numMessages; j++)
{
var length = input.GetInt32();
var dat = new byte[length];
for (int i = 0; i < length; i++)
{
dat[i] = input.Get();
}
using (var str = new MemoryStream(dat))
{
Messages[j] = new BulletinItem(str);
}
}
Message = input.GetPascalVLCString();
BanEndDate = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.BulletinResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutInt32(Messages.Length);
foreach (var msg in Messages)
{
byte[] dat;
using (var str = new MemoryStream())
{
msg.Save(str);
dat = str.ToArray();
}
output.PutInt32(dat.Length);
foreach (var b in dat)
output.Put(b);
}
output.PutPascalVLCString(Message);
output.PutUInt32(BanEndDate);
}
}
public enum BulletinResponseType
{
MESSAGES,
SEND_SUCCESS, //returns message you sent, with dbid
SUCCESS,
SEND_FAIL_NON_RESIDENT,
SEND_FAIL_BAD_PERMISSION,
SEND_FAIL_INVALID_LOT,
SEND_FAIL_INVALID_MESSAGE,
SEND_FAIL_INVALID_TITLE,
SEND_FAIL_GAMEPLAY_BAN,
SEND_FAIL_TOO_FREQUENT,
SEND_FAIL_JUST_MOVED,
FAIL_MESSAGE_DOESNT_EXIST,
FAIL_NOT_MAYOR,
FAIL_CANT_DELETE,
FAIL_ALREADY_PROMOTED,
FAIL_BAD_PERMISSION,
CANCEL = 0xFE,
FAIL_UNKNOWN = 0xFF
}
}

View file

@ -0,0 +1,32 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class ChangeRoommateRequest : AbstractElectronPacket
{
public ChangeRoommateType Type;
public uint AvatarId;
public uint LotLocation;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<ChangeRoommateType>();
AvatarId = input.GetUInt32();
LotLocation = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.ChangeRoommateRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutUInt32(AvatarId);
output.PutUInt32(LotLocation);
}
}
}

View file

@ -0,0 +1,29 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class ChangeRoommateResponse : AbstractElectronPacket
{
public ChangeRoommateResponseStatus Type;
public uint Extra;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<ChangeRoommateResponseStatus>();
Extra = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.ChangeRoommateResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutUInt32(Extra);
}
}
}

View file

@ -0,0 +1,47 @@
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Electron.Packets
{
public class CreateASimResponse : AbstractElectronPacket
{
public CreateASimStatus Status { get; set; }
public CreateASimFailureReason Reason { get; set; } = CreateASimFailureReason.NONE;
public uint NewAvatarId { get; set; }
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.CreateASimResponse;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Status = input.GetEnum<CreateASimStatus>();
Reason = input.GetEnum<CreateASimFailureReason>();
NewAvatarId = input.GetUInt32();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum<CreateASimStatus>(Status);
output.PutEnum<CreateASimFailureReason>(Reason);
output.PutUInt32(NewAvatarId);
}
}
public enum CreateASimStatus
{
SUCCESS = 0x01,
FAILED = 0x02
}
public enum CreateASimFailureReason
{
NONE = 0x00,
NAME_TAKEN = 0x01,
NAME_VALIDATION_ERROR = 0x02,
DESC_VALIDATION_ERROR = 0x03,
BODY_VALIDATION_ERROR = 0x04,
HEAD_VALIDATION_ERROR = 0x05
}
}

View file

@ -0,0 +1,28 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FSOVMCommand : AbstractElectronPacket
{
public byte[] Data;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
var dataLen = input.GetInt32(); //TODO: limits? 4MB is probably reasonable.
Data = new byte[dataLen];
input.Get(Data, 0, dataLen);
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FSOVMCommand;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutInt32(Data.Length);
output.Put(Data, 0, Data.Length);
}
}
}

View file

@ -0,0 +1,28 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FSOVMDirectToClient : AbstractElectronPacket
{
public byte[] Data;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
var dataLen = input.GetInt32(); //TODO: limits? 4MB is probably reasonable.
Data = new byte[dataLen];
input.Get(Data, 0, dataLen);
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FSOVMDirectToClient;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutInt32(Data.Length);
output.Put(Data, 0, Data.Length);
}
}
}

View file

@ -0,0 +1,43 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FSOVMProtocolMessage : AbstractElectronPacket
{
public bool UseCst;
public string Title;
public string Message;
public FSOVMProtocolMessage()
{
}
public FSOVMProtocolMessage(bool cst, string title, string body)
{
UseCst = cst;
Title = title;
Message = body;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
UseCst = input.GetBool();
Title = input.GetPascalVLCString();
Message = input.GetPascalVLCString();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FSOVMProtocolMessage;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutBool(UseCst);
output.PutPascalVLCString(Title);
output.PutPascalVLCString(Message);
}
}
}

View file

@ -0,0 +1,28 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FSOVMTickBroadcast : AbstractElectronPacket
{
public byte[] Data;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
var dataLen = input.GetInt32(); //TODO: limits? 4MB is probably reasonable.
Data = new byte[dataLen];
input.Get(Data, 0, dataLen);
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FSOVMTickBroadcast;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutInt32(Data.Length);
output.Put(Data, 0, Data.Length);
}
}
}

View file

@ -0,0 +1,25 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FindAvatarRequest : AbstractElectronPacket
{
public uint AvatarId;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
AvatarId = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FindAvatarRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(AvatarId);
}
}
}

View file

@ -0,0 +1,32 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Server.Protocol.Electron.Model;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FindAvatarResponse : AbstractElectronPacket
{
public uint AvatarId;
public FindAvatarResponseStatus Status;
public uint LotId; //0 if status is not FOUND.
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
AvatarId = input.GetUInt32();
Status = input.GetEnum<FindAvatarResponseStatus>();
LotId = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FindAvatarResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(AvatarId);
output.PutEnum(Status);
output.PutUInt32(LotId);
}
}
}

View file

@ -0,0 +1,28 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FindLotRequest : AbstractElectronPacket
{
public uint LotId;
public bool OpenIfClosed;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
LotId = input.GetUInt32();
OpenIfClosed = input.GetBool();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FindLotRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(LotId);
output.PutBool(OpenIfClosed);
}
}
}

View file

@ -0,0 +1,38 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Server.Protocol.Electron.Model;
namespace FSO.Server.Protocol.Electron.Packets
{
public class FindLotResponse : AbstractElectronPacket
{
public FindLotResponseStatus Status;
public uint LotId;
public string LotServerTicket;
public string Address;
public string User;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Status = input.GetEnum<FindLotResponseStatus>();
LotId = input.GetUInt32();
LotServerTicket = input.GetPascalVLCString();
Address = input.GetPascalVLCString();
User = input.GetPascalVLCString();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.FindLotResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Status);
output.PutUInt32(LotId);
output.PutPascalVLCString(LotServerTicket);
output.PutPascalVLCString(Address);
output.PutPascalVLCString(User);
}
}
}

View file

@ -0,0 +1,54 @@
using FSO.Common.Model;
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using System;
using System.IO;
namespace FSO.Server.Protocol.Electron.Packets
{
public class GlobalTuningUpdate : AbstractElectronPacket
{
public DynamicTuning Tuning;
public byte[] ObjectUpgrades;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
var dataLen = input.GetInt32();
if (dataLen > 4000000 || dataLen > input.Remaining) throw new Exception("Tuning too long");
var data = new byte[dataLen];
input.Get(data, 0, dataLen);
using (var mem = new MemoryStream(data))
{
using (var reader = new BinaryReader(mem))
{
Tuning = new DynamicTuning(reader);
}
}
var upgLen = input.GetInt32();
if (upgLen > 10000000 || upgLen > input.Remaining) throw new Exception("Upgrades too long");
ObjectUpgrades = new byte[upgLen];
input.Get(ObjectUpgrades, 0, upgLen);
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.GlobalTuningUpdate;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
using (var mem = new MemoryStream())
{
using (var writer = new BinaryWriter(mem))
{
Tuning.SerializeInto(writer);
var result = mem.ToArray();
output.PutInt32(result.Length);
output.Put(result, 0, result.Length);
}
}
output.PutInt32(ObjectUpgrades.Length);
output.Put(ObjectUpgrades, 0, ObjectUpgrades.Length);
}
}
}

View file

@ -0,0 +1,63 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Common.Enum;
namespace FSO.Server.Protocol.Electron.Packets
{
public class InstantMessage : AbstractElectronPacket
{
public UserReferenceType FromType;
public uint From;
public uint To;
public InstantMessageType Type;
public string Message;
public string AckID;
public InstantMessageFailureReason Reason = InstantMessageFailureReason.NONE;
public uint Color;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
FromType = input.GetEnum<UserReferenceType>();
From = input.GetUInt32();
To = input.GetUInt32();
Type = input.GetEnum<InstantMessageType>();
Message = input.GetPascalVLCString();
AckID = input.GetPascalVLCString();
Reason = input.GetEnum<InstantMessageFailureReason>();
Color = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.InstantMessage;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(FromType);
output.PutUInt32(From);
output.PutUInt32(To);
output.PutEnum(Type);
output.PutPascalVLCString(Message);
output.PutPascalVLCString(AckID);
output.PutEnum(Reason);
output.PutUInt32(Color);
}
}
public enum InstantMessageType
{
MESSAGE,
SUCCESS_ACK,
FAILURE_ACK
}
public enum InstantMessageFailureReason
{
NONE,
THEY_ARE_OFFLINE,
THEY_ARE_IGNORING_YOU,
YOU_ARE_IGNORING_THEM,
MESSAGE_QUEUE_FULL
}
}

View file

@ -0,0 +1,23 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class KeepAlive : AbstractElectronPacket
{
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.KeepAlive;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
}
}
}

View file

@ -0,0 +1,70 @@
using FSO.Common.Serialization;
using FSO.Files.Formats.tsodata;
using Mina.Core.Buffer;
using System.IO;
namespace FSO.Server.Protocol.Electron.Packets
{
public class MailRequest : AbstractElectronPacket
{
public MailRequestType Type;
public long TimestampID;
public MessageItem Item;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<MailRequestType>();
if (Type == MailRequestType.SEND) {
var length = input.GetInt32();
var dat = new byte[length];
for (int i=0; i<length; i++)
{
dat[i] = input.Get();
}
using (var str = new MemoryStream(dat))
{
Item = new MessageItem(str);
}
} else
{
TimestampID = input.GetInt64();
}
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.MailRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
if (Type == MailRequestType.SEND)
{
byte[] dat;
if (Item == null) dat = new byte[0];
else
{
using (var str = new MemoryStream())
{
Item.Save(str);
dat = str.ToArray();
}
}
output.PutInt32(dat.Length);
foreach (var b in dat)
output.Put(b);
} else
output.PutInt64(TimestampID);
}
}
public enum MailRequestType : byte
{
POLL_INBOX,
SEND,
DELETE
}
}

View file

@ -0,0 +1,69 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Files.Formats.tsodata;
using System.IO;
namespace FSO.Server.Protocol.Electron.Packets
{
public class MailResponse : AbstractElectronPacket
{
public MailResponseType Type;
public MessageItem[] Messages = new MessageItem[0];
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<MailResponseType>();
var numMessages = input.GetInt32();
Messages = new MessageItem[numMessages];
for (int j = 0; j < numMessages; j++)
{
var length = input.GetInt32();
var dat = new byte[length];
for (int i = 0; i < length; i++)
{
dat[i] = input.Get();
}
using (var str = new MemoryStream(dat))
{
Messages[j] = new MessageItem(str);
}
}
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.MailResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutInt32(Messages.Length);
foreach (var msg in Messages)
{
byte[] dat;
using (var str = new MemoryStream())
{
msg.Save(str);
dat = str.ToArray();
}
output.PutInt32(dat.Length);
foreach (var b in dat)
output.Put(b);
}
}
}
public enum MailResponseType
{
POLL_RESPONSE,
NEW_MAIL,
SEND_IGNORING_YOU,
SEND_IGNORING_THEM,
SEND_THROTTLED,
SEND_THROTTLED_DAILY,
SEND_FAILED,
SEND_SUCCESS //returns the message you sent, with its db id
}
}

View file

@ -0,0 +1,29 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Server.Protocol.Electron.Model;
namespace FSO.Server.Protocol.Electron.Packets
{
public class ModerationRequest : AbstractElectronPacket
{
public ModerationRequestType Type;
public uint EntityId;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<ModerationRequestType>();
EntityId = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.ModerationRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutUInt32(EntityId);
}
}
}

View file

@ -0,0 +1,76 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using System.Collections.Generic;
namespace FSO.Server.Protocol.Electron.Packets
{
public class NhoodCandidateList : AbstractElectronPacket
{
public bool NominationMode;
public List<NhoodCandidate> Candidates;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
NominationMode = input.GetBool();
int candCount = input.GetInt32();
Candidates = new List<NhoodCandidate>();
for (int i=0; i<candCount; i++)
{
var candidate = new NhoodCandidate()
{
ID = input.GetUInt32(),
Name = input.GetPascalVLCString(),
Rating = input.GetUInt32()
};
if (!NominationMode)
{
candidate.LastNhoodName = input.GetPascalVLCString();
candidate.LastNhoodID = input.GetUInt32();
candidate.TermNumber = input.GetUInt32();
candidate.Message = input.GetPascalVLCString();
}
Candidates.Add(candidate);
}
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.NhoodCandidateList;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutBool(NominationMode);
output.PutInt32(Candidates.Count);
foreach (var candidate in Candidates)
{
output.PutUInt32(candidate.ID);
output.PutPascalVLCString(candidate.Name);
output.PutUInt32(candidate.Rating);
if (!NominationMode)
{
output.PutPascalVLCString(candidate.LastNhoodName);
output.PutUInt32(candidate.LastNhoodID);
output.PutUInt32(candidate.TermNumber);
output.PutPascalVLCString(candidate.Message);
}
}
}
}
public class NhoodCandidate
{
public uint ID;
public string Name;
public uint Rating;
public string LastNhoodName = "";
public uint LastNhoodID;
public uint TermNumber;
public string Message = "";
}
}

View file

@ -0,0 +1,71 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class NhoodRequest : AbstractElectronPacket, IActionRequest
{
public NhoodRequestType Type;
public uint TargetAvatar; //vote, nominate, rate, delete rate, force mayor, gameplay ban, add candidate, remove candidate
public uint TargetNHood; //vote, nominate, add candidate, remove candidate
public object OType => Type;
public bool NeedsValidation =>
Type == NhoodRequestType.CAN_NOMINATE || Type == NhoodRequestType.CAN_RATE
|| Type == NhoodRequestType.CAN_RUN || Type == NhoodRequestType.CAN_VOTE || Type == NhoodRequestType.CAN_FREE_VOTE;
public string Message = ""; //bulletin, rate
public uint Value; //rate (stars), nomination_run (accept if >0)
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<NhoodRequestType>();
TargetAvatar = input.GetUInt32();
TargetNHood = input.GetUInt32();
Message = input.GetPascalString();
Value = input.GetUInt32();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.NhoodRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutUInt32(TargetAvatar);
output.PutUInt32(TargetNHood);
output.PutPascalString(Message);
output.PutUInt32(Value);
}
}
public enum NhoodRequestType : byte
{
VOTE = 0,
CAN_VOTE,
NOMINATE,
CAN_NOMINATE,
RATE,
CAN_RATE,
NOMINATION_RUN,
CAN_RUN,
CAN_FREE_VOTE,
FREE_VOTE,
//moderator commands
DELETE_RATE,
FORCE_MAYOR,
REMOVE_CANDIDATE,
ADD_CANDIDATE,
TEST_ELECTION, //nhood id, avatar id = state (over/nomination/election), value = end date in x days
PRETEND_DATE, //run the daily nhood task as if it's (value) date. (Epoch value)
NHOOD_GAMEPLAY_BAN
}
}

View file

@ -0,0 +1,81 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Electron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class NhoodResponse : AbstractElectronPacket, IActionResponse
{
public NhoodResponseCode Code;
public uint BanEndDate;
public string Message = "";
public bool Success => Code == NhoodResponseCode.SUCCESS;
public object OCode => Code;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Code = input.GetEnum<NhoodResponseCode>();
BanEndDate = input.GetUInt32();
Message = input.GetPascalVLCString();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.NhoodResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Code);
output.PutUInt32(BanEndDate);
output.PutPascalVLCString(Message);
}
}
public enum NhoodResponseCode : byte
{
SUCCESS = 0x00,
//nominate/vote
NOT_IN_NHOOD = 0x01,
ELECTION_OVER = 0x02,
CANDIDATE_NOT_IN_NHOOD = 0x03,
CANDIDATE_NOT_NOMINATED = 0x04,
ALREADY_VOTED = 0x05,
ALREADY_VOTED_SAME_IP = 0x06,
BAD_STATE = 0x07,
//rate
NOT_YOUR_MAYOR = 0x08,
INVALID_RATING = 0x09,
CANT_RATE_AVATAR = 0x0A,
//accept or decline a nomination
NOBODY_NOMINATED_YOU_IDIOT = 0x0B,
ALREADY_RUNNING = 0x0C,
BAD_COMMENT = 0x0D,
//moderator actions
NOT_MODERATOR = 0x0E,
INVALID_AVATAR = 0x0F,
INVALID_NHOOD = 0x10,
//shared
NHOOD_GAMEPLAY_BAN = 0x11,
CANDIDATE_MOVED_RECENTLY = 0x12,
YOU_MOVED_RECENTLY = 0x13,
CANDIDATE_NHOOD_GAMEPLAY_BAN = 0x14,
MISSING_ENTITY = 0x15, //missing someone
//free vote
NHOOD_NO_ELECTION = 0x16,
ALREADY_ENROLLED_FOR_FREE_VOTE = 0x17,
FREE_VOTE_ALREADY_ELIGIBLE = 0x18,
FREE_VOTE_MOVE_DATE = 0x19,
FREE_VOTE_ELECTION_OVER = 0x1A,
CANCEL = 0xFE,
UNKNOWN_ERROR = 0xFF
};
}

View file

@ -0,0 +1,37 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class PurchaseLotRequest : AbstractElectronPacket
{
public ushort LotLocation_X;
public ushort LotLocation_Y;
public string Name;
public bool StartFresh;
public bool MayorMode;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
LotLocation_X = input.GetUInt16();
LotLocation_Y = input.GetUInt16();
Name = input.GetPascalString();
StartFresh = input.GetBool();
MayorMode = input.GetBool();
}
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.PurchaseLotRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt16(LotLocation_X);
output.PutUInt16(LotLocation_Y);
output.PutPascalString(Name);
output.PutBool(StartFresh);
output.PutBool(MayorMode);
}
}
}

View file

@ -0,0 +1,60 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Electron.Packets
{
public class PurchaseLotResponse : AbstractElectronPacket
{
public PurchaseLotStatus Status { get; set; }
public PurchaseLotFailureReason Reason { get; set; } = PurchaseLotFailureReason.NONE;
public uint NewLotId { get; set; }
public int NewFunds { get; set; }
public override ElectronPacketType GetPacketType()
{
return ElectronPacketType.PurchaseLotResponse;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Status = input.GetEnum<PurchaseLotStatus>();
Reason = input.GetEnum<PurchaseLotFailureReason>();
NewLotId = input.GetUInt32();
NewFunds = input.GetInt32();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum<PurchaseLotStatus>(Status);
output.PutEnum<PurchaseLotFailureReason>(Reason);
output.PutUInt32(NewLotId);
output.PutInt32(NewFunds);
}
}
public enum PurchaseLotStatus
{
SUCCESS = 0x01,
FAILED = 0x02
}
public enum PurchaseLotFailureReason
{
NONE = 0x00,
NAME_TAKEN = 0x01,
NAME_VALIDATION_ERROR = 0x02,
INSUFFICIENT_FUNDS = 0x03,
LOT_TAKEN = 0x04,
LOT_NOT_PURCHASABLE = 0x05,
IN_LOT_CANT_EVICT = 0x06,
NOT_OFFLINE_FOR_MOVE = 0x07,
LOCATION_TAKEN = 0x08,
NHOOD_RESERVED = 0x09,
TH_NOT_MAYOR = 0x80,
TH_INCORRECT_NHOOD = 0x81,
UNKNOWN = 0xFF
}
}

View file

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A08ADE32-27E2-44F4-BC52-11A16C56BAA8}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FSO.Server.Protocol</RootNamespace>
<AssemblyName>FSO.Server.Protocol</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ServerRelease|AnyCPU'">
<OutputPath>bin\ServerRelease\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=3.4.1.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Logging.3.4.1\lib\net40\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="Common.Logging.Core, Version=3.4.1.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Logging.Core.3.4.1\lib\net40\Common.Logging.Core.dll</HintPath>
</Reference>
<Reference Include="Mina.NET, Version=2.0.11.0, Culture=neutral, PublicKeyToken=bc4dde96e5154fe5, processorArchitecture=MSIL">
<HintPath>..\packages\Mina.2.0.11\lib\net40\Mina.NET.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=3.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.Ninject.3.3.1\lib\net40-client\Ninject.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.5.7\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Aries\AriesPackets.cs" />
<Compile Include="Aries\AriesProtocol.cs" />
<Compile Include="Aries\AriesProtocolDecoder.cs" />
<Compile Include="Aries\AriesProtocolEncoder.cs" />
<Compile Include="Aries\AriesProtocolLogger.cs" />
<Compile Include="Aries\AriesPacketType.cs" />
<Compile Include="Aries\IAriesPacket.cs" />
<Compile Include="Aries\Packets\AnswerAccepted.cs" />
<Compile Include="Aries\Packets\AnswerChallenge.cs" />
<Compile Include="Aries\Packets\RequestChallenge.cs" />
<Compile Include="Aries\Packets\RequestChallengeResponse.cs" />
<Compile Include="Aries\Packets\RequestClientSession.cs" />
<Compile Include="Aries\Packets\RequestClientSessionResponse.cs" />
<Compile Include="Authorization\AuthRequest.cs" />
<Compile Include="Authorization\AuthResult.cs" />
<Compile Include="CitySelector\AvatarAppearanceType.cs" />
<Compile Include="CitySelector\AvatarData.cs" />
<Compile Include="CitySelector\AvatarGender.cs" />
<Compile Include="CitySelector\InitialConnectServletRequest.cs" />
<Compile Include="CitySelector\InitialConnectServletResult.cs" />
<Compile Include="CitySelector\ShardSelectorServletRequest.cs" />
<Compile Include="CitySelector\ShardSelectorServletResponse.cs" />
<Compile Include="CitySelector\ShardStatus.cs" />
<Compile Include="CitySelector\ShardStatusItem.cs" />
<Compile Include="CitySelector\UserAuthorized.cs" />
<Compile Include="CitySelector\XMLErrorMessage.cs" />
<Compile Include="Electron\AbstractElectronPacket.cs" />
<Compile Include="Electron\ElectronPackets.cs" />
<Compile Include="Electron\ElectronPacketType.cs" />
<Compile Include="Electron\IElectronPacket.cs" />
<Compile Include="Electron\Model\ChangeRoommateResponseStatus.cs" />
<Compile Include="Electron\Model\ChangeRoommateType.cs" />
<Compile Include="Electron\Model\FindAvatarResponseStatus.cs" />
<Compile Include="Electron\Model\FindLotResponseStatus.cs" />
<Compile Include="Electron\Model\IActionRequest.cs" />
<Compile Include="Electron\Model\IActionResponse.cs" />
<Compile Include="Electron\Model\ModerationRequestType.cs" />
<Compile Include="Electron\Packets\AvatarRetireRequest.cs" />
<Compile Include="Electron\Packets\BulletinRequest.cs" />
<Compile Include="Electron\Packets\BulletinResponse.cs" />
<Compile Include="Electron\Packets\ChangeRoommateRequest.cs" />
<Compile Include="Electron\Packets\ChangeRoommateResponse.cs" />
<Compile Include="Electron\Packets\FSOVMProtocolMessage.cs" />
<Compile Include="Electron\Packets\GlobalTuningUpdate.cs" />
<Compile Include="Electron\Packets\MailRequest.cs" />
<Compile Include="Electron\Packets\MailResponse.cs" />
<Compile Include="Electron\Packets\ModerationRequest.cs" />
<Compile Include="Electron\Packets\FindAvatarResponse.cs" />
<Compile Include="Electron\Packets\FindLotResponse.cs" />
<Compile Include="Electron\Packets\CreateASimResponse.cs" />
<Compile Include="Electron\Packets\FindAvatarRequest.cs" />
<Compile Include="Electron\Packets\FSOVMDirectToClient.cs" />
<Compile Include="Electron\Packets\FSOVMCommand.cs" />
<Compile Include="Electron\Packets\FSOVMTickBroadcast.cs" />
<Compile Include="Electron\Packets\InstantMessage.cs" />
<Compile Include="Electron\Packets\FindLotRequest.cs" />
<Compile Include="Electron\Packets\KeepAlive.cs" />
<Compile Include="Electron\Packets\NhoodCandidateList.cs" />
<Compile Include="Electron\Packets\NhoodRequest.cs" />
<Compile Include="Electron\Packets\NhoodResponse.cs" />
<Compile Include="Electron\Packets\PurchaseLotRequest.cs" />
<Compile Include="Electron\Packets\PurchaseLotResponse.cs" />
<Compile Include="Gluon\AbstractElectronPacket.cs" />
<Compile Include="Gluon\GluonPackets.cs" />
<Compile Include="Gluon\GluonPacketType.cs" />
<Compile Include="Gluon\IGluonPacket.cs" />
<Compile Include="Gluon\Model\ChangeType.cs" />
<Compile Include="Gluon\Model\ClaimAction.cs" />
<Compile Include="Gluon\Model\ClaimType.cs" />
<Compile Include="Gluon\Packets\AdvertiseCapacity.cs" />
<Compile Include="Gluon\Packets\CityNotify.cs" />
<Compile Include="Gluon\Packets\HealthPing.cs" />
<Compile Include="Gluon\Packets\HealthPingResponse.cs" />
<Compile Include="Gluon\Packets\IGluonCall.cs" />
<Compile Include="Gluon\Packets\MatchmakerNotify.cs" />
<Compile Include="Gluon\Packets\NotifyLotRoommateChange.cs" />
<Compile Include="Gluon\Packets\RequestLotClientTermination.cs" />
<Compile Include="Gluon\Packets\RequestTask.cs" />
<Compile Include="Gluon\Packets\RequestTaskResponse.cs" />
<Compile Include="Gluon\Packets\SendCityMail.cs" />
<Compile Include="Gluon\Packets\ShardShutdownCompleteResponse.cs" />
<Compile Include="Gluon\Packets\ShardShutdownRequest.cs" />
<Compile Include="Gluon\Packets\TransferClaim.cs" />
<Compile Include="Gluon\Packets\TransferClaimResponse.cs" />
<Compile Include="Gluon\Packets\TuningChanged.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils\ChallengeResponse.cs" />
<Compile Include="Utils\SslClientFilter.cs" />
<Compile Include="Utils\CustomCumulativeProtocolDecoder.cs" />
<Compile Include="Voltron\AbstractVoltronPacket.cs" />
<Compile Include="Voltron\cTSONetMessageParameter.cs" />
<Compile Include="Voltron\DataService\AvatarSkills.cs" />
<Compile Include="Voltron\DataService\Bookmark.cs" />
<Compile Include="Voltron\DataService\Avatar.cs" />
<Compile Include="Voltron\DataService\AvatarAppearance.cs" />
<Compile Include="Voltron\DataService\cITSOProperty.cs" />
<Compile Include="Voltron\DataService\clsid.cs" />
<Compile Include="Voltron\DataService\cTSOSerializer.cs" />
<Compile Include="Voltron\DataService\cTSOValue.cs" />
<Compile Include="Voltron\DataService\Location.cs" />
<Compile Include="Voltron\DataService\Lot.cs" />
<Compile Include="Voltron\Model\Gender.cs" />
<Compile Include="Voltron\Model\RPCRequest.cs" />
<Compile Include="Voltron\IVoltronPacket.cs" />
<Compile Include="Voltron\Model\MagicNumberEnum.cs" />
<Compile Include="Voltron\Model\Sender.cs" />
<Compile Include="Voltron\Model\SkinTone.cs" />
<Compile Include="Voltron\Packets\AnnouncementMsgPDU.cs" />
<Compile Include="Voltron\Packets\ChatMsgPDU.cs" />
<Compile Include="Voltron\Packets\ServerByePDU.cs" />
<Compile Include="Voltron\Packets\ClientByePDU.cs" />
<Compile Include="Voltron\Packets\ClientOnlinePDU.cs" />
<Compile Include="Voltron\Packets\DataServiceWrapperPDU.cs" />
<Compile Include="Voltron\Packets\DBRequestWrapperPDU.cs" />
<Compile Include="Voltron\Packets\FindPlayerPDU.cs" />
<Compile Include="Voltron\Packets\FindPlayerResponsePDU.cs" />
<Compile Include="Voltron\Packets\HostOnlinePDU.cs" />
<Compile Include="Voltron\Packets\OccupantArrivedPDU.cs" />
<Compile Include="Voltron\Packets\RSGZWrapperPDU.cs" />
<Compile Include="Voltron\Packets\SetIgnoreListPDU.cs" />
<Compile Include="Voltron\Packets\SetIgnoreListResponsePDU.cs" />
<Compile Include="Voltron\Packets\SetInvinciblePDU.cs" />
<Compile Include="Voltron\Packets\TransmitCreateAvatarNotificationPDU.cs" />
<Compile Include="Voltron\Packets\VariableVoltronPacket.cs" />
<Compile Include="Voltron\VoltronPackets.cs" />
<Compile Include="Voltron\VoltronPacketType.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\FSO.Server.Common\FSO.Server.Common.csproj">
<Project>{39b61962-fe43-4b64-8e57-8f793737fffe}</Project>
<Name>FSO.Server.Common</Name>
</ProjectReference>
<ProjectReference Include="..\tso.common\FSO.Common.csproj">
<Project>{c42962a1-8796-4f47-9dcd-79ed5904d8ca}</Project>
<Name>FSO.Common</Name>
</ProjectReference>
<ProjectReference Include="..\tso.files\FSO.Files.csproj">
<Project>{18583453-a970-4ac5-83b1-2d6bfdf94c24}</Project>
<Name>FSO.Files</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,36 @@
using System;
using Mina.Core.Buffer;
using FSO.Common.Serialization;
using FSO.Server.Protocol.Gluon.Packets;
namespace FSO.Server.Protocol.Gluon
{
public abstract class AbstractGluonPacket : IGluonPacket
{
public static IoBuffer Allocate(int size)
{
IoBuffer buffer = IoBuffer.Allocate(size);
buffer.Order = ByteOrder.BigEndian;
return buffer;
}
public abstract GluonPacketType GetPacketType();
public abstract void Deserialize(IoBuffer input, ISerializationContext context);
public abstract void Serialize(IoBuffer output, ISerializationContext context);
}
public abstract class AbstractGluonCallPacket : AbstractGluonPacket, IGluonCall
{
public Guid CallId { get; set; }
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutPascalString(CallId.ToString());
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
CallId = Guid.Parse(input.GetPascalString());
}
}
}

View file

@ -0,0 +1,107 @@
namespace FSO.Server.Protocol.Gluon
{
public enum GluonPacketType
{
AdvertiseCapacity,
TransferClaim,
TransferClaimResponse,
RequestLotClientTermination,
ShardShutdownRequest,
ShardShutdownCompleteResponse,
HealthPing,
HealthPingResponse,
RequestTask,
RequestTaskResponse,
NotifyLotRoommateChange,
MatchmakerNotify,
CityNotify,
TuningChanged,
CitySendMail,
Unknown
}
public static class GluonPacketTypeUtils
{
public static GluonPacketType FromPacketCode(ushort code)
{
switch (code)
{
case 0x0001:
return GluonPacketType.AdvertiseCapacity;
case 0x0002:
return GluonPacketType.TransferClaim;
case 0x0003:
return GluonPacketType.TransferClaimResponse;
case 0x0004:
return GluonPacketType.RequestLotClientTermination;
case 0x0005:
return GluonPacketType.ShardShutdownRequest;
case 0x0006:
return GluonPacketType.ShardShutdownCompleteResponse;
case 0x0007:
return GluonPacketType.HealthPing;
case 0x0008:
return GluonPacketType.HealthPingResponse;
case 0x0009:
return GluonPacketType.RequestTask;
case 0x0010:
return GluonPacketType.RequestTaskResponse;
case 0x0011:
return GluonPacketType.NotifyLotRoommateChange;
case 0x0012:
return GluonPacketType.MatchmakerNotify;
case 0x0013:
return GluonPacketType.CityNotify;
case 0x0014:
return GluonPacketType.TuningChanged;
case 0x0015:
return GluonPacketType.CitySendMail;
default:
return GluonPacketType.Unknown;
}
}
public static ushort GetPacketCode(this GluonPacketType type)
{
switch (type)
{
case GluonPacketType.AdvertiseCapacity:
return 0x0001;
case GluonPacketType.TransferClaim:
return 0x0002;
case GluonPacketType.TransferClaimResponse:
return 0x0003;
case GluonPacketType.RequestLotClientTermination:
return 0x0004;
case GluonPacketType.ShardShutdownRequest:
return 0x0005;
case GluonPacketType.ShardShutdownCompleteResponse:
return 0x0006;
case GluonPacketType.HealthPing:
return 0x0007;
case GluonPacketType.HealthPingResponse:
return 0x0008;
case GluonPacketType.RequestTask:
return 0x0009;
case GluonPacketType.RequestTaskResponse:
return 0x0010;
case GluonPacketType.NotifyLotRoommateChange:
return 0x0011;
case GluonPacketType.MatchmakerNotify:
return 0x0012;
case GluonPacketType.CityNotify:
return 0x0013;
case GluonPacketType.TuningChanged:
return 0x0014;
case GluonPacketType.CitySendMail:
return 0x0015;
}
return 0xFFFF;
}
}
}

View file

@ -0,0 +1,50 @@
using FSO.Server.Protocol.Gluon.Packets;
using System;
using System.Collections.Generic;
namespace FSO.Server.Protocol.Gluon
{
public class GluonPackets
{
public static Dictionary<ushort, Type> GLUON_PACKET_BY_TYPEID;
public static Type[] ELECTRON_PACKETS = new Type[] {
typeof(AdvertiseCapacity),
typeof(TransferClaim),
typeof(TransferClaimResponse),
typeof(RequestLotClientTermination),
typeof(ShardShutdownRequest),
typeof(ShardShutdownCompleteResponse),
typeof(HealthPing),
typeof(HealthPingResponse),
typeof(RequestTask),
typeof(RequestTaskResponse),
typeof(NotifyLotRoommateChange),
typeof(MatchmakerNotify),
typeof(CityNotify),
typeof(TuningChanged),
typeof(SendCityMail)
};
static GluonPackets()
{
GLUON_PACKET_BY_TYPEID = new Dictionary<ushort, Type>();
foreach (Type packetType in ELECTRON_PACKETS)
{
IGluonPacket packet = (IGluonPacket)Activator.CreateInstance(packetType);
GLUON_PACKET_BY_TYPEID.Add(packet.GetPacketType().GetPacketCode(), packetType);
}
}
public static Type GetByPacketCode(ushort code)
{
if (GLUON_PACKET_BY_TYPEID.ContainsKey(code))
{
return GLUON_PACKET_BY_TYPEID[code];
}
else
{
return null;
}
}
}
}

View file

@ -0,0 +1,9 @@
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Gluon
{
public interface IGluonPacket : IoBufferDeserializable, IoBufferSerializable
{
GluonPacketType GetPacketType();
}
}

View file

@ -0,0 +1,11 @@
namespace FSO.Server.Protocol.Gluon.Model
{
public enum ChangeType
{
REMOVE_ROOMMATE,
ADD_ROOMMATE,
BECOME_OWNER,
BECOME_OWNER_WITH_OBJECTS,
ROOMIE_INHERIT_OBJECTS_ONLY
}
}

View file

@ -0,0 +1,10 @@
namespace FSO.Server.Protocol.Gluon.Model
{
public enum ClaimAction
{
DEFAULT,
LOT_HOST,
LOT_CLEANUP
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.Gluon.Model
{
public enum ClaimType
{
LOT,
AVATAR
}
}

View file

@ -0,0 +1,37 @@
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class AdvertiseCapacity : AbstractGluonPacket
{
public short MaxLots;
public short CurrentLots;
public byte CpuPercentAvg;
public long RamUsed;
public long RamAvaliable;
public override GluonPacketType GetPacketType()
{
return GluonPacketType.AdvertiseCapacity;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
MaxLots = input.GetInt16();
CurrentLots = input.GetInt16();
CpuPercentAvg = input.Get();
RamUsed = input.GetInt64();
RamAvaliable = input.GetInt64();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutInt16(MaxLots);
output.PutInt16(CurrentLots);
output.Put(CpuPercentAvg);
output.PutInt64(RamUsed);
output.PutInt64(RamAvaliable);
}
}
}

View file

@ -0,0 +1,45 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
// Task -> City notifications.
namespace FSO.Server.Protocol.Gluon.Packets
{
public class CityNotify : AbstractGluonPacket
{
public CityNotifyType Mode;
public uint Value;
public string Message = "";
public CityNotify() { }
public CityNotify(CityNotifyType mode)
{
Mode = mode;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Mode = input.GetEnum<CityNotifyType>();
Value = input.GetUInt32();
Message = input.GetPascalVLCString();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.CityNotify;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Mode);
output.PutUInt32(Value);
output.PutPascalVLCString(Message);
}
}
public enum CityNotifyType : byte
{
NhoodUpdate = 1
}
}

View file

@ -0,0 +1,23 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class HealthPing : AbstractGluonCallPacket
{
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.HealthPing;
}
}
}

View file

@ -0,0 +1,27 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class HealthPingResponse : AbstractGluonCallPacket
{
public string PoolHash { get; set; }
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
PoolHash = input.GetPascalString();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
output.PutPascalString(PoolHash);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.HealthPingResponse;
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace FSO.Server.Protocol.Gluon.Packets
{
public interface IGluonCall
{
Guid CallId { get; set; }
}
}

View file

@ -0,0 +1,40 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
/// <summary>
/// Lot -> City server messages used to notify the matchmaker about some change to lot state.
/// (currently only when an avatar leaves a lot. this frees up a space for the matchmaker to shove someone else in)
/// </summary>
public class MatchmakerNotify : AbstractGluonPacket
{
public MatchmakerNotifyType Mode;
public uint LotID;
public uint AvatarID;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Mode = input.GetEnum<MatchmakerNotifyType>();
LotID = input.GetUInt32();
AvatarID = input.GetUInt32();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.MatchmakerNotify;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Mode);
output.PutUInt32(LotID);
output.PutUInt32(AvatarID);
}
}
public enum MatchmakerNotifyType : byte
{
RemoveAvatar = 1
}
}

View file

@ -0,0 +1,39 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Gluon.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
/// <summary>
/// A signal sent from the city server to notify a lot that an avatar's roommate status on that lot has changed.
/// May wish to change this to be more generic for further avatar related changes in future.
/// </summary>
public class NotifyLotRoommateChange : AbstractGluonPacket
{
public uint AvatarId;
public uint ReplaceId;
public int LotId;
public ChangeType Change;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
AvatarId = input.GetUInt32();
ReplaceId = input.GetUInt32();
LotId = input.GetInt32();
Change = input.GetEnum<ChangeType>();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.NotifyLotRoommateChange;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(AvatarId);
output.PutUInt32(ReplaceId);
output.PutInt32(LotId);
output.PutEnum(Change);
}
}
}

View file

@ -0,0 +1,31 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class RequestLotClientTermination : AbstractGluonPacket
{
public uint AvatarId;
public int LotId;
public string FromOwner;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
AvatarId = input.GetUInt32();
LotId = input.GetInt32();
FromOwner = input.GetPascalString();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.RequestLotClientTermination;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(AvatarId);
output.PutInt32(LotId);
output.PutPascalString(FromOwner);
}
}
}

View file

@ -0,0 +1,34 @@
using System.Text;
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class RequestTask : AbstractGluonCallPacket
{
public string TaskType { get; set; }
public string ParameterJson { get; set; }
public int ShardId { get; set; }
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
TaskType = input.GetPascalString();
ShardId = input.GetInt32();
ParameterJson = input.GetString(Encoding.UTF8);
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
output.PutPascalString(TaskType);
output.PutInt32(ShardId);
output.PutString(ParameterJson, Encoding.UTF8);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.RequestTask;
}
}
}

View file

@ -0,0 +1,27 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class RequestTaskResponse : AbstractGluonCallPacket
{
public int TaskId;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
TaskId = input.GetInt32();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
output.PutInt32(TaskId);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.RequestTaskResponse;
}
}
}

View file

@ -0,0 +1,58 @@
using FSO.Common.Serialization;
using FSO.Files.Formats.tsodata;
using Mina.Core.Buffer;
using System.Collections.Generic;
using System.IO;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class SendCityMail : AbstractGluonCallPacket
{
public List<MessageItem> Items { get; set; }
public SendCityMail() { }
public SendCityMail(List<MessageItem> items) {
Items = items;
}
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
var itemCount = input.GetInt32();
var dataSize = input.GetInt32();
var data = input.GetSlice(dataSize).GetBytes();
using (var mem = new MemoryStream(data)) {
Items = new List<MessageItem>();
for (int i = 0; i < itemCount; i++)
{
var message = new MessageItem();
message.Read(mem);
Items.Add(message);
}
}
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
byte[] data = null;
using (var mem = new MemoryStream())
{
foreach (var item in Items)
{
item.Save(mem);
}
data = mem.ToArray();
}
output.PutInt32(Items.Count);
output.PutInt32(data.Length);
output.Put(data);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.CitySendMail;
}
}
}

View file

@ -0,0 +1,25 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class ShardShutdownCompleteResponse : AbstractGluonPacket
{
public uint ShardId;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
ShardId = input.GetUInt32();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.ShardShutdownCompleteResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(ShardId);
}
}
}

View file

@ -0,0 +1,29 @@
using FSO.Common.Serialization;
using FSO.Server.Common;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class ShardShutdownRequest : AbstractGluonPacket
{
public uint ShardId;
public ShutdownType Type;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
ShardId = input.GetUInt32();
Type = input.GetEnum<ShutdownType>();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.ShardShutdownRequest;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(ShardId);
output.PutEnum(Type);
}
}
}

View file

@ -0,0 +1,41 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Server.Protocol.Gluon.Model;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class TransferClaim : AbstractGluonPacket
{
public ClaimType Type;
public ClaimAction Action;
public int EntityId;
public uint ClaimId;
public uint SpecialId; //job lot info
public string FromOwner;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Type = input.GetEnum<ClaimType>();
Action = input.GetEnum<ClaimAction>();
EntityId = input.GetInt32();
ClaimId = input.GetUInt32();
SpecialId = input.GetUInt32();
FromOwner = input.GetPascalString();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.TransferClaim;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Type);
output.PutEnum(Action);
output.PutInt32(EntityId);
output.PutUInt32(ClaimId);
output.PutUInt32(SpecialId);
output.PutPascalString(FromOwner);
}
}
}

View file

@ -0,0 +1,45 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
using FSO.Server.Protocol.Gluon.Model;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class TransferClaimResponse : AbstractGluonPacket
{
public TransferClaimResponseStatus Status;
public ClaimType Type;
public int EntityId;
public uint ClaimId;
public string NewOwner;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
Status = input.GetEnum<TransferClaimResponseStatus>();
Type = input.GetEnum<ClaimType>();
EntityId = input.GetInt32();
ClaimId = input.GetUInt32();
NewOwner = input.GetPascalString();
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.TransferClaimResponse;
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutEnum(Status);
output.PutEnum(Type);
output.PutInt32(EntityId);
output.PutUInt32(ClaimId);
output.PutPascalString(NewOwner);
}
}
public enum TransferClaimResponseStatus
{
ACCEPTED,
REJECTED,
CLAIM_NOT_FOUND
}
}

View file

@ -0,0 +1,27 @@
using FSO.Common.Serialization;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Gluon.Packets
{
public class TuningChanged : AbstractGluonCallPacket
{
public bool UpdateInstantly;
public override void Deserialize(IoBuffer input, ISerializationContext context)
{
base.Deserialize(input, context);
UpdateInstantly = input.GetBool();
}
public override void Serialize(IoBuffer output, ISerializationContext context)
{
base.Serialize(output, context);
output.PutBool(UpdateInstantly);
}
public override GluonPacketType GetPacketType()
{
return GluonPacketType.TuningChanged;
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FSO.Server.Protocol")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FSO.Server.Protocol")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a08ade32-27e2-44f4-bc52-11a16c56baa8")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace FSO.Server.Protocol.Utils
{
public class ChallengeResponse
{
private static int _salt_byte_length = 320;
private static int _nonce_byte_length = 32;
private static RNGCryptoServiceProvider _random_crypt_prov = new RNGCryptoServiceProvider();
private static Random _random_gen = new Random();
private static int _min_iteration_count = 4000;
private static int _max_iteration_count = 5000;
private const int _sha_output_length = 20;
private static byte[] ComputeHMACHash(byte[] data, string secret)
{
using (var _hmac_sha_1 = new HMACSHA1(data, true))
{
byte[] _hash_bytes = _hmac_sha_1.ComputeHash(Encoding.UTF8.GetBytes(secret));
return _hash_bytes;
}
}
public static string AnswerChallenge(string challenge, string secret)
{
var components = new Dictionary<string, string>();
var parts = challenge.Split(',');
foreach(var part in parts){
var subParts = part.Split(new char[] { '=' }, 2);
components.Add(subParts[0], subParts[1]);
}
var iterations = int.Parse(components["i"]);
var salt = components["s"];
var salted = Hi(secret, Convert.FromBase64String(salt), iterations);
return Convert.ToBase64String(ComputeHMACHash(salted, secret));
}
public static string GetChallenge()
{
var numIterations = GetRandomInteger();
return "i=" + numIterations + ",s=" + Convert.ToBase64String(GetRandomByteArray(_nonce_byte_length));
}
private static byte[] Hi(string password, byte[] salt, int iteration_count)
{
Rfc2898DeriveBytes _pdb = new Rfc2898DeriveBytes(password, salt, iteration_count);
return _pdb.GetBytes(_sha_output_length);
}
private static int GetRandomInteger()
{
if (_random_gen == null)
_random_gen = new Random();
int _random_int = _random_gen.Next(_min_iteration_count, _max_iteration_count);
if (_random_int < 0)
_random_int *= -1;
return _random_int;
}
private static byte[] GetRandomByteArray(int byte_array_length)
{
byte[] _random_byte_array = new byte[byte_array_length];
_random_crypt_prov.GetBytes(_random_byte_array);
return _random_byte_array;
}
}
}

View file

@ -0,0 +1,160 @@
using System;
using Mina.Core.Buffer;
using Mina.Core.Session;
using Mina.Filter.Codec;
namespace FSO.Server.Protocol.Utils
{
/// <summary>
/// A <see cref="IProtocolDecoder"/> that cumulates the content of received buffers to a
/// cumulative buffer to help users implement decoders.
/// </summary>
public abstract class CustomCumulativeProtocolDecoder : ProtocolDecoderAdapter
{
private readonly AttributeKey BUFFER = new AttributeKey(typeof(CustomCumulativeProtocolDecoder), "buffer");
/// <summary>
/// </summary>
protected CustomCumulativeProtocolDecoder()
{ }
/// <inheritdoc/>
public override void Decode(IoSession session, IoBuffer input, IProtocolDecoderOutput output)
{
if (!session.TransportMetadata.HasFragmentation)
{
while (input.HasRemaining)
{
if (!DoDecode(session, input, output))
break;
}
return;
}
Boolean usingSessionBuffer = true;
IoBuffer buf = session.GetAttribute<IoBuffer>(BUFFER);
// If we have a session buffer, append data to that; otherwise
// use the buffer read from the network directly.
if (buf == null)
{
buf = input;
usingSessionBuffer = false;
}
else
{
Boolean appended = false;
// Make sure that the buffer is auto-expanded.
if (buf.AutoExpand)
{
try
{
buf.Put(input);
appended = true;
}
catch (InvalidOperationException)
{
// A user called derivation method (e.g. slice()),
// which disables auto-expansion of the parent buffer.
}
catch (OverflowException)
{
// A user disabled auto-expansion.
}
}
if (appended)
{
buf.Flip();
}
else
{
// Reallocate the buffer if append operation failed due to
// derivation or disabled auto-expansion.
buf.Flip();
IoBuffer newBuf = IoBuffer.Allocate(buf.Remaining + input.Remaining);
newBuf.AutoExpand = true;
newBuf.Order = buf.Order;
newBuf.Put(buf);
newBuf.Put(input);
newBuf.Flip();
buf = newBuf;
// Update the session attribute.
session.SetAttribute(BUFFER, buf);
}
}
while (true)
{
Int32 oldPos = buf.Position;
Boolean decoded = DoDecode(session, buf, output);
if (decoded)
{
if (buf.Position == oldPos)
throw new InvalidOperationException("DoDecode() can't return true when buffer is not consumed.");
if (!buf.HasRemaining)
break;
}
else
{
break;
}
}
// if there is any data left that cannot be decoded, we store
// it in a buffer in the session and next time this decoder is
// invoked the session buffer gets appended to
if (buf.HasRemaining)
{
if (usingSessionBuffer && buf.AutoExpand)
{
buf.Compact();
}
else
{
StoreRemainingInSession(buf, session);
}
}
else
{
if (usingSessionBuffer)
{
RemoveSessionBuffer(session);
}
}
}
/// <summary>
/// Implement this method to consume the specified cumulative buffer and decode its content into message(s).
/// </summary>
/// <param name="session"></param>
/// <param name="input"></param>
/// <param name="output"></param>
/// <returns>
/// true if and only if there's more to decode in the buffer
/// and you want to have DoDecode method invoked again.
/// Return false if remaining data is not enough to decode,
/// then this method will be invoked again when more data is cumulated.
/// </returns>
protected abstract Boolean DoDecode(IoSession session, IoBuffer input, IProtocolDecoderOutput output);
private void RemoveSessionBuffer(IoSession session)
{
session.RemoveAttribute(BUFFER);
}
private void StoreRemainingInSession(IoBuffer buf, IoSession session)
{
IoBuffer remainingBuf = IoBuffer.Allocate(buf.Capacity);
remainingBuf.AutoExpand = true;
remainingBuf.Order = buf.Order;
remainingBuf.Put(buf);
session.SetAttribute(BUFFER, remainingBuf);
}
}
}

View file

@ -0,0 +1,618 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Common.Logging;
using Mina.Core.Buffer;
using Mina.Core.Filterchain;
using Mina.Core.Future;
using Mina.Core.Session;
using Mina.Core.Write;
namespace FSO.Server.Protocol.Utils
{
public class CustomSslFilter : IoFilterAdapter
{
static readonly ILog log = LogManager.GetLogger(typeof(CustomSslFilter));
private static readonly AttributeKey NEXT_FILTER = new AttributeKey(typeof(CustomSslFilter), "nextFilter");
private static readonly AttributeKey SSL_HANDLER = new AttributeKey(typeof(CustomSslFilter), "handler");
X509Certificate _serverCertificate = null;
SslProtocols _sslProtocol = SslProtocols.Default;
public CustomSslFilter(String certFile)
: this(X509Certificate.CreateFromCertFile(certFile))
{ }
public CustomSslFilter(X509Certificate cert)
{
_serverCertificate = cert;
}
public SslProtocols SslProtocol
{
get { return _sslProtocol; }
set { _sslProtocol = value; }
}
public X509Certificate Certificate
{
get { return _serverCertificate; }
}
/// <summary>
/// Returns <code>true</code> if and only if the specified session is
/// encrypted/decrypted over SSL/TLS currently.
/// </summary>
public Boolean IsSslStarted(IoSession session)
{
SslHandler handler = session.GetAttribute<SslHandler>(SSL_HANDLER);
return handler != null && handler.Authenticated;
}
/// <inheritdoc/>
public override void OnPreAdd(IoFilterChain parent, String name, INextFilter nextFilter)
{
if (parent.Contains<CustomSslFilter>())
throw new InvalidOperationException("Only one SSL filter is permitted in a chain.");
IoSession session = parent.Session;
session.SetAttribute(NEXT_FILTER, nextFilter);
// Create a SSL handler and start handshake.
SslHandler handler = new SslHandler(this, session);
session.SetAttribute(SSL_HANDLER, handler);
}
/// <inheritdoc/>
public override void OnPostAdd(IoFilterChain parent, String name, INextFilter nextFilter)
{
SslHandler handler = GetSslSessionHandler(parent.Session);
handler.Handshake(nextFilter);
}
/// <inheritdoc/>
public override void OnPreRemove(IoFilterChain parent, String name, INextFilter nextFilter)
{
IoSession session = parent.Session;
session.RemoveAttribute(NEXT_FILTER);
session.RemoveAttribute(SSL_HANDLER);
}
/// <inheritdoc/>
public override void SessionClosed(INextFilter nextFilter, IoSession session)
{
SslHandler handler = GetSslSessionHandler(session);
try
{
// release resources
handler.Destroy();
}
finally
{
// notify closed session
base.SessionClosed(nextFilter, session);
}
}
/// <inheritdoc/>
public override void MessageReceived(INextFilter nextFilter, IoSession session, Object message)
{
IoBuffer buf = (IoBuffer)message;
SslHandler handler = GetSslSessionHandler(session);
// forward read encrypted data to SSL handler
handler.MessageReceived(nextFilter, buf);
}
/// <inheritdoc/>
public override void MessageSent(INextFilter nextFilter, IoSession session, IWriteRequest writeRequest)
{
EncryptedWriteRequest encryptedWriteRequest = writeRequest as EncryptedWriteRequest;
if (encryptedWriteRequest == null)
{
// ignore extra buffers used for handshaking
}
else
{
base.MessageSent(nextFilter, session, encryptedWriteRequest.InnerRequest);
}
}
/// <inheritdoc/>
public override void ExceptionCaught(INextFilter nextFilter, IoSession session, Exception cause)
{
base.ExceptionCaught(nextFilter, session, cause);
}
/// <inheritdoc/>
public override void FilterWrite(INextFilter nextFilter, IoSession session, IWriteRequest writeRequest)
{
SslHandler handler = GetSslSessionHandler(session);
handler.ScheduleFilterWrite(nextFilter, writeRequest);
}
/// <inheritdoc/>
public override void FilterClose(INextFilter nextFilter, IoSession session)
{
SslHandler handler = session.GetAttribute<SslHandler>(SSL_HANDLER);
if (handler == null)
{
// The connection might already have closed, or
// SSL might have not started yet.
base.FilterClose(nextFilter, session);
return;
}
IWriteFuture future = null;
try
{
future = InitiateClosure(handler, nextFilter, session);
future.Complete += (s, e) => base.FilterClose(nextFilter, session);
}
finally
{
if (future == null)
base.FilterClose(nextFilter, session);
}
}
private IWriteFuture InitiateClosure(SslHandler handler, INextFilter nextFilter, IoSession session)
{
IWriteFuture future = DefaultWriteFuture.NewWrittenFuture(session);
handler.Destroy();
return future;
}
private SslHandler GetSslSessionHandler(IoSession session)
{
SslHandler handler = session.GetAttribute<SslHandler>(SSL_HANDLER);
if (handler == null)
throw new InvalidOperationException();
if (handler.SslFilter != this)
throw new ArgumentException("Not managed by this filter.");
return handler;
}
public static void DisplaySecurityLevel(SslStream stream)
{
log.DebugFormat("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
log.DebugFormat("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
log.DebugFormat("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
log.DebugFormat("Protocol: {0}", stream.SslProtocol);
}
public static void DisplaySecurityServices(SslStream stream)
{
log.DebugFormat("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
log.DebugFormat("IsSigned: {0}", stream.IsSigned);
log.DebugFormat("Is Encrypted: {0}", stream.IsEncrypted);
}
public static void DisplayStreamProperties(SslStream stream)
{
log.DebugFormat("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
log.DebugFormat("Can timeout: {0}", stream.CanTimeout);
}
public static void DisplayCertificateInformation(SslStream stream)
{
log.DebugFormat("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
X509Certificate localCertificate = stream.LocalCertificate;
if (stream.LocalCertificate != null)
{
log.DebugFormat("Local cert was issued to {0} and is valid from {1} until {2}.",
localCertificate.Subject,
localCertificate.GetEffectiveDateString(),
localCertificate.GetExpirationDateString());
}
else
{
log.DebugFormat("Local certificate is null.");
}
// Display the properties of the client's certificate.
X509Certificate remoteCertificate = stream.RemoteCertificate;
if (stream.RemoteCertificate != null)
{
log.DebugFormat("Remote cert was issued to {0} and is valid from {1} until {2}.",
remoteCertificate.Subject,
remoteCertificate.GetEffectiveDateString(),
remoteCertificate.GetExpirationDateString());
}
else
{
log.DebugFormat("Remote certificate is null.");
}
}
internal class EncryptedWriteRequest : WriteRequestWrapper
{
private readonly IoBuffer _encryptedMessage;
public EncryptedWriteRequest(IWriteRequest writeRequest, IoBuffer encryptedMessage)
: base(writeRequest)
{
_encryptedMessage = encryptedMessage;
}
public override Object Message
{
get { return _encryptedMessage; }
}
}
}
class SslHandler : IDisposable
{
static readonly ILog log = LogManager.GetLogger(typeof(CustomSslFilter));
private readonly CustomSslFilter _sslFilter;
private readonly IoSession _session;
private readonly IoSessionStream _sessionStream;
private readonly SslStream _sslStream;
private volatile Boolean _authenticated;
private readonly ConcurrentQueue<IoFilterEvent> _preHandshakeEventQueue = new ConcurrentQueue<IoFilterEvent>();
private INextFilter _currentNextFilter;
private IWriteRequest _currentWriteRequest;
public SslHandler(CustomSslFilter sslFilter, IoSession session)
{
_sslFilter = sslFilter;
_session = session;
_sessionStream = new IoSessionStream(this);
_sslStream = new SslStream(_sessionStream, false);
}
public CustomSslFilter SslFilter
{
get { return _sslFilter; }
}
public Boolean Authenticated
{
get { return _authenticated; }
private set
{
_authenticated = value;
if (value)
FlushPreHandshakeEvents();
}
}
public void Dispose()
{
_sessionStream.Dispose();
_sslStream.Dispose();
}
public void Handshake(INextFilter nextFilter)
{
//lock (_sessionStream)
//{
_currentNextFilter = nextFilter;
_sslStream.BeginAuthenticateAsClient("auth.east.ea.com", null, _sslFilter.SslProtocol, false, AuthenticateCallback, null);
//}
}
private void AuthenticateCallback(IAsyncResult ar)
{
try
{
_sslStream.EndAuthenticateAsClient(ar);
}
catch (AuthenticationException e)
{
_sslFilter.ExceptionCaught(_currentNextFilter, _session, e);
return;
}
catch (IOException e)
{
_sslFilter.ExceptionCaught(_currentNextFilter, _session, e);
return;
}
Authenticated = true;
if (log.IsDebugEnabled)
{
// Display the properties and settings for the authenticated stream.
CustomSslFilter.DisplaySecurityLevel(_sslStream);
CustomSslFilter.DisplaySecurityServices(_sslStream);
CustomSslFilter.DisplayCertificateInformation(_sslStream);
CustomSslFilter.DisplayStreamProperties(_sslStream);
}
}
public void ScheduleFilterWrite(INextFilter nextFilter, IWriteRequest writeRequest)
{
if (!_authenticated)
{
if (_session.Connected)
{
// Handshake not complete yet.
_preHandshakeEventQueue.Enqueue(new IoFilterEvent(nextFilter, IoEventType.Write, _session, writeRequest));
}
return;
}
IoBuffer buf = (IoBuffer)writeRequest.Message;
if (buf.Remaining == 0) { return; }
//lock (_sessionStream)
//{
ArraySegment<Byte> array = buf.GetRemaining();
_currentNextFilter = nextFilter;
_currentWriteRequest = writeRequest;
// SSL encrypt
_sslStream.Write(array.Array, array.Offset, array.Count);
_sslStream.Flush();
//}
}
public void MessageReceived(INextFilter nextFilter, IoBuffer buf)
{
//lock (_sessionStream)
//{
_currentNextFilter = nextFilter;
_sessionStream.Write(buf);
if (_authenticated)
{
IoBuffer readBuffer = ReadBuffer();
nextFilter.MessageReceived(_session, readBuffer);
}
//}
}
public void Destroy()
{
_sslStream.Close();
IoFilterEvent scheduledWrite;
while (_preHandshakeEventQueue.TryDequeue(out scheduledWrite))
{ }
}
private void FlushPreHandshakeEvents()
{
lock (_sessionStream)
{
IoFilterEvent scheduledWrite;
while (_preHandshakeEventQueue.TryDequeue(out scheduledWrite))
{
_sslFilter.FilterWrite(scheduledWrite.NextFilter, scheduledWrite.Session, (IWriteRequest)scheduledWrite.Parameter);
}
}
}
private void WriteBuffer(IoBuffer buf)
{
IWriteRequest writeRequest;
if (_authenticated)
writeRequest = new CustomSslFilter.EncryptedWriteRequest(_currentWriteRequest, buf);
else
writeRequest = new DefaultWriteRequest(buf);
_currentNextFilter.FilterWrite(_session, writeRequest);
}
private IoBuffer ReadBuffer()
{
IoBuffer buf = IoBuffer.Allocate(_sessionStream.Remaining);
while (true)
{
ArraySegment<Byte> array = buf.GetRemaining();
Int32 bytesRead = _sslStream.Read(array.Array, array.Offset, array.Count);
if (bytesRead <= 0)
break;
buf.Position += bytesRead;
if (_sessionStream.Remaining == 0)
break;
else
{
// We have to grow the target buffer, it's too small.
buf.Capacity += _sessionStream.Remaining;
buf.Limit = buf.Capacity;
}
}
buf.Flip();
return buf;
}
class IoSessionStream : System.IO.Stream
{
readonly Object _syncRoot = new Byte[0];
readonly SslHandler _sslHandler;
readonly IoBuffer _buf;
volatile Boolean _closed;
volatile Boolean _released;
IOException _exception;
public IoSessionStream(SslHandler sslHandler)
{
_sslHandler = sslHandler;
_buf = IoBuffer.Allocate(16);
_buf.AutoExpand = true;
_buf.Limit = 0;
}
public override Int32 ReadByte()
{
lock (_syncRoot)
{
if (!WaitForData())
return 0;
return _buf.Get() & 0xff;
}
}
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
{
lock (_syncRoot)
{
if (!WaitForData())
return 0;
Int32 readBytes = Math.Min(count, _buf.Remaining);
_buf.Get(buffer, offset, readBytes);
return readBytes;
}
}
public override void Close()
{
base.Close();
if (_closed)
return;
lock (_syncRoot)
{
_closed = true;
ReleaseBuffer();
Monitor.PulseAll(_syncRoot);
}
}
public override void Write(Byte[] buffer, Int32 offset, Int32 count)
{
_sslHandler.WriteBuffer(IoBuffer.Wrap((Byte[])buffer.Clone(), offset, count));
}
public override void WriteByte(Byte value)
{
IoBuffer buf = IoBuffer.Allocate(1);
buf.Put(value);
buf.Flip();
_sslHandler.WriteBuffer(buf);
}
public override void Flush()
{ }
public void Write(IoBuffer buf)
{
if (_closed)
return;
lock (_syncRoot)
{
if (_buf.HasRemaining)
{
_buf.Compact().Put(buf).Flip();
}
else
{
_buf.Clear().Put(buf).Flip();
Monitor.PulseAll(_syncRoot);
}
}
}
private Boolean WaitForData()
{
if (_released)
return false;
lock (_syncRoot)
{
while (!_released && _buf.Remaining == 0 && _exception == null)
{
try
{
Monitor.Wait(_syncRoot);
}
catch (ThreadInterruptedException e)
{
throw new IOException("Interrupted while waiting for more data", e);
}
}
}
if (_exception != null)
{
ReleaseBuffer();
throw _exception;
}
if (_closed && _buf.Remaining == 0)
{
ReleaseBuffer();
return false;
}
return true;
}
private void ReleaseBuffer()
{
if (_released)
return;
_released = true;
}
public IOException Exception
{
set
{
if (_exception == null)
{
lock (_syncRoot)
{
_exception = value;
Monitor.PulseAll(_syncRoot);
}
}
}
}
public Int32 Remaining
{
get { return _buf.Remaining; }
}
public override Boolean CanRead
{
get { return true; }
}
public override Boolean CanSeek
{
get { return false; }
}
public override Boolean CanWrite
{
get { return true; }
}
public override Int64 Length
{
get { throw new NotSupportedException(); }
}
public override Int64 Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override Int64 Seek(Int64 offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(Int64 value)
{
throw new NotSupportedException();
}
}
}
}

View file

@ -0,0 +1,33 @@
using FSO.Common.Serialization;
using FSO.Server.Protocol.Voltron.Model;
using Mina.Core.Buffer;
namespace FSO.Server.Protocol.Voltron
{
public abstract class AbstractVoltronPacket : IVoltronPacket
{
public static Sender GetSender(IoBuffer buffer)
{
var ariesID = buffer.GetPascalString();
var masterID = buffer.GetPascalString();
return new Sender { AriesID = ariesID, MasterAccountID = masterID };
}
public static void PutSender(IoBuffer buffer, Sender sender)
{
buffer.PutPascalString(sender.AriesID);
buffer.PutPascalString(sender.MasterAccountID);
}
public static IoBuffer Allocate(int size)
{
IoBuffer buffer = IoBuffer.Allocate(size);
buffer.Order = ByteOrder.BigEndian;
return buffer;
}
public abstract VoltronPacketType GetPacketType();
public abstract void Serialize(IoBuffer output, ISerializationContext context);
public abstract void Deserialize(IoBuffer input, ISerializationContext context);
}
}

View file

@ -0,0 +1,20 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class Avatar
{
public bool Avatar_IsFounder { get; set; }
public string Avatar_Name { get; set; }
public string Avatar_Description { get; set; }
public bool Avatar_IsParentalControlLocked { get; set; }
/*public bool Avatar_IsOnline { get; set; }
public uint Avatar_LotGridXY { get; set; }
public uint Avatar_Age { get; set; }
public ushort Avatar_SkillsLockPoints { get; set; }
public AvatarAppearance Avatar_Appearance { get; set; }
public AvatarSkills Avatar_Skills { get; set; }
public List<Bookmark> Avatar_BookmarksVec { get; set; }*/
}
}

View file

@ -0,0 +1,9 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class AvatarAppearance
{
public ulong AvatarAppearance_BodyOutfitID { get; set; }
public byte AvatarAppearance_SkinTone { get; set; }
public ulong AvatarAppearance_HeadOutfitID { get; set; }
}
}

View file

@ -0,0 +1,18 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class AvatarSkills
{
public ushort AvatarSkills_Logic { get; set; }
public ushort AvatarSkills_LockLv_Logic { get; set; }
public ushort AvatarSkills_Body { get; set; }
public ushort AvatarSkills_LockLv_Body { get; set; }
public ushort AvatarSkills_LockLv_Mechanical { get; set; }
public ushort AvatarSkills_LockLv_Creativity { get; set; }
public ushort AvatarSkills_LockLv_Cooking { get; set; }
public ushort AvatarSkills_Cooking { get; set; }
public ushort AvatarSkills_Charisma { get; set; }
public ushort AvatarSkills_LockLv_Charisma { get; set; }
public ushort AvatarSkills_Mechanical { get; set; }
public ushort AvatarSkills_Creativity { get; set; }
}
}

View file

@ -0,0 +1,13 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class Bookmark
{
public uint Bookmark_TargetID { get; set; }
public BookmarkType Bookmark_Type { get; set; }
}
public enum BookmarkType : byte
{
BOOKMARK = 0x01
}
}

View file

@ -0,0 +1,6 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class Location
{
}
}

View file

@ -0,0 +1,7 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class Lot
{
}
}

View file

@ -0,0 +1,41 @@
using System.Collections.Generic;
using Mina.Core.Buffer;
using FSO.Common.Serialization;
namespace FSO.Server.Protocol.Voltron.DataService
{
public class cITSOProperty : IoBufferSerializable
{
public uint StructType;
public List<cITSOField> StructFields;
/**cTSOValue<class cRZAutoRefCount<class cITSOProperty> > body:
* dword Body clsid (iid=896E3E90 or "GZIID_cITSOProperty"; clsid should be 0x89739A79 for cTSOProperty)
* dword Body
* dword Struct type (e.g. 0x3B0430BF for AvatarAppearance)
* dword Field count
* Fields - for each field:
* dword Field name (e.g. 0x1D530275 for AvatarAppearance_BodyOutfitID)
* dword cTSOValue clsid
* cTSOValue body**/
public void Serialize(IoBuffer output, ISerializationContext context)
{
output.PutUInt32(0x89739A79);
output.PutUInt32(StructType);
output.PutUInt32((uint)StructFields.Count);
foreach(var item in StructFields){
output.PutUInt32(item.ID);
output.PutUInt32(item.Value.Type);
output.PutSerializable(item.Value.Value, context);
}
}
}
public class cITSOField
{
public uint ID;
public cTSOValue Value;
}
}

View file

@ -0,0 +1,398 @@
using FSO.Common.Serialization;
using FSO.Files.Formats.tsodata;
using Mina.Core.Buffer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace FSO.Server.Protocol.Voltron.DataService
{
/// <summary>
/// TODO: Rewrite this to have much tighter performance
/// </summary>
public class cTSOSerializer
{
//private static Logger LOG = LogManager.GetCurrentClassLogger();
public static cTSOSerializer INSTANCE = null;
public static cTSOSerializer Get(){
return INSTANCE;
}
public const uint cTSOValue_bool = 0x696D1183;
public const uint cTSOValue_uint8 = 0xC976087C;
public const uint cTSOValue_uint16 = 0xE9760891;
public const uint cTSOValue_uint32 = 0x696D1189;
public const uint cTSOValue_uint64 = 0x69D3E3DB;
public const uint cTSOValue_sint8 = 0xE976088A;
public const uint cTSOValue_sint16 = 0xE9760897;
public const uint cTSOValue_sint32 = 0x896D1196;
public const uint cTSOValue_sint64 = 0x89D3E3EF;
public const uint cTSOValue_string = 0x896D1688;
public const uint cTSOValue_property = 0xA96E7E5B;
private TSODataDefinition Format;
private Dictionary<uint, Type> ClassesById = new Dictionary<uint, Type>();
private Dictionary<Type, uint> IdByClass = new Dictionary<Type, uint>();
private Dictionary<uint, Type> cNetMessageParametersById = new Dictionary<uint, Type>();
public cTSOSerializer(TSODataDefinition data){
INSTANCE = this;
this.Format = data;
//Scan for classes with decorations
var assembly = Assembly.GetAssembly(typeof(cTSOSerializer));
foreach (Type type in assembly.GetTypes())
{
System.Attribute[] attributes = System.Attribute.GetCustomAttributes(type);
foreach (Attribute attribute in attributes)
{
if (attribute is clsid){
ClassesById.Add(((clsid)attribute).Value, type);
IdByClass.Add(type, ((clsid)attribute).Value);
}else if(attribute is cTSONetMessageParameter)
{
var param = (cTSONetMessageParameter)attribute;
object paramValue = param.Value;
/*if(paramValue is DBRequestType){
paramValue = ((DBRequestType)paramValue).GetRequestID();
}else if(paramValue is DBResponseType)
{
paramValue = ((DBResponseType)paramValue).GetResponseID();
}*/
cNetMessageParametersById.Add((uint)paramValue, type);
}
}
}
}
public object Deserialize(uint clsid, IoBuffer buffer)
{
if (cNetMessageParametersById.ContainsKey(clsid))
{
var instance = (IoBufferDeserializable)Activator.CreateInstance(cNetMessageParametersById[clsid]);
//instance.Deserialize(buffer);
return instance;
}
else if (ClassesById.ContainsKey(clsid))
{
var instance = (IoBufferDeserializable)Activator.CreateInstance(ClassesById[clsid]);
//instance.Deserialize(buffer);
return instance;
}
else if(clsid == cTSOValue_string)
{
return buffer.GetPascalVLCString();
}
return null;
}
public object Serialize(object obj)
{
if(obj is IoBufferSerializable)
{
return (IoBufferSerializable)obj;
}
return GetValue(obj).Value;
}
public cTSOValue GetValue(object obj){
var type = obj.GetType();
if (IdByClass.ContainsKey(type))
{
uint clsid = IdByClass[type];
return new cTSOValue() { Type = clsid, Value = obj };
}
throw new Exception("Unknown class " + type);
}
public DerivedStruct GetDerivedStruct(uint id)
{
return Format.DerivedStructs.FirstOrDefault(x => x.ID == id);
}
public DerivedStruct GetDerivedStruct(string name)
{
return Format.DerivedStructs.FirstOrDefault(x => x.Name == name);
}
/*public List<DataServiceWrapperPDU> SerializeDerivedUpdate(uint avatarId, string derivedTypeName, uint structId, object instance)
{
var type = GetDerivedStruct(derivedTypeName);
var fields = SerializeDerived(derivedTypeName, structId, instance);
var result = new List<DataServiceWrapperPDU>();
foreach(var update in fields){
result.Add(new DataServiceWrapperPDU() {
SendingAvatarID = avatarId,
RequestTypeID = 0x3998426C,
Body = update
});
}
return result;
}*/
/*
public List<cTSOTopicUpdateMessage> SerializeDerived(string derivedTypeName, uint structId, object instance)
{
return SerializeDerived(GetDerivedStruct(derivedTypeName).ID, structId, instance);
}
public List<cTSOTopicUpdateMessage> SerializeDerived(uint derivedType, uint structId, object instance){
var result = new List<cTSOTopicUpdateMessage>();
var type = Format.DerivedStructs.First(x => x.ID == derivedType);
var parent = Format.Structs.First(x => x.ID == type.Parent);
foreach(var field in parent.Fields){
var mask = type.FieldMasks.FirstOrDefault(x => x.ID == field.ID);
var action = DerivedStructFieldMaskType.KEEP;
if (mask != null){
action = mask.Type;
}
if(action == DerivedStructFieldMaskType.REMOVE){
continue;
}
object value = GetFieldValue(instance, field.Name);
if (value == null) { continue; }
try {
var serialized = SerializeField(field, value);
serialized.StructType = parent.ID;
serialized.StructId = structId;
result.Add(serialized);
}catch(Exception ex)
{
LOG.Error(ex);
}
}
return result;
}*/
private object GetFieldValue(object obj, string fieldName)
{
var objectField = obj.GetType().GetProperty(fieldName);
if (objectField == null) { return null; }
var value = objectField.GetValue(obj);
return value;
}
/*private cTSOTopicUpdateMessage SerializeField(StructField field, object value){
cTSOTopicUpdateMessage result = new cTSOTopicUpdateMessage();
result.StructField = field.ID;
if(field.Classification == StructFieldClassification.List)
{
IoBuffer resultBytes = AbstractVoltronPacket.Allocate(4);
resultBytes.AutoExpand = true;
var serializedValues = new List<cTSOValue>();
System.Collections.ICollection list = (System.Collections.ICollection)value;
foreach (var item in list){
serializedValues.Add(SerializeValue(field.TypeID, item));
}
var itemType = serializedValues.First().Type;
var vectorType = GetVectorClsId(itemType);
resultBytes.PutUInt32((uint)serializedValues.Count);
foreach (var serializedValue in serializedValues){
//resultBytes.PutSerializable(serializedValue.Value);
}
resultBytes.Flip();
/*result.cTSOValue = new cTSOValue {
Type = 0xA97384A3,//field.TypeID,
Value = resultBytes
};
}else if(field.Classification == StructFieldClassification.SingleField)
{
var serializedValue = SerializeValue(field.TypeID, value);
//result.cTSOValue = serializedValue;
}
return result;
}*/
private uint GetVectorClsId(uint clsid)
{
switch (clsid)
{
//cTSOValue < unsigned long>
case 0x696D1189:
//cTSOValueVector < unsigned long>
return 0x89738496;
//cTSOValue < long >
case 0x896D1196:
//cTSOValueVector < long >
return 0x8973849A;
//cTSOValue < unsigned short>
case 0xE9760891:
//cTSOValueVector < unsigned short>
return 0x097608B3;
//cTSOValue < short >
case 0xE9760897:
//cTSOValueVector < short >
return 0x097608B6;
//cTSOValue<class cRZAutoRefCount<class cITSOProperty> >
case 0xA96E7E5B:
//cTSOValueVector<class cRZAutoRefCount<class cITSOProperty> >
return 0xA97384A3;
//cTSOValue <class cRZAutoRefCount<class cIGZString> >
default:
throw new Exception("Cannot map clsid to vector clsid, " + clsid);
}
}
private cTSOValue SerializeValue(uint type, object value)
{
var result = new cTSOValue();
IoBuffer buffer = null;
switch (type)
{
case 0x48BC841E:
if (!(value is sbyte) && !(value is Enum))
{
return null;
}
result.Type = cTSOValue_sint8;
result.Value = new sbyte[] { Convert.ToSByte(value) };
break;
case 0x74336731:
if (!(value is ushort))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(2);
buffer.PutUInt16((ushort)value);
buffer.Flip();
result.Type = cTSOValue_uint16;
result.Value = buffer;
break;
case 0xF192ECA6:
if (!(value is short))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(2);
buffer.PutInt16((short)value);
buffer.Flip();
result.Type = cTSOValue_sint16;
result.Value = buffer;
break;
case 0xE0463A2F:
if (!(value is uint))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(4);
buffer.PutUInt32((uint)value);
buffer.Flip();
result.Type = cTSOValue_uint32;
result.Value = buffer;
break;
case 0xA0587098:
if (!(value is int))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(4);
buffer.PutInt32((int)value);
buffer.Flip();
result.Type = cTSOValue_sint32;
result.Value = buffer;
break;
case 0x385070C9:
if (!(value is ulong))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(8);
buffer.PutUInt64((ulong)value);
buffer.Flip();
result.Type = cTSOValue_uint64;
result.Value = buffer;
break;
case 0x90D315F7:
if (!(value is long))
{
return null;
}
buffer = AbstractVoltronPacket.Allocate(8);
buffer.PutInt64((long)value);
buffer.Flip();
result.Type = cTSOValue_sint64;
result.Value = buffer;
break;
default:
//It may be a struct
var _struct = Format.Structs.FirstOrDefault(x => x.ID == type);
if (_struct != null)
{
var body = new cITSOProperty();
body.StructType = _struct.ID;
body.StructFields = new List<cITSOField>();
foreach(var field in _struct.Fields){
object fieldValue = GetFieldValue(value, field.Name);
if (fieldValue == null) { continue; }
body.StructFields.Add(new cITSOField {
ID = field.ID,
Value = SerializeValue(field.TypeID, fieldValue)
});
}
result.Type = cTSOValue_property;
result.Value = body;
return result;
}
return null;
}
return result;
}
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Protocol.Voltron.DataService
{
public class cTSOValue
{
public uint Type { get; set; }
public object Value { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show more