mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-08-01 15:24:27 +02:00
Cleaned up namespaces (still have to do Map Project) and removed references to FFXIV Classic from the code. Removed the Launcher Editor project as it is no longer needed (host file editing is cleaner).
This commit is contained in:
parent
7587a6e142
commit
0f61c4c0e1
544 changed files with 54548 additions and 55498 deletions
154
Lobby Server/CharacterCreatorUtils.cs
Normal file
154
Lobby Server/CharacterCreatorUtils.cs
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class CharacterCreatorUtils
|
||||
{
|
||||
private static readonly Dictionary<uint, uint[]> equipmentAppearance = new Dictionary<uint, uint[]>
|
||||
{
|
||||
{ 2, new uint[]{60818432,60818432,0,0,0,0,0,0,10656,10560,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //PUG
|
||||
{ 3, new uint[]{79692890,0,0,0,0,0,0,0,31776,4448,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //GLA
|
||||
{ 4, new uint[]{147850310,0,0,0,0,0,0,23713,0,10016,5472,1152,6144,0,0,0,0,0,0,0,0,0} }, //MRD
|
||||
{ 7, new uint[]{210764860,236979210,0,0,0,231736320,0,0,9888,9984,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //ARC
|
||||
{ 8, new uint[]{168823858,0,0,0,0,0,0,0,13920,7200,1024,10656,6144,0,0,0,0,0,0,0,0,0} }, //LNC
|
||||
|
||||
{ 22, new uint[]{294650980,0,0,0,0,0,0,0,7744,5472,1024,5504,4096,0,0,0,0,0,0,0,0,0} }, //THM
|
||||
{ 23, new uint[]{347079700,0,0,0,0,0,0,0,4448,2240,1024,4416,4096,0,0,0,0,0,0,0,0,0} }, //CNJ
|
||||
|
||||
{ 29, new uint[]{705692672,0,0,0,0,0,0,0,0,10016,10656,9632,2048,0,0,0,0,0,0,0,0,0} }, //CRP
|
||||
{ 30, new uint[]{721421372,0,0,0,0,0,0,0,0,2241,2336,2304,2048,0,0,0,0,0,0,0,0,0} }, //BSM
|
||||
{ 31, new uint[]{737149962,0,0,0,0,0,0,0,32992,2240,1024,2272,2048,0,0,0,0,0,0,0,0,0} }, //ARM
|
||||
{ 32, new uint[]{752878592,0,0,0,0,0,0,0,2368,3424,1024,10656,2048,0,0,0,0,0,0,0,0,0} }, //GSM
|
||||
{ 33, new uint[]{768607252,0,0,0,0,0,0,4448,4449,1792,1024,21888,2048,0,0,0,0,0,0,0,0,0} }, //LTW
|
||||
{ 34, new uint[]{784335922,0,0,0,0,0,0,0,5505,5473,1024,5505,2048,0,0,0,0,0,0,0,0,0} }, //WVR
|
||||
{ 35, new uint[]{800064522,0,0,0,0,0,0,20509,5504,2241,1024,1152,2048,0,0,0,0,0,0,0,0,0} }, //ALC
|
||||
{ 36, new uint[]{815793192,0,0,0,0,0,0,5632,34848,1792,1024,25825,2048,0,0,0,0,0,0,0,0,0} }, //CUL
|
||||
|
||||
{ 39, new uint[]{862979092,0,0,0,0,0,0,0,1184,2242,6464,6528,14336,0,0,0,0,0,0,0,0,0} }, //MIN
|
||||
{ 40, new uint[]{878707732,0,0,0,0,0,0,6304,6624,6560,1024,1152,14336,0,0,0,0,0,0,0,0,0} }, //BOT
|
||||
{ 41, new uint[]{894436372,0,0,0,0,0,0,6400,1184,9984,1024,6529,14336,0,0,0,0,0,0,0,0,0} }, //FSH
|
||||
};
|
||||
|
||||
public static uint[] GetEquipmentForClass(uint charClass)
|
||||
{
|
||||
if (equipmentAppearance.ContainsKey(charClass))
|
||||
return equipmentAppearance[charClass];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetClassNameForId(short id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 2: return "pug";
|
||||
case 3: return "gla";
|
||||
case 4: return "mrd";
|
||||
case 7: return "arc";
|
||||
case 8: return "lnc";
|
||||
case 22: return "thm";
|
||||
case 23: return "cnj";
|
||||
case 29: return "crp";
|
||||
case 30: return "bsm";
|
||||
case 31: return "arm";
|
||||
case 32: return "gsm";
|
||||
case 33: return "ltw";
|
||||
case 34: return "wvr";
|
||||
case 35: return "alc";
|
||||
case 36: return "cul";
|
||||
case 39: return "min";
|
||||
case 40: return "btn";
|
||||
case 41: return "fsh";
|
||||
default: return "undefined";
|
||||
}
|
||||
}
|
||||
|
||||
public static uint GetUndershirtForTribe(uint tribe)
|
||||
{
|
||||
uint graphicId;
|
||||
switch (tribe)
|
||||
{
|
||||
case 1:
|
||||
graphicId = 1184;
|
||||
break;
|
||||
case 2:
|
||||
graphicId = 1186;
|
||||
break;
|
||||
case 3:
|
||||
graphicId = 1187;
|
||||
break;
|
||||
case 4:
|
||||
graphicId = 1184;
|
||||
break;
|
||||
case 5:
|
||||
graphicId = 1024;
|
||||
break;
|
||||
case 6:
|
||||
graphicId = 1187;
|
||||
break;
|
||||
case 7:
|
||||
graphicId = 1505;
|
||||
break;
|
||||
case 8:
|
||||
graphicId = 1184;
|
||||
break;
|
||||
case 9:
|
||||
graphicId = 1185;
|
||||
break;
|
||||
case 10:
|
||||
graphicId = 1504;
|
||||
break;
|
||||
case 11:
|
||||
graphicId = 1505;
|
||||
break;
|
||||
case 12:
|
||||
graphicId = 1216;
|
||||
break;
|
||||
case 13:
|
||||
graphicId = 1186;
|
||||
break;
|
||||
case 14:
|
||||
graphicId = 1184;
|
||||
break;
|
||||
case 15:
|
||||
graphicId = 1186;
|
||||
break;
|
||||
default:
|
||||
graphicId = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return graphicId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
98
Lobby Server/ClientConnection.cs
Normal file
98
Lobby Server/ClientConnection.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using Cyotek.Collections.Generic;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class ClientConnection
|
||||
{
|
||||
//Connection stuff
|
||||
public Blowfish blowfish;
|
||||
public Socket socket;
|
||||
public byte[] buffer = new byte[0xffff];
|
||||
public CircularBuffer<byte> incomingStream = new CircularBuffer<byte>(1024);
|
||||
public BlockingCollection<BasePacket> SendPacketQueue = new BlockingCollection<BasePacket>(100);
|
||||
public int lastPartialSize = 0;
|
||||
|
||||
//Instance Stuff
|
||||
public uint currentUserId = 0;
|
||||
public uint currentAccount;
|
||||
public string currentSessionToken;
|
||||
|
||||
//Chara Creation
|
||||
public string newCharaName;
|
||||
public uint newCharaPid;
|
||||
public uint newCharaCid;
|
||||
public ushort newCharaSlot;
|
||||
public ushort newCharaWorldId;
|
||||
|
||||
|
||||
public void ProcessIncoming(int bytesIn)
|
||||
{
|
||||
if (bytesIn == 0)
|
||||
return;
|
||||
|
||||
incomingStream.Put(buffer, 0, bytesIn);
|
||||
}
|
||||
|
||||
public void QueuePacket(BasePacket packet)
|
||||
{
|
||||
SendPacketQueue.Add(packet);
|
||||
}
|
||||
|
||||
public void FlushQueuedSendPackets()
|
||||
{
|
||||
if (!socket.Connected)
|
||||
return;
|
||||
|
||||
while (SendPacketQueue.Count > 0)
|
||||
{
|
||||
BasePacket packet = SendPacketQueue.Take();
|
||||
byte[] packetBytes = packet.GetPacketBytes();
|
||||
byte[] buffer = new byte[0xffff];
|
||||
Array.Copy(packetBytes, buffer, packetBytes.Length);
|
||||
try {
|
||||
socket.Send(packetBytes);
|
||||
}
|
||||
catch(Exception e)
|
||||
{ Program.Log.Error(e, "Weird case, socket was d/ced: {0}"); }
|
||||
}
|
||||
}
|
||||
|
||||
public String GetAddress()
|
||||
{
|
||||
return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port);
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
socket.Disconnect(false);
|
||||
}
|
||||
}
|
||||
}
|
112
Lobby Server/ConfigConstants.cs
Normal file
112
Lobby Server/ConfigConstants.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class ConfigConstants
|
||||
{
|
||||
public static String OPTIONS_BINDIP;
|
||||
public static String OPTIONS_PORT;
|
||||
public static bool OPTIONS_TIMESTAMP = false;
|
||||
|
||||
public static String DATABASE_HOST;
|
||||
public static String DATABASE_PORT;
|
||||
public static String DATABASE_NAME;
|
||||
public static String DATABASE_USERNAME;
|
||||
public static String DATABASE_PASSWORD;
|
||||
|
||||
public static bool Load()
|
||||
{
|
||||
Program.Log.Info("Loading lobby_config.ini file");
|
||||
|
||||
if (!File.Exists("./lobby_config.ini"))
|
||||
{
|
||||
Program.Log.Error("FILE NOT FOUND!");
|
||||
Program.Log.Error("Loading defaults...");
|
||||
}
|
||||
|
||||
INIFile configIni = new INIFile("./lobby_config.ini");
|
||||
|
||||
ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1");
|
||||
ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54994");
|
||||
ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true");
|
||||
|
||||
ConfigConstants.DATABASE_HOST = configIni.GetValue("Database", "host", "");
|
||||
ConfigConstants.DATABASE_PORT = configIni.GetValue("Database", "port", "");
|
||||
ConfigConstants.DATABASE_NAME = configIni.GetValue("Database", "database", "");
|
||||
ConfigConstants.DATABASE_USERNAME = configIni.GetValue("Database", "username", "");
|
||||
ConfigConstants.DATABASE_PASSWORD = configIni.GetValue("Database", "password", "");
|
||||
|
||||
return true;
|
||||
}
|
||||
public static void ApplyLaunchArgs(string[] launchArgs)
|
||||
{
|
||||
var args = (from arg in launchArgs select arg.ToLower().Trim().TrimStart('-')).ToList();
|
||||
|
||||
for (var i = 0; i + 1 < args.Count; i += 2)
|
||||
{
|
||||
var arg = args[i];
|
||||
var val = args[i + 1];
|
||||
var legit = false;
|
||||
|
||||
if (arg == "ip")
|
||||
{
|
||||
IPAddress ip;
|
||||
if (IPAddress.TryParse(val, out ip) && (legit = true))
|
||||
OPTIONS_BINDIP = val;
|
||||
}
|
||||
else if (arg == "port")
|
||||
{
|
||||
UInt16 port;
|
||||
if (UInt16.TryParse(val, out port) && (legit = true))
|
||||
OPTIONS_PORT = val;
|
||||
}
|
||||
else if (arg == "user" && (legit = true))
|
||||
{
|
||||
DATABASE_USERNAME = val;
|
||||
}
|
||||
else if (arg == "p" && (legit = true))
|
||||
{
|
||||
DATABASE_PASSWORD = val;
|
||||
}
|
||||
else if (arg == "db" && (legit = true))
|
||||
{
|
||||
DATABASE_NAME = val;
|
||||
}
|
||||
else if (arg == "host" && (legit = true))
|
||||
{
|
||||
DATABASE_HOST = val;
|
||||
}
|
||||
if (!legit)
|
||||
{
|
||||
Program.Log.Error("Invalid parameter <{0}> for argument: <--{1}> or argument doesnt exist!", val, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
Lobby Server/DataObjects/Account.cs
Normal file
31
Lobby Server/DataObjects/Account.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class Account
|
||||
{
|
||||
public UInt32 id;
|
||||
public string name;
|
||||
}
|
||||
}
|
65
Lobby Server/DataObjects/Appearance.cs
Normal file
65
Lobby Server/DataObjects/Appearance.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class Appearance
|
||||
{
|
||||
////////////
|
||||
//Chara Info
|
||||
public byte size = 0;
|
||||
public byte voice = 0;
|
||||
public ushort skinColor = 0;
|
||||
|
||||
public ushort hairStyle = 0;
|
||||
public ushort hairColor = 0;
|
||||
public ushort hairHighlightColor = 0;
|
||||
public ushort hairVariation = 0;
|
||||
public ushort eyeColor = 0;
|
||||
public byte characteristicsColor = 0;
|
||||
|
||||
public byte faceType = 0;
|
||||
public byte faceEyebrows = 0;
|
||||
public byte faceEyeShape = 0;
|
||||
public byte faceIrisSize = 0;
|
||||
public byte faceNose = 0;
|
||||
public byte faceMouth = 0;
|
||||
public byte faceFeatures = 0;
|
||||
public byte characteristics = 0;
|
||||
public byte ears = 0;
|
||||
|
||||
public uint mainHand = 0;
|
||||
public uint offHand = 0;
|
||||
|
||||
public uint head = 0;
|
||||
public uint body = 0;
|
||||
public uint legs = 0;
|
||||
public uint hands = 0;
|
||||
public uint feet = 0;
|
||||
public uint waist = 0;
|
||||
public uint rightEar = 0;
|
||||
public uint leftEar = 0;
|
||||
public uint rightFinger = 0;
|
||||
public uint leftFinger = 0;
|
||||
//Chara Info
|
||||
////////////
|
||||
}
|
||||
}
|
305
Lobby Server/DataObjects/CharaInfo.cs
Normal file
305
Lobby Server/DataObjects/CharaInfo.cs
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class CharaInfo
|
||||
{
|
||||
public Appearance appearance;
|
||||
|
||||
public struct FaceInfo
|
||||
{
|
||||
[BitfieldLength(5)]
|
||||
public uint characteristics;
|
||||
[BitfieldLength(3)]
|
||||
public uint characteristicsColor;
|
||||
[BitfieldLength(6)]
|
||||
public uint type;
|
||||
[BitfieldLength(2)]
|
||||
public uint ears;
|
||||
[BitfieldLength(2)]
|
||||
public uint mouth;
|
||||
[BitfieldLength(2)]
|
||||
public uint features;
|
||||
[BitfieldLength(3)]
|
||||
public uint nose;
|
||||
[BitfieldLength(3)]
|
||||
public uint eyeShape;
|
||||
[BitfieldLength(1)]
|
||||
public uint irisSize;
|
||||
[BitfieldLength(3)]
|
||||
public uint eyebrows;
|
||||
[BitfieldLength(2)]
|
||||
public uint unknown;
|
||||
}
|
||||
|
||||
public uint guardian = 0;
|
||||
public uint birthMonth = 0;
|
||||
public uint birthDay = 0;
|
||||
public uint currentClass = 0;
|
||||
public uint currentJob = 0;
|
||||
public uint initialTown = 0;
|
||||
public uint tribe = 0;
|
||||
|
||||
public ushort zoneId;
|
||||
public float x, y, z, rot;
|
||||
|
||||
public uint currentLevel = 1;
|
||||
|
||||
public uint weapon1;
|
||||
public uint weapon2;
|
||||
public uint head;
|
||||
public uint body;
|
||||
public uint hands;
|
||||
public uint legs;
|
||||
public uint feet;
|
||||
public uint belt;
|
||||
|
||||
public static CharaInfo GetFromNewCharRequest(String encoded)
|
||||
{
|
||||
byte[] data = Convert.FromBase64String(encoded.Replace('-', '+').Replace('_', '/'));
|
||||
|
||||
CharaInfo info = new CharaInfo();
|
||||
Appearance appearance = new Appearance();
|
||||
|
||||
using (MemoryStream stream = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(stream))
|
||||
{
|
||||
uint version = reader.ReadUInt32();
|
||||
uint unknown1 = reader.ReadUInt32();
|
||||
info.tribe = reader.ReadByte();
|
||||
appearance.size = reader.ReadByte();
|
||||
appearance.hairStyle = reader.ReadUInt16();
|
||||
appearance.hairHighlightColor = reader.ReadByte();
|
||||
appearance.hairVariation = reader.ReadByte();
|
||||
appearance.faceType = reader.ReadByte();
|
||||
appearance.characteristics = reader.ReadByte();
|
||||
appearance.characteristicsColor = reader.ReadByte();
|
||||
|
||||
reader.ReadUInt32();
|
||||
|
||||
appearance.faceEyebrows = reader.ReadByte();
|
||||
appearance.faceIrisSize = reader.ReadByte();
|
||||
appearance.faceEyeShape = reader.ReadByte();
|
||||
appearance.faceNose = reader.ReadByte();
|
||||
appearance.faceFeatures = reader.ReadByte();
|
||||
appearance.faceMouth = reader.ReadByte();
|
||||
appearance.ears = reader.ReadByte();
|
||||
appearance.hairColor = reader.ReadUInt16();
|
||||
|
||||
reader.ReadUInt32();
|
||||
|
||||
appearance.skinColor = reader.ReadUInt16();
|
||||
appearance.eyeColor = reader.ReadUInt16();
|
||||
|
||||
appearance.voice = reader.ReadByte();
|
||||
info.guardian = reader.ReadByte();
|
||||
info.birthMonth = reader.ReadByte();
|
||||
info.birthDay = reader.ReadByte();
|
||||
info.currentClass = reader.ReadUInt16();
|
||||
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
|
||||
reader.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||
|
||||
info.initialTown = reader.ReadByte();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
info.appearance = appearance;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static String BuildForCharaList(Character chara, Appearance appearance)
|
||||
{
|
||||
byte[] data;
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||
{
|
||||
//Build faceinfo for later
|
||||
FaceInfo faceInfo = new FaceInfo();
|
||||
faceInfo.characteristics = appearance.characteristics;
|
||||
faceInfo.characteristicsColor = appearance.characteristicsColor;
|
||||
faceInfo.type = appearance.faceType;
|
||||
faceInfo.ears = appearance.ears;
|
||||
faceInfo.features = appearance.faceFeatures;
|
||||
faceInfo.eyebrows = appearance.faceEyebrows;
|
||||
faceInfo.eyeShape = appearance.faceEyeShape;
|
||||
faceInfo.irisSize = appearance.faceIrisSize;
|
||||
faceInfo.mouth = appearance.faceMouth;
|
||||
faceInfo.nose = appearance.faceNose;
|
||||
|
||||
string location1 = "prv0Inn01\0";
|
||||
string location2 = "defaultTerritory\0";
|
||||
|
||||
writer.Write((UInt32)0x000004c0);
|
||||
writer.Write((UInt32)0x232327ea);
|
||||
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(chara.name + '\0').Length);
|
||||
writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name + '\0'));
|
||||
writer.Write((UInt32)0x1c);
|
||||
writer.Write((UInt32)0x04);
|
||||
writer.Write((UInt32)GetTribeModel(chara.tribe));
|
||||
writer.Write((UInt32)appearance.size);
|
||||
uint colorVal = appearance.skinColor | (uint)(appearance.hairColor << 10) | (uint)(appearance.eyeColor << 20);
|
||||
writer.Write((UInt32)colorVal);
|
||||
|
||||
var bitfield = PrimitiveConversion.ToUInt32(faceInfo);
|
||||
|
||||
writer.Write((UInt32)bitfield); //FACE, Figure this out!
|
||||
uint hairVal = appearance.hairHighlightColor | (uint)(appearance.hairStyle << 10) | (uint)(appearance.characteristicsColor << 20);
|
||||
writer.Write((UInt32)hairVal);
|
||||
writer.Write((UInt32)appearance.voice);
|
||||
writer.Write((UInt32)appearance.mainHand);
|
||||
writer.Write((UInt32)appearance.offHand);
|
||||
|
||||
writer.Write((UInt32)0);
|
||||
writer.Write((UInt32)0);
|
||||
writer.Write((UInt32)0);
|
||||
writer.Write((UInt32)0);
|
||||
writer.Write((UInt32)0);
|
||||
|
||||
writer.Write((UInt32)appearance.head);
|
||||
writer.Write((UInt32)appearance.body);
|
||||
writer.Write((UInt32)appearance.legs);
|
||||
writer.Write((UInt32)appearance.hands);
|
||||
writer.Write((UInt32)appearance.feet);
|
||||
writer.Write((UInt32)appearance.waist);
|
||||
|
||||
writer.Write((UInt32)0);
|
||||
|
||||
writer.Write((UInt32)appearance.rightEar);
|
||||
writer.Write((UInt32)appearance.leftEar);
|
||||
|
||||
writer.Write((UInt32)0);
|
||||
writer.Write((UInt32)0);
|
||||
|
||||
writer.Write((UInt32)appearance.rightFinger);
|
||||
writer.Write((UInt32)appearance.leftFinger);
|
||||
|
||||
for (int i = 0; i < 0x8; i++)
|
||||
writer.Write((byte)0);
|
||||
|
||||
writer.Write((UInt32)1);
|
||||
writer.Write((UInt32)1);
|
||||
|
||||
writer.Write((byte)chara.currentClass);
|
||||
writer.Write((UInt16)chara.currentLevel);
|
||||
writer.Write((byte)chara.currentJob);
|
||||
writer.Write((UInt16)1);
|
||||
writer.Write((byte)chara.tribe);
|
||||
|
||||
writer.Write((UInt32)0xe22222aa);
|
||||
|
||||
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(location1).Length);
|
||||
writer.Write(System.Text.Encoding.UTF8.GetBytes(location1));
|
||||
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(location2).Length);
|
||||
writer.Write(System.Text.Encoding.UTF8.GetBytes(location2));
|
||||
|
||||
writer.Write((byte)chara.guardian);
|
||||
writer.Write((byte)chara.birthMonth);
|
||||
writer.Write((byte)chara.birthDay);
|
||||
|
||||
writer.Write((UInt16)0x17);
|
||||
writer.Write((UInt32)4);
|
||||
writer.Write((UInt32)4);
|
||||
|
||||
writer.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||
|
||||
writer.Write((UInt32)chara.initialTown);
|
||||
writer.Write((UInt32)chara.initialTown);
|
||||
}
|
||||
|
||||
data = stream.GetBuffer();
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
|
||||
}
|
||||
|
||||
public static String Debug()
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes("./packets/charaappearance.bin");
|
||||
|
||||
Program.Log.Debug(Common.Utils.ByteArrayToHex(bytes));
|
||||
|
||||
return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_');
|
||||
}
|
||||
|
||||
public static UInt32 GetTribeModel(byte tribe)
|
||||
{
|
||||
switch (tribe)
|
||||
{
|
||||
//Hyur Midlander Male
|
||||
case 1:
|
||||
default:
|
||||
return 1;
|
||||
|
||||
//Hyur Midlander Female
|
||||
case 2:
|
||||
return 2;
|
||||
|
||||
//Elezen Male
|
||||
case 4:
|
||||
case 6:
|
||||
return 3;
|
||||
|
||||
//Elezen Female
|
||||
case 5:
|
||||
case 7:
|
||||
return 4;
|
||||
|
||||
//Lalafell Male
|
||||
case 8:
|
||||
case 10:
|
||||
return 5;
|
||||
|
||||
//Lalafell Female
|
||||
case 9:
|
||||
case 11:
|
||||
return 6;
|
||||
|
||||
//Miqo'te Female
|
||||
case 12:
|
||||
case 13:
|
||||
return 8;
|
||||
|
||||
//Roegadyn Male
|
||||
case 14:
|
||||
case 15:
|
||||
return 7;
|
||||
|
||||
//Hyur Highlander Male
|
||||
case 3:
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
Lobby Server/DataObjects/Character.cs
Normal file
64
Lobby Server/DataObjects/Character.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class Character
|
||||
{
|
||||
public uint id;
|
||||
public ushort slot;
|
||||
public ushort serverId;
|
||||
public string name;
|
||||
public ushort state;
|
||||
public string charaInfo;
|
||||
public bool isLegacy;
|
||||
public bool doRename;
|
||||
public uint currentZoneId;
|
||||
|
||||
public byte guardian;
|
||||
public byte birthMonth;
|
||||
public byte birthDay;
|
||||
|
||||
public uint currentClass = 3;
|
||||
public uint currentJob = 0;
|
||||
public int currentLevel = 1;
|
||||
|
||||
public byte initialTown;
|
||||
public byte tribe;
|
||||
|
||||
public static CharaInfo EncodedToCharacter(String charaInfo)
|
||||
{
|
||||
charaInfo.Replace("+", "-");
|
||||
charaInfo.Replace("/", "_");
|
||||
byte[] data = System.Convert.FromBase64String(charaInfo);
|
||||
|
||||
Program.Log.Debug("------------Base64 printout------------------");
|
||||
Program.Log.Debug(Common.Utils.ByteArrayToHex(data));
|
||||
Program.Log.Debug("------------Base64 printout------------------");
|
||||
|
||||
CharaInfo chara = new CharaInfo();
|
||||
|
||||
return chara;
|
||||
}
|
||||
}
|
||||
}
|
39
Lobby Server/DataObjects/Retainer.cs
Normal file
39
Lobby Server/DataObjects/Retainer.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class Retainer
|
||||
{
|
||||
public readonly uint id;
|
||||
public readonly uint characterId;
|
||||
public readonly string name;
|
||||
public readonly bool doRename;
|
||||
|
||||
public Retainer(uint characterId, uint retainerId, string name, bool doRename)
|
||||
{
|
||||
this.id = retainerId;
|
||||
this.characterId = characterId;
|
||||
this.name = name;
|
||||
this.doRename = doRename;
|
||||
}
|
||||
}
|
||||
}
|
52
Lobby Server/DataObjects/World.cs
Normal file
52
Lobby Server/DataObjects/World.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
namespace Meteor.Lobby.DataObjects
|
||||
{
|
||||
class World
|
||||
{
|
||||
public readonly ushort id;
|
||||
public readonly string address;
|
||||
public readonly ushort port;
|
||||
public readonly ushort listPosition;
|
||||
public readonly ushort population;
|
||||
public readonly string name;
|
||||
public readonly bool isActive;
|
||||
|
||||
public World(
|
||||
ushort id,
|
||||
string address,
|
||||
ushort port,
|
||||
ushort listPosition,
|
||||
ushort population,
|
||||
string name,
|
||||
bool isActive)
|
||||
{
|
||||
this.id = id;
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.listPosition = listPosition;
|
||||
this.population = population;
|
||||
this.name = name;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
}
|
||||
}
|
746
Lobby Server/Database.cs
Normal file
746
Lobby Server/Database.cs
Normal file
|
@ -0,0 +1,746 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Meteor.Lobby.DataObjects;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
//charState: 0 - Reserved, 1 - Inactive, 2 - Active
|
||||
|
||||
class Database
|
||||
{
|
||||
public static uint GetUserIdFromSession(String sessionId)
|
||||
{
|
||||
uint id = 0;
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
MySqlCommand cmd = new MySqlCommand("SELECT * FROM sessions WHERE id = @sessionId AND expiration > NOW()", conn);
|
||||
cmd.Parameters.AddWithValue("@sessionId", sessionId);
|
||||
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (Reader.Read())
|
||||
{
|
||||
id = Reader.GetUInt32("userId");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static bool ReserveCharacter(uint userId, uint slot, uint serverId, String name, out uint pid, out uint cid)
|
||||
{
|
||||
bool alreadyExists = false;
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
//Check if exists
|
||||
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId AND state != 2 AND state != 1", conn);
|
||||
cmd.Parameters.AddWithValue("@serverId", serverId);
|
||||
cmd.Parameters.AddWithValue("@name", name);
|
||||
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (Reader.HasRows)
|
||||
{
|
||||
alreadyExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Reserve
|
||||
if (!alreadyExists)
|
||||
{
|
||||
MySqlCommand cmd2 = new MySqlCommand();
|
||||
cmd2.Connection = conn;
|
||||
cmd2.CommandText = "INSERT INTO characters(userId, slot, serverId, name, state) VALUES(@userId, @slot, @serverId, @name, 0)";
|
||||
cmd2.Prepare();
|
||||
cmd2.Parameters.AddWithValue("@userId", userId);
|
||||
cmd2.Parameters.AddWithValue("@slot", slot);
|
||||
cmd2.Parameters.AddWithValue("@serverId", serverId);
|
||||
cmd2.Parameters.AddWithValue("@name", name);
|
||||
cmd2.ExecuteNonQuery();
|
||||
cid = (ushort)cmd2.LastInsertedId;
|
||||
pid = 0xBABE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pid = 0;
|
||||
cid = 0;
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
pid = 0;
|
||||
cid = 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
|
||||
Program.Log.Debug("[SQL] CID={0} Created on 'characters' table.", cid);
|
||||
}
|
||||
|
||||
return alreadyExists;
|
||||
}
|
||||
|
||||
public static void MakeCharacter(uint accountId, uint cid, CharaInfo charaInfo)
|
||||
{
|
||||
//Update character entry
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = @"
|
||||
UPDATE characters SET
|
||||
state=2,
|
||||
currentZoneId=@zoneId,
|
||||
positionX=@x,
|
||||
positionY=@y,
|
||||
positionZ=@z,
|
||||
rotation=@r,
|
||||
guardian=@guardian,
|
||||
birthDay=@birthDay,
|
||||
birthMonth=@birthMonth,
|
||||
initialTown=@initialTown,
|
||||
tribe=@tribe
|
||||
WHERE userId=@userId AND id=@cid;
|
||||
|
||||
INSERT INTO characters_appearance
|
||||
(characterId, baseId, size, voice, skinColor, hairStyle, hairColor, hairHighlightColor, hairVariation, eyeColor, faceType, faceEyebrows, faceEyeShape, faceIrisSize, faceNose, faceMouth, faceFeatures, ears, characteristics, characteristicsColor, mainhand, offhand, head, body, hands, legs, feet, waist)
|
||||
VALUES
|
||||
(@cid, 4294967295, @size, @voice, @skinColor, @hairStyle, @hairColor, @hairHighlightColor, @hairVariation, @eyeColor, @faceType, @faceEyebrows, @faceEyeShape, @faceIrisSize, @faceNose, @faceMouth, @faceFeatures, @ears, @characteristics, @characteristicsColor, @mainhand, @offhand, @head, @body, @hands, @legs, @feet, @waist)
|
||||
";
|
||||
cmd.Parameters.AddWithValue("@userId", accountId);
|
||||
cmd.Parameters.AddWithValue("@cid", cid);
|
||||
cmd.Parameters.AddWithValue("@guardian", charaInfo.guardian);
|
||||
cmd.Parameters.AddWithValue("@birthDay", charaInfo.birthDay);
|
||||
cmd.Parameters.AddWithValue("@birthMonth", charaInfo.birthMonth);
|
||||
cmd.Parameters.AddWithValue("@initialTown", charaInfo.initialTown);
|
||||
cmd.Parameters.AddWithValue("@tribe", charaInfo.tribe);
|
||||
|
||||
cmd.Parameters.AddWithValue("@zoneId", charaInfo.zoneId);
|
||||
cmd.Parameters.AddWithValue("@x", charaInfo.x);
|
||||
cmd.Parameters.AddWithValue("@y", charaInfo.y);
|
||||
cmd.Parameters.AddWithValue("@z", charaInfo.z);
|
||||
cmd.Parameters.AddWithValue("@r", charaInfo.rot);
|
||||
|
||||
cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size);
|
||||
cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice);
|
||||
cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor);
|
||||
cmd.Parameters.AddWithValue("@hairStyle", charaInfo.appearance.hairStyle);
|
||||
cmd.Parameters.AddWithValue("@hairColor", charaInfo.appearance.hairColor);
|
||||
cmd.Parameters.AddWithValue("@hairHighlightColor", charaInfo.appearance.hairHighlightColor);
|
||||
cmd.Parameters.AddWithValue("@hairVariation", charaInfo.appearance.hairVariation);
|
||||
cmd.Parameters.AddWithValue("@eyeColor", charaInfo.appearance.eyeColor);
|
||||
cmd.Parameters.AddWithValue("@faceType", charaInfo.appearance.faceType);
|
||||
cmd.Parameters.AddWithValue("@faceEyebrows", charaInfo.appearance.faceEyebrows);
|
||||
cmd.Parameters.AddWithValue("@faceEyeShape", charaInfo.appearance.faceEyeShape);
|
||||
cmd.Parameters.AddWithValue("@faceIrisSize", charaInfo.appearance.faceIrisSize);
|
||||
cmd.Parameters.AddWithValue("@faceNose", charaInfo.appearance.faceNose);
|
||||
cmd.Parameters.AddWithValue("@faceMouth", charaInfo.appearance.faceMouth);
|
||||
cmd.Parameters.AddWithValue("@faceFeatures", charaInfo.appearance.faceFeatures);
|
||||
cmd.Parameters.AddWithValue("@ears", charaInfo.appearance.ears);
|
||||
cmd.Parameters.AddWithValue("@characteristics", charaInfo.appearance.characteristics);
|
||||
cmd.Parameters.AddWithValue("@characteristicsColor", charaInfo.appearance.characteristicsColor);
|
||||
|
||||
cmd.Parameters.AddWithValue("@mainhand", charaInfo.weapon1);
|
||||
cmd.Parameters.AddWithValue("@offhand", charaInfo.weapon2);
|
||||
cmd.Parameters.AddWithValue("@head", charaInfo.head);
|
||||
cmd.Parameters.AddWithValue("@body", charaInfo.body);
|
||||
cmd.Parameters.AddWithValue("@legs", charaInfo.legs);
|
||||
cmd.Parameters.AddWithValue("@hands", charaInfo.hands);
|
||||
cmd.Parameters.AddWithValue("@feet", charaInfo.feet);
|
||||
cmd.Parameters.AddWithValue("@waist", charaInfo.belt);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
conn.Dispose();
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//Create Level and EXP entries
|
||||
try
|
||||
{
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = String.Format("INSERT INTO characters_class_levels(characterId, {0}) VALUES(@characterId, 1); INSERT INTO characters_class_exp(characterId) VALUES(@characterId)", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass));
|
||||
cmd.Prepare();
|
||||
|
||||
cmd.Parameters.AddWithValue("@characterId", cid);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
conn.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
//Create Parameter Save
|
||||
try
|
||||
{
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = String.Format("INSERT INTO characters_parametersave(characterId, hp, hpMax, mp, mpMax, mainSkill, mainSkillLevel) VALUES(@characterId, 1900, 1000, 115, 115, @mainSkill, 1);", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass));
|
||||
cmd.Prepare();
|
||||
|
||||
cmd.Parameters.AddWithValue("@characterId", cid);
|
||||
cmd.Parameters.AddWithValue("@mainSkill", charaInfo.currentClass);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
conn.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
//Create Hotbar
|
||||
try
|
||||
{
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = "SELECT id FROM server_battle_commands WHERE classJob = @classjob AND lvl = 1 ORDER BY id DESC";
|
||||
cmd.Prepare();
|
||||
|
||||
cmd.Parameters.AddWithValue("@classJob", charaInfo.currentClass);
|
||||
List<uint> defaultActions = new List<uint>();
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while(reader.Read())
|
||||
{
|
||||
defaultActions.Add(reader.GetUInt32("id"));
|
||||
}
|
||||
}
|
||||
MySqlCommand cmd2 = new MySqlCommand();
|
||||
cmd2.Connection = conn;
|
||||
cmd2.CommandText = "INSERT INTO characters_hotbar (characterId, classId, hotbarSlot, commandId, recastTime) VALUES (@characterId, @classId, @hotbarSlot, @commandId, 0)";
|
||||
cmd2.Prepare();
|
||||
cmd2.Parameters.AddWithValue("@characterId", cid);
|
||||
cmd2.Parameters.AddWithValue("@classId", charaInfo.currentClass);
|
||||
cmd2.Parameters.Add("@hotbarSlot", MySqlDbType.Int16);
|
||||
cmd2.Parameters.Add("@commandId", MySqlDbType.Int16);
|
||||
|
||||
for(int i = 0; i < defaultActions.Count; i++)
|
||||
{
|
||||
cmd2.Parameters["@hotbarSlot"].Value = i;
|
||||
cmd2.Parameters["@commandId"].Value = defaultActions[i];
|
||||
cmd2.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch(MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Program.Log.Debug("[SQL] CID={0} state updated to active(2).", cid);
|
||||
}
|
||||
|
||||
public static bool RenameCharacter(uint userId, uint characterId, uint serverId, String newName)
|
||||
{
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
//Check if exists
|
||||
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId", conn);
|
||||
cmd.Parameters.AddWithValue("@serverId", serverId);
|
||||
cmd.Parameters.AddWithValue("@name", newName);
|
||||
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (Reader.HasRows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = "UPDATE characters SET name=@name, DoRename=0 WHERE id=@cid AND userId=@uid";
|
||||
cmd.Prepare();
|
||||
cmd.Parameters.AddWithValue("@uid", userId);
|
||||
cmd.Parameters.AddWithValue("@cid", characterId);
|
||||
cmd.Parameters.AddWithValue("@name", newName);
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
|
||||
Program.Log.Debug("[SQL] CID={0} name updated to \"{1}\".", characterId, newName);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteCharacter(uint characterId, String name)
|
||||
{
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = "UPDATE characters SET state=1 WHERE id=@cid AND name=@name";
|
||||
cmd.Prepare();
|
||||
cmd.Parameters.AddWithValue("@cid", characterId);
|
||||
cmd.Parameters.AddWithValue("@name", name);
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Program.Log.Debug("[SQL] CID={0} deleted.", characterId);
|
||||
}
|
||||
|
||||
public static List<World> GetServers()
|
||||
{
|
||||
string query;
|
||||
MySqlCommand cmd;
|
||||
List<World> worldList = new List<World>();
|
||||
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
query = "SELECT * FROM servers WHERE isActive=true";
|
||||
cmd = new MySqlCommand(query, conn);
|
||||
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
ushort id;
|
||||
string address;
|
||||
ushort port;
|
||||
ushort listPosition;
|
||||
ushort population;
|
||||
string name;
|
||||
bool isActive;
|
||||
|
||||
id = reader.GetUInt16("id");
|
||||
address = reader.GetString("address");
|
||||
port = reader.GetUInt16("port");
|
||||
listPosition = reader.GetUInt16("listPosition");
|
||||
population = 2;
|
||||
name = reader.GetString("name");
|
||||
isActive = reader.GetBoolean("isActive");
|
||||
|
||||
worldList.Add(new World(id, address, port, listPosition, population, name, isActive));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
worldList = new List<World>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
return worldList;
|
||||
}
|
||||
|
||||
public static World GetServer(uint serverId)
|
||||
{
|
||||
string query;
|
||||
MySqlCommand cmd;
|
||||
World world = null;
|
||||
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
query = "SELECT * FROM servers WHERE id=@ServerId";
|
||||
cmd = new MySqlCommand(query, conn);
|
||||
cmd.Parameters.AddWithValue("@ServerId", serverId);
|
||||
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
ushort id;
|
||||
string address;
|
||||
ushort port;
|
||||
ushort listPosition;
|
||||
ushort population;
|
||||
string name;
|
||||
bool isActive;
|
||||
|
||||
id = reader.GetUInt16("id");
|
||||
address = reader.GetString("address");
|
||||
port = reader.GetUInt16("port");
|
||||
listPosition = reader.GetUInt16("listPosition");
|
||||
population = 2; //TODO
|
||||
name = reader.GetString("name");
|
||||
isActive = reader.GetBoolean("isActive");
|
||||
|
||||
world = new World(id, address, port, listPosition, population, name, isActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public static List<Character> GetCharacters(uint userId)
|
||||
{
|
||||
List<Character> characters = new List<Character>();
|
||||
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
//Load basic info
|
||||
string query = @"
|
||||
SELECT
|
||||
id,
|
||||
slot,
|
||||
serverId,
|
||||
name,
|
||||
isLegacy,
|
||||
doRename,
|
||||
currentZoneId,
|
||||
guardian,
|
||||
birthMonth,
|
||||
birthDay,
|
||||
initialTown,
|
||||
tribe,
|
||||
mainSkill,
|
||||
mainSkillLevel
|
||||
FROM characters
|
||||
INNER JOIN characters_parametersave ON id = characters_parametersave.characterId
|
||||
WHERE userId = @userId AND state = 2
|
||||
ORDER BY slot";
|
||||
MySqlCommand cmd = new MySqlCommand(query, conn);
|
||||
cmd.Parameters.AddWithValue("@userId", userId);
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Character chara = new Character();
|
||||
chara.id = reader.GetUInt32("id");
|
||||
chara.slot = reader.GetUInt16("slot");
|
||||
chara.serverId = reader.GetUInt16("serverId");
|
||||
chara.name = reader.GetString("name");
|
||||
chara.isLegacy = reader.GetBoolean("isLegacy");
|
||||
chara.doRename = reader.GetBoolean("doRename");
|
||||
chara.currentZoneId = reader.GetUInt32("currentZoneId");
|
||||
chara.guardian = reader.GetByte("guardian");
|
||||
chara.birthMonth = reader.GetByte("birthMonth");
|
||||
chara.birthDay = reader.GetByte("birthDay");
|
||||
chara.initialTown = reader.GetByte("initialTown");
|
||||
chara.tribe = reader.GetByte("tribe");
|
||||
chara.currentClass = reader.GetByte("mainSkill");
|
||||
//chara.currentJob = ???
|
||||
chara.currentLevel = reader.GetInt16("mainSkillLevel");
|
||||
characters.Add(chara);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return characters;
|
||||
}
|
||||
|
||||
public static Character GetCharacter(uint userId, uint charId)
|
||||
{
|
||||
Character chara = null;
|
||||
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
string query = @"
|
||||
SELECT
|
||||
id,
|
||||
slot,
|
||||
serverId,
|
||||
name,
|
||||
isLegacy,
|
||||
doRename,
|
||||
currentZoneId,
|
||||
guardian,
|
||||
birthMonth,
|
||||
birthDay,
|
||||
initialTown,
|
||||
tribe,
|
||||
mainSkill,
|
||||
mainSkillLevel
|
||||
FROM characters
|
||||
INNER JOIN characters_parametersave ON id = characters_parametersave.characterId
|
||||
WHERE id = @charId";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(query, conn);
|
||||
cmd.Parameters.AddWithValue("@charId", charId);
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
chara = new Character();
|
||||
chara.id = reader.GetUInt32("id");
|
||||
chara.slot = reader.GetUInt16("slot");
|
||||
chara.serverId = reader.GetUInt16("serverId");
|
||||
chara.name = reader.GetString("name");
|
||||
chara.isLegacy = reader.GetBoolean("isLegacy");
|
||||
chara.doRename = reader.GetBoolean("doRename");
|
||||
chara.currentZoneId = reader.GetUInt32("currentZoneId");
|
||||
chara.guardian = reader.GetByte("guardian");
|
||||
chara.birthMonth = reader.GetByte("birthMonth");
|
||||
chara.birthDay = reader.GetByte("birthDay");
|
||||
chara.initialTown = reader.GetByte("initialTown");
|
||||
chara.tribe = reader.GetByte("tribe");
|
||||
chara.currentClass = reader.GetByte("mainSkill");
|
||||
//chara.currentJob = ???
|
||||
chara.currentLevel = reader.GetInt16("mainSkillLevel");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
return chara;
|
||||
}
|
||||
|
||||
public static Appearance GetAppearance(uint charaId)
|
||||
{
|
||||
Appearance appearance = new Appearance();
|
||||
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
//Load appearance
|
||||
string query = @"
|
||||
SELECT
|
||||
baseId,
|
||||
size,
|
||||
voice,
|
||||
skinColor,
|
||||
hairStyle,
|
||||
hairColor,
|
||||
hairHighlightColor,
|
||||
eyeColor,
|
||||
characteristics,
|
||||
characteristicsColor,
|
||||
faceType,
|
||||
ears,
|
||||
faceMouth,
|
||||
faceFeatures,
|
||||
faceNose,
|
||||
faceEyeShape,
|
||||
faceIrisSize,
|
||||
faceEyebrows,
|
||||
mainHand,
|
||||
offHand,
|
||||
head,
|
||||
body,
|
||||
legs,
|
||||
hands,
|
||||
feet,
|
||||
waist,
|
||||
leftFinger,
|
||||
rightFinger,
|
||||
leftEar,
|
||||
rightEar
|
||||
FROM characters_appearance WHERE characterId = @charaId";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(query, conn);
|
||||
cmd.Parameters.AddWithValue("@charaId", charaId);
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
appearance.size = reader.GetByte("size");
|
||||
appearance.voice = reader.GetByte("voice");
|
||||
appearance.skinColor = reader.GetUInt16("skinColor");
|
||||
appearance.hairStyle = reader.GetUInt16("hairStyle");
|
||||
appearance.hairColor = reader.GetUInt16("hairColor");
|
||||
appearance.hairHighlightColor = reader.GetUInt16("hairHighlightColor");
|
||||
appearance.eyeColor = reader.GetUInt16("eyeColor");
|
||||
appearance.characteristics = reader.GetByte("characteristics");
|
||||
appearance.characteristicsColor = reader.GetByte("characteristicsColor");
|
||||
appearance.faceType = reader.GetByte("faceType");
|
||||
appearance.ears = reader.GetByte("ears");
|
||||
appearance.faceMouth = reader.GetByte("faceMouth");
|
||||
appearance.faceFeatures = reader.GetByte("faceFeatures");
|
||||
appearance.faceNose = reader.GetByte("faceNose");
|
||||
appearance.faceEyeShape = reader.GetByte("faceEyeShape");
|
||||
appearance.faceIrisSize = reader.GetByte("faceIrisSize");
|
||||
appearance.faceEyebrows = reader.GetByte("faceEyebrows");
|
||||
|
||||
appearance.mainHand = reader.GetUInt32("mainHand");
|
||||
appearance.offHand = reader.GetUInt32("offHand");
|
||||
appearance.head = reader.GetUInt32("head");
|
||||
appearance.body = reader.GetUInt32("body");
|
||||
appearance.mainHand = reader.GetUInt32("mainHand");
|
||||
appearance.legs = reader.GetUInt32("legs");
|
||||
appearance.hands = reader.GetUInt32("hands");
|
||||
appearance.feet = reader.GetUInt32("feet");
|
||||
appearance.waist = reader.GetUInt32("waist");
|
||||
appearance.leftFinger = reader.GetUInt32("leftFinger");
|
||||
appearance.rightFinger = reader.GetUInt32("rightFinger");
|
||||
appearance.leftEar = reader.GetUInt32("leftEar");
|
||||
appearance.rightEar = reader.GetUInt32("rightEar");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return appearance;
|
||||
}
|
||||
|
||||
public static List<String> GetReservedNames(uint userId)
|
||||
{
|
||||
List<String> reservedNames = new List<String>();
|
||||
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
string query = "SELECT name FROM reserved_names WHERE userId=@UserId";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(query, conn);
|
||||
cmd.Parameters.AddWithValue("@UserId", userId);
|
||||
using (MySqlDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
reservedNames.Add(reader.GetString("name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
conn.Dispose();
|
||||
}
|
||||
}
|
||||
return reservedNames;
|
||||
}
|
||||
|
||||
public static List<Retainer> GetRetainers(uint userId)
|
||||
{
|
||||
return new List<Retainer>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
167
Lobby Server/Lobby Server.csproj
Normal file
167
Lobby Server/Lobby Server.csproj
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" />
|
||||
<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>{703091E0-F69C-4177-8FAE-C258AC6A65AA}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Meteor.Lobby</RootNamespace>
|
||||
<AssemblyName>Lobby Server</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<NuGetPackageImportStamp>cc1ba6f5</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Cyotek.Collections.Generic.CircularBuffer">
|
||||
<HintPath>..\packages\Cyotek.CircularBuffer.1.0.0.0\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FFXIVClassic.Common">
|
||||
<HintPath>..\FFXIVClassic Common Class Lib\bin\Debug\FFXIVClassic.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DataObjects\Account.cs" />
|
||||
<Compile Include="DataObjects\Appearance.cs" />
|
||||
<Compile Include="DataObjects\CharaInfo.cs" />
|
||||
<Compile Include="DataObjects\Retainer.cs" />
|
||||
<Compile Include="DataObjects\Character.cs" />
|
||||
<Compile Include="ClientConnection.cs" />
|
||||
<Compile Include="ConfigConstants.cs" />
|
||||
<Compile Include="Database.cs" />
|
||||
<Compile Include="DataObjects\World.cs" />
|
||||
<Compile Include="PacketProcessor.cs" />
|
||||
<Compile Include="Packets\Receive\CharacterModifyPacket.cs" />
|
||||
<Compile Include="Packets\Receive\SecurityHandshakePacket.cs" />
|
||||
<Compile Include="Packets\Receive\SelectCharacterPacket.cs" />
|
||||
<Compile Include="Packets\Receive\SessionPacket.cs" />
|
||||
<Compile Include="Packets\Send\CharaCreatorPacket.cs" />
|
||||
<Compile Include="Packets\Send\RetainerListPacket.cs" />
|
||||
<Compile Include="Packets\Send\ErrorPacket.cs" />
|
||||
<Compile Include="Packets\HardCoded_Packets.cs" />
|
||||
<Compile Include="Packets\Send\SelectCharacterConfirmPacket.cs" />
|
||||
<Compile Include="Packets\Send\CharacterListPacket.cs" />
|
||||
<Compile Include="Packets\Send\ImportListPacket.cs" />
|
||||
<Compile Include="Packets\Send\AccountListPacket.cs" />
|
||||
<Compile Include="Packets\Send\WorldListPacket.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Server.cs" />
|
||||
<Compile Include="CharacterCreatorUtils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="NLog.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="app.config" />
|
||||
<None Include="NLog.xsd">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(SolutionDir)data\lobby_config.ini" "$(SolutionDir)$(ProjectName)\$(OutDir)" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props'))" />
|
||||
</Target>
|
||||
<!-- 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>
|
62
Lobby Server/NLog.config
Normal file
62
Lobby Server/NLog.config
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
|
||||
autoReload="true"
|
||||
throwExceptions="false"
|
||||
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
|
||||
|
||||
|
||||
<!-- optional, add some variabeles
|
||||
https://github.com/nlog/NLog/wiki/Configuration-file#variables
|
||||
-->
|
||||
<variable name="myvar" value="myvalue" />
|
||||
|
||||
<!--
|
||||
See https://github.com/nlog/nlog/wiki/Configuration-file
|
||||
for information on customizing logging rules and outputs.
|
||||
-->
|
||||
<targets async="true">
|
||||
|
||||
<!--
|
||||
add your targets here
|
||||
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
|
||||
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Write events to a file with the date in the filename.
|
||||
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
|
||||
layout="${longdate} ${uppercase:${level}} ${message}" />
|
||||
-->
|
||||
<!--<target xsi:type="ColoredConsole" name="console" layout="[${longdate}] [${uppercase:${level}}] ${message}" />-->
|
||||
<target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}/map.log"
|
||||
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
|
||||
<target xsi:type="ColoredConsole" name="console"
|
||||
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
|
||||
<target xsi:type="ColoredConsole" name="packets"
|
||||
layout="${message}">
|
||||
<highlight-row
|
||||
condition="equals('${logger}', 'Meteor.Common.BasePacket') and equals('${event-context:item=color}', '6')"
|
||||
backgroundColor="DarkYellow" foregroundColor="NoChange" />
|
||||
<highlight-row
|
||||
condition="equals('${logger}', 'Meteor.Common.SubPacket') and equals('${event-context:item=color}', '4')"
|
||||
backgroundColor="DarkRed" foregroundColor="NoChange" />
|
||||
<highlight-row
|
||||
condition="equals('${logger}', 'Meteor.Common.SubPacket') and equals('${event-context:item=color}', '5')"
|
||||
backgroundColor="DarkMagenta" foregroundColor="NoChange" />
|
||||
</target>
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<!-- add your logging rules here -->
|
||||
<logger name='*' minlevel='Trace' writeTo='file' />
|
||||
<logger name='Meteor.Lobby.Program' minlevel='Trace' writeTo='console' />
|
||||
<logger name='Meteor.Common.*' minlevel='Debug' writeTo='packets' />
|
||||
<!--
|
||||
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
|
||||
<logger name="*" minlevel="Debug" writeTo="f" />
|
||||
-->
|
||||
</rules>
|
||||
</nlog>
|
2601
Lobby Server/NLog.xsd
Normal file
2601
Lobby Server/NLog.xsd
Normal file
File diff suppressed because it is too large
Load diff
411
Lobby Server/PacketProcessor.cs
Normal file
411
Lobby Server/PacketProcessor.cs
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
using Meteor.Common;
|
||||
using Meteor.Lobby.DataObjects;
|
||||
using Meteor.Lobby.Packets;
|
||||
using Meteor.Lobby.Packets.Receive;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class PacketProcessor
|
||||
{
|
||||
|
||||
public void ProcessPacket(ClientConnection client, BasePacket packet)
|
||||
{
|
||||
|
||||
if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T')) //Test Ticket Data
|
||||
{
|
||||
packet.DebugPrintPacket();
|
||||
//Crypto handshake
|
||||
ProcessStartSession(client, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
BasePacket.DecryptPacket(client.blowfish, ref packet);
|
||||
|
||||
packet.DebugPrintPacket();
|
||||
|
||||
List<SubPacket> subPackets = packet.GetSubpackets();
|
||||
foreach (SubPacket subpacket in subPackets)
|
||||
{
|
||||
subpacket.DebugPrintSubPacket();
|
||||
|
||||
if (subpacket.header.type == 3)
|
||||
{
|
||||
switch (subpacket.gameMessage.opcode)
|
||||
{
|
||||
case 0x03:
|
||||
ProcessGetCharacters(client, subpacket);
|
||||
break;
|
||||
case 0x04:
|
||||
ProcessSelectCharacter(client, subpacket);
|
||||
break;
|
||||
case 0x05:
|
||||
ProcessSessionAcknowledgement(client, subpacket);
|
||||
break;
|
||||
case 0x0B:
|
||||
ProcessModifyCharacter(client, subpacket);
|
||||
break;
|
||||
case 0x0F:
|
||||
//Mod Retainers
|
||||
default:
|
||||
Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessStartSession(ClientConnection client, BasePacket packet)
|
||||
{
|
||||
SecurityHandshakePacket securityHandshake = new SecurityHandshakePacket(packet.data);
|
||||
|
||||
byte[] blowfishKey = GenerateKey(securityHandshake.ticketPhrase, securityHandshake.clientNumber);
|
||||
client.blowfish = new Blowfish(blowfishKey);
|
||||
|
||||
Program.Log.Info("SecCNum: 0x{0:X}", securityHandshake.clientNumber);
|
||||
|
||||
//Respond with acknowledgment
|
||||
BasePacket outgoingPacket = new BasePacket(HardCoded_Packets.g_secureConnectionAcknowledgment);
|
||||
BasePacket.EncryptPacket(client.blowfish, outgoingPacket);
|
||||
client.QueuePacket(outgoingPacket);
|
||||
}
|
||||
|
||||
private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
packet.DebugPrintSubPacket();
|
||||
SessionPacket sessionPacket = new SessionPacket(packet.data);
|
||||
String clientVersion = sessionPacket.version;
|
||||
|
||||
Program.Log.Info("Got acknowledgment for secure session.");
|
||||
Program.Log.Info("CLIENT VERSION: {0}", clientVersion);
|
||||
|
||||
uint userId = Database.GetUserIdFromSession(sessionPacket.session);
|
||||
client.currentUserId = userId;
|
||||
client.currentSessionToken = sessionPacket.session; ;
|
||||
|
||||
if (userId == 0)
|
||||
{
|
||||
ErrorPacket errorPacket = new ErrorPacket(sessionPacket.sequence, 0, 0, 13001, "Your session has expired, please login again.");
|
||||
SubPacket subpacket = errorPacket.BuildPacket();
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
BasePacket errorBasePacket = BasePacket.CreatePacket(subpacket, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, errorBasePacket);
|
||||
client.QueuePacket(errorBasePacket);
|
||||
|
||||
Program.Log.Info("Invalid session, kicking...");
|
||||
return;
|
||||
}
|
||||
|
||||
Program.Log.Info("USER ID: {0}", userId);
|
||||
|
||||
List<Account> accountList = new List<Account>();
|
||||
Account defaultAccount = new Account();
|
||||
defaultAccount.id = 1;
|
||||
defaultAccount.name = "FINAL FANTASY XIV";
|
||||
accountList.Add(defaultAccount);
|
||||
AccountListPacket listPacket = new AccountListPacket(1, accountList);
|
||||
BasePacket basePacket = BasePacket.CreatePacket(listPacket.BuildPackets(), true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
}
|
||||
|
||||
private void ProcessGetCharacters(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
Program.Log.Info("{0} => Get characters", client.currentUserId == 0 ? client.GetAddress() : "User " + client.currentUserId);
|
||||
|
||||
SendWorldList(client, packet);
|
||||
SendImportList(client, packet);
|
||||
SendRetainerList(client, packet);
|
||||
SendCharacterList(client, packet);
|
||||
|
||||
}
|
||||
|
||||
private void ProcessSelectCharacter(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
SelectCharacterPacket selectCharRequest = new SelectCharacterPacket(packet.data);
|
||||
|
||||
Program.Log.Info("{0} => Select character id {1}", client.currentUserId == 0 ? client.GetAddress() : "User " + client.currentUserId, selectCharRequest.characterId);
|
||||
|
||||
Character chara = Database.GetCharacter(client.currentUserId, selectCharRequest.characterId);
|
||||
World world = null;
|
||||
|
||||
if (chara != null)
|
||||
world = Database.GetServer(chara.serverId);
|
||||
|
||||
if (world == null)
|
||||
{
|
||||
ErrorPacket errorPacket = new ErrorPacket(selectCharRequest.sequence, 0, 0, 13001, "World Does not exist or is inactive.");
|
||||
SubPacket subpacket = errorPacket.BuildPacket();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
return;
|
||||
}
|
||||
|
||||
SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket(selectCharRequest.sequence, selectCharRequest.characterId, client.currentSessionToken, world.address, world.port, selectCharRequest.ticket);
|
||||
|
||||
BasePacket outgoingPacket = BasePacket.CreatePacket(connectCharacter.BuildPackets(), true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, outgoingPacket);
|
||||
client.QueuePacket(outgoingPacket);
|
||||
}
|
||||
|
||||
private void ProcessModifyCharacter(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
CharacterModifyPacket charaReq = new CharacterModifyPacket(packet.data);
|
||||
var slot = charaReq.slot;
|
||||
var name = charaReq.characterName;
|
||||
var worldId = charaReq.worldId;
|
||||
|
||||
uint pid = 0, cid = 0;
|
||||
|
||||
//Get world from new char instance
|
||||
if (worldId == 0)
|
||||
worldId = client.newCharaWorldId;
|
||||
|
||||
//Check if this character exists, Get world from there
|
||||
if (worldId == 0 && charaReq.characterId != 0)
|
||||
{
|
||||
Character chara = Database.GetCharacter(client.currentUserId, charaReq.characterId);
|
||||
if (chara != null)
|
||||
worldId = chara.serverId;
|
||||
}
|
||||
|
||||
string worldName = null;
|
||||
World world = Database.GetServer(worldId);
|
||||
if (world != null)
|
||||
worldName = world.name;
|
||||
|
||||
if (worldName == null)
|
||||
{
|
||||
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 0, 0, 13001, "World Does not exist or is inactive.");
|
||||
SubPacket subpacket = errorPacket.BuildPacket();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
|
||||
Program.Log.Info("User {0} => Error; invalid server id: \"{1}\"", client.currentUserId, worldId);
|
||||
return;
|
||||
}
|
||||
|
||||
bool alreadyTaken;
|
||||
|
||||
switch (charaReq.command)
|
||||
{
|
||||
case 0x01://Reserve
|
||||
|
||||
alreadyTaken = Database.ReserveCharacter(client.currentUserId, slot, worldId, name, out pid, out cid);
|
||||
|
||||
if (alreadyTaken)
|
||||
{
|
||||
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
|
||||
SubPacket subpacket = errorPacket.BuildPacket();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
|
||||
Program.Log.Info("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
pid = 0;
|
||||
client.newCharaCid = cid;
|
||||
client.newCharaSlot = slot;
|
||||
client.newCharaWorldId = worldId;
|
||||
client.newCharaName = name;
|
||||
}
|
||||
|
||||
Program.Log.Info("User {0} => Character reserved \"{1}\"", client.currentUserId, name);
|
||||
break;
|
||||
case 0x02://Make
|
||||
CharaInfo info = CharaInfo.GetFromNewCharRequest(charaReq.characterInfoEncoded);
|
||||
|
||||
//Set Initial Appearance (items will be loaded in by map server)
|
||||
uint[] classAppearance = CharacterCreatorUtils.GetEquipmentForClass(info.currentClass);
|
||||
info.weapon1 = classAppearance[0];
|
||||
info.weapon2 = classAppearance[1];
|
||||
info.head = classAppearance[7];
|
||||
|
||||
if (classAppearance[8] != 0)
|
||||
info.body = classAppearance[8];
|
||||
else
|
||||
info.body = CharacterCreatorUtils.GetUndershirtForTribe(info.tribe);
|
||||
|
||||
info.legs = classAppearance[9];
|
||||
info.hands = classAppearance[10];
|
||||
info.feet = classAppearance[11];
|
||||
info.belt = classAppearance[12];
|
||||
|
||||
//Set Initial Position
|
||||
switch (info.initialTown)
|
||||
{
|
||||
case 1: //ocn0Battle02 (Limsa)
|
||||
info.zoneId = 193;
|
||||
info.x = 0.016f;
|
||||
info.y = 10.35f;
|
||||
info.z = -36.91f;
|
||||
info.rot = 0.025f;
|
||||
break;
|
||||
case 2: //fst0Battle03 (Gridania)
|
||||
info.zoneId = 166;
|
||||
info.x = 369.5434f;
|
||||
info.y = 4.21f;
|
||||
info.z = -706.1074f;
|
||||
info.rot = -1.26721f;
|
||||
break;
|
||||
case 3: //wil0Battle01 (Ul'dah)
|
||||
info.zoneId = 184;
|
||||
info.x = 5.364327f;
|
||||
info.y = 196.0f;
|
||||
info.z = 133.6561f;
|
||||
info.rot = -2.849384f;
|
||||
break;
|
||||
}
|
||||
|
||||
Database.MakeCharacter(client.currentUserId, client.newCharaCid, info);
|
||||
|
||||
pid = 1;
|
||||
cid = client.newCharaCid;
|
||||
name = client.newCharaName;
|
||||
|
||||
Program.Log.Info("User {0} => Character Created \"{1}\"", client.currentUserId, name);
|
||||
break;
|
||||
case 0x03://Rename
|
||||
|
||||
alreadyTaken = Database.RenameCharacter(client.currentUserId, charaReq.characterId, worldId, charaReq.characterName);
|
||||
|
||||
if (alreadyTaken)
|
||||
{
|
||||
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
|
||||
SubPacket subpacket = errorPacket.BuildPacket();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
|
||||
Program.Log.Info("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName);
|
||||
return;
|
||||
}
|
||||
|
||||
Program.Log.Info("User {0} => Character renamed \"{1}\"", client.currentUserId, name);
|
||||
break;
|
||||
case 0x04://Delete
|
||||
Database.DeleteCharacter(charaReq.characterId, charaReq.characterName);
|
||||
|
||||
Program.Log.Info("User {0} => Character deleted \"{1}\"", client.currentUserId, name);
|
||||
break;
|
||||
case 0x06://Rename Retainer
|
||||
|
||||
Program.Log.Info("User {0} => Retainer renamed \"{1}\"", client.currentUserId, name);
|
||||
break;
|
||||
}
|
||||
|
||||
CharaCreatorPacket charaCreator = new CharaCreatorPacket(charaReq.sequence, charaReq.command, pid, cid, 1, name, worldName);
|
||||
BasePacket charaCreatorPacket = BasePacket.CreatePacket(charaCreator.BuildPacket(), true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, charaCreatorPacket);
|
||||
client.QueuePacket(charaCreatorPacket);
|
||||
|
||||
}
|
||||
|
||||
private void SendWorldList(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
List<World> serverList = Database.GetServers();
|
||||
WorldListPacket worldlistPacket = new WorldListPacket(0, serverList);
|
||||
List<SubPacket> subPackets = worldlistPacket.BuildPackets();
|
||||
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
|
||||
}
|
||||
|
||||
private void SendImportList(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
List<String> names = Database.GetReservedNames(client.currentUserId);
|
||||
|
||||
ImportListPacket importListPacket = new ImportListPacket(0, names);
|
||||
List<SubPacket> subPackets = importListPacket.BuildPackets();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
}
|
||||
|
||||
private void SendRetainerList(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
List<Retainer> retainers = Database.GetRetainers(client.currentUserId);
|
||||
|
||||
RetainerListPacket retainerListPacket = new RetainerListPacket(0, retainers);
|
||||
List<SubPacket> subPackets = retainerListPacket.BuildPackets();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
}
|
||||
|
||||
private void SendCharacterList(ClientConnection client, SubPacket packet)
|
||||
{
|
||||
List<Character> characterList = Database.GetCharacters(client.currentUserId);
|
||||
|
||||
if (characterList.Count > 8)
|
||||
Program.Log.Error("Warning, got more than 8 characters. List truncated, check DB for issues.");
|
||||
|
||||
CharacterListPacket characterlistPacket = new CharacterListPacket(0, characterList);
|
||||
List<SubPacket> subPackets = characterlistPacket.BuildPackets();
|
||||
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
|
||||
BasePacket.EncryptPacket(client.blowfish, basePacket);
|
||||
client.QueuePacket(basePacket);
|
||||
}
|
||||
|
||||
private byte[] GenerateKey(string ticketPhrase, uint clientNumber)
|
||||
{
|
||||
byte[] key;
|
||||
using (MemoryStream memStream = new MemoryStream(0x2C))
|
||||
{
|
||||
using (BinaryWriter binWriter = new BinaryWriter(memStream))
|
||||
{
|
||||
binWriter.Write((Byte)0x78);
|
||||
binWriter.Write((Byte)0x56);
|
||||
binWriter.Write((Byte)0x34);
|
||||
binWriter.Write((Byte)0x12);
|
||||
binWriter.Write((UInt32)clientNumber);
|
||||
binWriter.Write((Byte)0xE8);
|
||||
binWriter.Write((Byte)0x03);
|
||||
binWriter.Write((Byte)0x00);
|
||||
binWriter.Write((Byte)0x00);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(ticketPhrase), 0, Encoding.ASCII.GetByteCount(ticketPhrase) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(ticketPhrase));
|
||||
}
|
||||
byte[] nonMD5edKey = memStream.GetBuffer();
|
||||
|
||||
using (MD5 md5Hash = MD5.Create())
|
||||
{
|
||||
key = md5Hash.ComputeHash(nonMD5edKey);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
73
Lobby Server/Packets/HardCoded_Packets.cs
Normal file
73
Lobby Server/Packets/HardCoded_Packets.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class HardCoded_Packets
|
||||
{
|
||||
public static byte[] g_secureConnectionAcknowledgment =
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0xA0, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x90, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0xED, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x10, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
|
||||
0x42, 0x82, 0x63, 0x52, 0x01, 0x00, 0x00, 0x00, 0x10, 0xEF, 0xDF, 0xFF, 0x53, 0x61, 0x6D, 0x70,
|
||||
0x6C, 0x65, 0x20, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x52, 0x75, 0x6E, 0x52, 0x75, 0x6E,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0xF7, 0xAF, 0xAF, 0xF7, 0x00, 0x00, 0xB8, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2C, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x4F, 0x62,
|
||||
0x6A, 0x65, 0x63, 0x74, 0x2E, 0x2E, 0x2E, 0x5B, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39,
|
||||
0x39, 0x2E, 0x38, 0x32, 0x3A, 0x36, 0x33, 0x34, 0x30, 0x37, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x70, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x6C, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x32, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xAF, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xFE, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0B, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0xCC, 0xCC, 0x0C, 0x69, 0x00, 0xE0, 0xD0, 0x58, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
|
||||
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0xF0, 0xEE, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
|
||||
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x34, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00,
|
||||
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x18, 0xBE, 0x34, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0xD8, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0xF7, 0xAF, 0x42, 0x82, 0x63, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0x00,
|
||||
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0xFF,
|
||||
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x24, 0xCF, 0x76, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x70, 0x7A, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD1, 0xF3, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xE8, 0x3E, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x70, 0x99, 0xAA, 0x01, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x58, 0x59, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x05, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0C, 0x69, 0x00, 0xE0, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xF0, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x23, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0x5A, 0x33, 0x02, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
66
Lobby Server/Packets/Receive/CharacterModifyPacket.cs
Normal file
66
Lobby Server/Packets/Receive/CharacterModifyPacket.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Lobby.Packets.Receive
|
||||
{
|
||||
class CharacterModifyPacket
|
||||
{
|
||||
public UInt64 sequence;
|
||||
public uint characterId;
|
||||
public uint personType;
|
||||
public byte slot;
|
||||
public byte command;
|
||||
public ushort worldId;
|
||||
public String characterName;
|
||||
public String characterInfoEncoded;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public CharacterModifyPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
sequence = binReader.ReadUInt64();
|
||||
characterId = binReader.ReadUInt32();
|
||||
personType = binReader.ReadUInt32();
|
||||
slot = binReader.ReadByte();
|
||||
command = binReader.ReadByte();
|
||||
worldId = binReader.ReadUInt16();
|
||||
|
||||
characterName = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
|
||||
characterInfoEncoded = Encoding.ASCII.GetString(binReader.ReadBytes(0x190)).Trim(new[] { '\0' });
|
||||
}
|
||||
catch (Exception){
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
Lobby Server/Packets/Receive/SecurityHandshakePacket.cs
Normal file
54
Lobby Server/Packets/Receive/SecurityHandshakePacket.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Lobby.Packets.Receive
|
||||
{
|
||||
class SecurityHandshakePacket
|
||||
{
|
||||
public string ticketPhrase;
|
||||
public uint clientNumber;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public SecurityHandshakePacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
binReader.BaseStream.Seek(0x34, SeekOrigin.Begin);
|
||||
ticketPhrase = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
|
||||
clientNumber = binReader.ReadUInt32();
|
||||
}
|
||||
catch (Exception){
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
Lobby Server/Packets/Receive/SelectCharacterPacket.cs
Normal file
57
Lobby Server/Packets/Receive/SelectCharacterPacket.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Meteor.Lobby.Packets.Receive
|
||||
{
|
||||
class SelectCharacterPacket
|
||||
{
|
||||
public UInt64 sequence;
|
||||
public uint characterId;
|
||||
public uint unknownId;
|
||||
public UInt64 ticket;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public SelectCharacterPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
sequence = binReader.ReadUInt64();
|
||||
characterId = binReader.ReadUInt32();
|
||||
unknownId = binReader.ReadUInt32();
|
||||
ticket = binReader.ReadUInt64();
|
||||
}
|
||||
catch (Exception){
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
56
Lobby Server/Packets/Receive/SessionPacket.cs
Normal file
56
Lobby Server/Packets/Receive/SessionPacket.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Lobby.Packets.Receive
|
||||
{
|
||||
class SessionPacket
|
||||
{
|
||||
public bool invalidPacket = false;
|
||||
public UInt64 sequence;
|
||||
public String session;
|
||||
public String version;
|
||||
|
||||
public SessionPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
sequence = binReader.ReadUInt64();
|
||||
binReader.ReadUInt32();
|
||||
binReader.ReadUInt32();
|
||||
session = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
|
||||
version = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
|
||||
}
|
||||
catch (Exception){
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
Lobby Server/Packets/Send/AccountListPacket.cs
Normal file
121
Lobby Server/Packets/Send/AccountListPacket.cs
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
using Meteor.Lobby.DataObjects;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class AccountListPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x0C;
|
||||
public const ushort MAXPERPACKET = 8;
|
||||
|
||||
private UInt64 sequence;
|
||||
private List<Account> accountList;
|
||||
|
||||
public AccountListPacket(UInt64 sequence, List<Account> accountList)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.accountList = accountList;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
int accountCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
MemoryStream memStream = null;
|
||||
BinaryWriter binWriter = null;
|
||||
|
||||
foreach (Account account in accountList)
|
||||
{
|
||||
if (totalCount == 0 || accountCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x280);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (UInt32)(accountList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
//Write Entries
|
||||
binWriter.Write((UInt32)account.id);
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(account.name.PadRight(0x40, '\0')));
|
||||
|
||||
accountCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of world list
|
||||
if (accountCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
accountCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//If there is anything left that was missed or the list is empty
|
||||
if (accountCount > 0 || accountList.Count == 0)
|
||||
{
|
||||
if (accountList.Count == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write Empty List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
}
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
86
Lobby Server/Packets/Send/CharaCreatorPacket.cs
Normal file
86
Lobby Server/Packets/Send/CharaCreatorPacket.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class CharaCreatorPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x0E;
|
||||
|
||||
public const ushort RESERVE = 0x01;
|
||||
public const ushort MAKE = 0x02;
|
||||
public const ushort RENAME = 0x03;
|
||||
public const ushort DELETE = 0x04;
|
||||
public const ushort RENAME_RETAINER = 0x06;
|
||||
|
||||
private UInt64 sequence;
|
||||
|
||||
private ushort command;
|
||||
private uint pid;
|
||||
private uint cid;
|
||||
private uint type;
|
||||
private uint ticket;
|
||||
private string charaName;
|
||||
private string worldName;
|
||||
|
||||
public CharaCreatorPacket(UInt64 sequence, ushort command, uint pid, uint cid, uint ticket, string charaName, string worldName)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.command = command;
|
||||
this.pid = pid;
|
||||
this.cid = cid;
|
||||
this.type = 0x400017;
|
||||
this.ticket = ticket;
|
||||
this.charaName = charaName;
|
||||
this.worldName = worldName;
|
||||
}
|
||||
|
||||
public SubPacket BuildPacket()
|
||||
{
|
||||
MemoryStream memStream = new MemoryStream(0x1F0);
|
||||
BinaryWriter binWriter = new BinaryWriter(memStream);
|
||||
|
||||
binWriter.Write((UInt64)sequence);
|
||||
binWriter.Write((byte)1);
|
||||
binWriter.Write((byte)1);
|
||||
binWriter.Write((UInt16)command);
|
||||
binWriter.Write((UInt32)0);
|
||||
|
||||
binWriter.Write((UInt32)pid); //PID
|
||||
binWriter.Write((UInt32)cid); //CID
|
||||
binWriter.Write((UInt32)type); //Type?
|
||||
binWriter.Write((UInt32)ticket); //Ticket
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(charaName.PadRight(0x20, '\0'))); //Name
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(worldName.PadRight(0x20, '\0'))); //World Name
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
|
||||
return new SubPacket(OPCODE, 0xe0006868, data);
|
||||
}
|
||||
}
|
||||
}
|
179
Lobby Server/Packets/Send/CharacterListPacket.cs
Normal file
179
Lobby Server/Packets/Send/CharacterListPacket.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
using Meteor.Lobby.DataObjects;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class CharacterListPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x0D;
|
||||
public const ushort MAXPERPACKET = 2;
|
||||
|
||||
private ulong sequence;
|
||||
private List<Character> characterList;
|
||||
|
||||
public CharacterListPacket(ulong sequence, List<Character> characterList)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.characterList = characterList;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
int numCharacters = characterList.Count >= 8 ? 8 : characterList.Count + 1;
|
||||
|
||||
int characterCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
MemoryStream memStream = null;
|
||||
BinaryWriter binWriter = null;
|
||||
|
||||
foreach (Character chara in characterList)
|
||||
{
|
||||
Appearance appearance = Database.GetAppearance(chara.id);
|
||||
|
||||
if (totalCount == 0 || characterCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x3B0);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters - totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
|
||||
|
||||
//Write Entries
|
||||
World world = Database.GetServer(chara.serverId);
|
||||
string worldname = world == null ? "Unknown" : world.name;
|
||||
|
||||
binWriter.Write((uint)0); //???
|
||||
binWriter.Write((uint)chara.id); //Character Id
|
||||
binWriter.Write((byte)(totalCount)); //Slot
|
||||
|
||||
byte options = 0;
|
||||
if (chara.state == 1)
|
||||
options |= 0x01;
|
||||
if (chara.doRename)
|
||||
options |= 0x02;
|
||||
if (chara.isLegacy)
|
||||
options |= 0x08;
|
||||
|
||||
binWriter.Write((byte)options); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
|
||||
binWriter.Write((ushort)0);
|
||||
binWriter.Write((uint)chara.currentZoneId); //Logged out zone
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name
|
||||
|
||||
binWriter.Write(CharaInfo.BuildForCharaList(chara, appearance)); //Appearance Data
|
||||
//binWriter.Write(CharaInfo.Debug()); //Appearance Data
|
||||
|
||||
characterCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of character list
|
||||
if (characterCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
characterCount = 0;
|
||||
}
|
||||
|
||||
//Incase DB came back with more than max
|
||||
if (totalCount >= 8)
|
||||
break;
|
||||
}
|
||||
|
||||
//Add a 'NEW' slot if there is space
|
||||
if (characterList.Count < 8)
|
||||
{
|
||||
if (characterCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x3B0);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters-totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
|
||||
|
||||
//Write Entries
|
||||
binWriter.Write((uint)0); //???
|
||||
binWriter.Write((uint)0); //Character Id
|
||||
binWriter.Write((byte)(totalCount)); //Slot
|
||||
binWriter.Write((byte)0); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
|
||||
binWriter.Write((ushort)0);
|
||||
binWriter.Write((uint)0); //Logged out zone
|
||||
|
||||
characterCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of character list
|
||||
if (characterCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
characterCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//If there is anything left that was missed or the list is empty
|
||||
if (characterCount > 0 || numCharacters == 0)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
}
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
67
Lobby Server/Packets/Send/ErrorPacket.cs
Normal file
67
Lobby Server/Packets/Send/ErrorPacket.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class ErrorPacket
|
||||
{
|
||||
private const ushort OPCODE = 0x02;
|
||||
|
||||
private UInt64 sequence;
|
||||
private uint errorCode;
|
||||
private uint statusCode;
|
||||
private uint textId;
|
||||
private string message;
|
||||
|
||||
public ErrorPacket(UInt64 sequence, uint errorCode, uint statusCode, uint textId, string message)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.errorCode = errorCode;
|
||||
this.statusCode = statusCode;
|
||||
this.textId = textId;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public SubPacket BuildPacket()
|
||||
{
|
||||
MemoryStream memStream = new MemoryStream(0x210);
|
||||
BinaryWriter binWriter = new BinaryWriter(memStream);
|
||||
|
||||
binWriter.Write(sequence);
|
||||
binWriter.Write(errorCode);
|
||||
binWriter.Write(statusCode);
|
||||
binWriter.Write(textId);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(message));
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
return subpacket;
|
||||
}
|
||||
}
|
||||
}
|
124
Lobby Server/Packets/Send/ImportListPacket.cs
Normal file
124
Lobby Server/Packets/Send/ImportListPacket.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class ImportListPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x16;
|
||||
public const ushort MAXPERPACKET = 12;
|
||||
|
||||
private UInt64 sequence;
|
||||
private List<String> namesList;
|
||||
|
||||
public ImportListPacket(UInt64 sequence, List<String> names)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.namesList = names;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
int namesCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
MemoryStream memStream = null;
|
||||
BinaryWriter binWriter = null;
|
||||
|
||||
foreach (String name in namesList)
|
||||
{
|
||||
if (totalCount == 0 || namesCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (UInt32)(namesList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
//Write Entries
|
||||
binWriter.Write((uint)0);
|
||||
binWriter.Write((uint)totalCount);
|
||||
|
||||
if (!name.Contains(" "))
|
||||
binWriter.Write(Encoding.ASCII.GetBytes((name+" Last").PadRight(0x20, '\0')));
|
||||
else
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(name.PadRight(0x20, '\0')));
|
||||
|
||||
namesCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of world list
|
||||
if (namesCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
namesCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//If there is anything left that was missed or the list is empty
|
||||
if (namesCount > 0 || namesList.Count == 0)
|
||||
{
|
||||
if (namesList.Count == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write Empty List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
}
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
126
Lobby Server/Packets/Send/RetainerListPacket.cs
Normal file
126
Lobby Server/Packets/Send/RetainerListPacket.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
using Meteor.Lobby.DataObjects;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class RetainerListPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x17;
|
||||
public const ushort MAXPERPACKET = 9;
|
||||
|
||||
private UInt64 sequence;
|
||||
private List<Retainer> retainerList;
|
||||
|
||||
public RetainerListPacket(UInt64 sequence, List<Retainer> retainerList)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.retainerList = retainerList;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
int retainerCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
MemoryStream memStream = null;
|
||||
BinaryWriter binWriter = null;
|
||||
|
||||
foreach (Retainer retainer in retainerList)
|
||||
{
|
||||
if (totalCount == 0 || retainerCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (UInt32)(retainerList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
binWriter.Write((UInt64)0);
|
||||
binWriter.Write((UInt32)0);
|
||||
}
|
||||
|
||||
//Write Entries
|
||||
binWriter.Write((uint)retainer.id);
|
||||
binWriter.Write((uint)retainer.characterId);
|
||||
binWriter.Write((ushort)totalCount);
|
||||
binWriter.Write((ushort)(retainer.doRename ? 0x04 : 0x00));
|
||||
binWriter.Write((uint)0);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(retainer.name.PadRight(0x20, '\0')));
|
||||
|
||||
retainerCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of character list
|
||||
if (retainerCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
retainerCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//If there is anything left that was missed or the list is empty
|
||||
if (retainerCount > 0 || retainerList.Count == 0)
|
||||
{
|
||||
if (retainerList.Count == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write Empty List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
}
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
80
Lobby Server/Packets/Send/SelectCharacterConfirmPacket.cs
Normal file
80
Lobby Server/Packets/Send/SelectCharacterConfirmPacket.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class SelectCharacterConfirmPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x0F;
|
||||
|
||||
private UInt64 sequence;
|
||||
private UInt32 characterId;
|
||||
private string sessionToken;
|
||||
private string worldIp;
|
||||
private UInt16 worldPort;
|
||||
private UInt64 selectCharTicket;
|
||||
|
||||
public SelectCharacterConfirmPacket(UInt64 sequence, UInt32 characterId, string sessionToken, string worldIp, ushort worldPort, UInt64 selectCharTicket)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.characterId = characterId;
|
||||
this.sessionToken = sessionToken;
|
||||
this.worldIp = worldIp;
|
||||
this.worldPort = worldPort;
|
||||
this.selectCharTicket = selectCharTicket;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
byte[] data;
|
||||
|
||||
using (MemoryStream memStream = new MemoryStream(0x98))
|
||||
{
|
||||
using (BinaryWriter binWriter = new BinaryWriter(memStream))
|
||||
{
|
||||
binWriter.Write((UInt64)sequence);
|
||||
binWriter.Write((UInt32)characterId); //ActorId
|
||||
binWriter.Write((UInt32)characterId); //CharacterId
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(sessionToken.PadRight(0x42, '\0'))); //Session Token
|
||||
binWriter.Write((UInt16)worldPort); //World Port
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(worldIp.PadRight(0x38, '\0'))); //World Hostname/IP
|
||||
binWriter.Write((UInt64)selectCharTicket); //Ticket or Handshake of somekind
|
||||
}
|
||||
data = memStream.GetBuffer();
|
||||
}
|
||||
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
123
Lobby Server/Packets/Send/WorldListPacket.cs
Normal file
123
Lobby Server/Packets/Send/WorldListPacket.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Meteor.Common;
|
||||
using Meteor.Lobby.DataObjects;
|
||||
|
||||
namespace Meteor.Lobby.Packets
|
||||
{
|
||||
class WorldListPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x15;
|
||||
public const ushort MAXPERPACKET = 6;
|
||||
|
||||
private UInt64 sequence;
|
||||
private List<World> worldList;
|
||||
|
||||
public WorldListPacket(UInt64 sequence, List<World> serverList)
|
||||
{
|
||||
this.sequence = sequence;
|
||||
this.worldList = serverList;
|
||||
}
|
||||
|
||||
public List<SubPacket> BuildPackets()
|
||||
{
|
||||
List<SubPacket> subPackets = new List<SubPacket>();
|
||||
|
||||
int serverCount = 0;
|
||||
int totalCount = 0;
|
||||
|
||||
MemoryStream memStream = null;
|
||||
BinaryWriter binWriter = null;
|
||||
|
||||
foreach (World world in worldList)
|
||||
{
|
||||
if (totalCount == 0 || serverCount % MAXPERPACKET == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (UInt32)(worldList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
//Write Entries
|
||||
binWriter.Write((ushort)world.id);
|
||||
binWriter.Write((ushort)world.listPosition);
|
||||
binWriter.Write((uint)world.population);
|
||||
binWriter.Write((UInt64)0);
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(world.name.PadRight(64, '\0')));
|
||||
|
||||
serverCount++;
|
||||
totalCount++;
|
||||
|
||||
//Send this chunk of world list
|
||||
if (serverCount >= MAXPERPACKET)
|
||||
{
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
serverCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//If there is anything left that was missed or the list is empty
|
||||
if (serverCount > 0 || worldList.Count == 0)
|
||||
{
|
||||
if (worldList.Count == 0)
|
||||
{
|
||||
memStream = new MemoryStream(0x210);
|
||||
binWriter = new BinaryWriter(memStream);
|
||||
|
||||
//Write Empty List Info
|
||||
binWriter.Write((UInt64)sequence);
|
||||
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||
binWriter.Write((UInt32)0);
|
||||
binWriter.Write((byte)0);
|
||||
binWriter.Write((UInt16)0);
|
||||
}
|
||||
|
||||
byte[] data = memStream.GetBuffer();
|
||||
binWriter.Dispose();
|
||||
memStream.Dispose();
|
||||
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
|
||||
subpacket.SetTargetId(0xe0006868);
|
||||
subPackets.Add(subpacket);
|
||||
}
|
||||
|
||||
return subPackets;
|
||||
}
|
||||
}
|
||||
}
|
87
Lobby Server/Program.cs
Normal file
87
Lobby Server/Program.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
using MySql.Data.MySqlClient;
|
||||
using NLog;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static Logger Log;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
// set up logging
|
||||
Log = LogManager.GetCurrentClassLogger();
|
||||
#if DEBUG
|
||||
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
|
||||
Debug.Listeners.Add(myWriter);
|
||||
#endif
|
||||
Log.Info("==================================");
|
||||
Log.Info("FFXIV Classic Lobby Server");
|
||||
Log.Info("Version: 0.1");
|
||||
Log.Info("==================================");
|
||||
|
||||
bool startServer = true;
|
||||
|
||||
//Load Config
|
||||
ConfigConstants.Load();
|
||||
ConfigConstants.ApplyLaunchArgs(args);
|
||||
|
||||
//Test DB Connection
|
||||
Program.Log.Info("Testing DB connection to \"{0}\"... ", ConfigConstants.DATABASE_HOST);
|
||||
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
|
||||
{
|
||||
try
|
||||
{
|
||||
conn.Open();
|
||||
conn.Close();
|
||||
|
||||
Program.Log.Info("Connection ok.");
|
||||
}
|
||||
catch (MySqlException e)
|
||||
{
|
||||
Program.Log.Error(e.ToString());
|
||||
startServer = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Start Server if A-OK
|
||||
if (startServer)
|
||||
{
|
||||
Server server = new Server();
|
||||
server.StartServer();
|
||||
while (true) Thread.Sleep(10000);
|
||||
}
|
||||
|
||||
Program.Log.Info("Press any key to continue...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
35
Lobby Server/Properties/AssemblyInfo.cs
Normal file
35
Lobby Server/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Reflection;
|
||||
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("FFXIVClassic_Lobby_Server")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FFXIVClassic_Lobby_Server")]
|
||||
[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("9b87f719-1e4d-4781-89c9-28d99f579253")]
|
||||
|
||||
// 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.*")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
264
Lobby Server/Server.cs
Normal file
264
Lobby Server/Server.cs
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015-2019 Project Meteor Dev Team
|
||||
|
||||
This file is part of Project Meteor Server.
|
||||
|
||||
Project Meteor Server is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Project Meteor Server is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Lobby
|
||||
{
|
||||
class Server
|
||||
{
|
||||
public const int FFXIV_LOBBY_PORT = 54994;
|
||||
public const int BUFFER_SIZE = 0xFFFF;
|
||||
public const int BACKLOG = 100;
|
||||
|
||||
private const int CLEANUP_THREAD_SLEEP_TIME = 60;
|
||||
|
||||
private Socket mServerSocket;
|
||||
private List<ClientConnection> mConnectionList = new List<ClientConnection>();
|
||||
private PacketProcessor mProcessor;
|
||||
|
||||
private Thread cleanupThread;
|
||||
private bool killCleanupThread = false;
|
||||
|
||||
private void SocketCleanup()
|
||||
{
|
||||
Program.Log.Info("Cleanup thread started; it will run every {0} seconds.", CLEANUP_THREAD_SLEEP_TIME);
|
||||
while (!killCleanupThread)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = mConnectionList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
ClientConnection conn = mConnectionList[i];
|
||||
if (conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)
|
||||
{
|
||||
conn.socket.Disconnect(false);
|
||||
mConnectionList.Remove(conn);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count != 0)
|
||||
Program.Log.Info("{0} connections were cleaned up.", count);
|
||||
Thread.Sleep(CLEANUP_THREAD_SLEEP_TIME*1000);
|
||||
}
|
||||
}
|
||||
|
||||
#region Socket Handling
|
||||
public bool StartServer()
|
||||
{
|
||||
//cleanupThread = new Thread(new ThreadStart(socketCleanup));
|
||||
//cleanupThread.Name = "LobbyThread:Cleanup";
|
||||
//cleanupThread.Start();
|
||||
|
||||
IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), int.Parse(ConfigConstants.OPTIONS_PORT));
|
||||
|
||||
try{
|
||||
mServerSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ApplicationException("Could not Create socket, check to make sure not duplicating port", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
mServerSocket.Bind(serverEndPoint);
|
||||
mServerSocket.Listen(BACKLOG);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ApplicationException("Error occured while binding socket, check inner exception", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
|
||||
}
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Program.Log.Info("Lobby Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
|
||||
mProcessor = new PacketProcessor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AcceptCallback(IAsyncResult result)
|
||||
{
|
||||
ClientConnection conn = null;
|
||||
try
|
||||
{
|
||||
System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
|
||||
conn = new ClientConnection();
|
||||
conn.socket = s.EndAccept(result);
|
||||
conn.buffer = new byte[BUFFER_SIZE];
|
||||
lock (mConnectionList)
|
||||
{
|
||||
mConnectionList.Add(conn);
|
||||
}
|
||||
//Queue recieving of data from the connection
|
||||
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
|
||||
//Queue the accept of the next incomming connection
|
||||
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
|
||||
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
if (conn.socket != null)
|
||||
{
|
||||
conn.socket.Close();
|
||||
lock (mConnectionList)
|
||||
{
|
||||
mConnectionList.Remove(conn);
|
||||
}
|
||||
}
|
||||
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (conn.socket != null)
|
||||
{
|
||||
conn.socket.Close();
|
||||
lock (mConnectionList)
|
||||
{
|
||||
mConnectionList.Remove(conn);
|
||||
}
|
||||
}
|
||||
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveCallback(IAsyncResult result)
|
||||
{
|
||||
ClientConnection conn = (ClientConnection)result.AsyncState;
|
||||
|
||||
try
|
||||
{
|
||||
int bytesRead = conn.socket.EndReceive(result);
|
||||
|
||||
bytesRead += conn.lastPartialSize;
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
//Build packets until can no longer or out of data
|
||||
while (true)
|
||||
{
|
||||
BasePacket basePacket = BuildPacket(ref offset, conn.buffer, bytesRead);
|
||||
|
||||
//If can't build packet, break, else process another
|
||||
if (basePacket == null)
|
||||
break;
|
||||
else
|
||||
mProcessor.ProcessPacket(conn, basePacket);
|
||||
}
|
||||
|
||||
//Not all bytes consumed, transfer leftover to beginning
|
||||
if (offset < bytesRead)
|
||||
Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset);
|
||||
|
||||
Array.Clear(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset));
|
||||
|
||||
conn.lastPartialSize = bytesRead - offset;
|
||||
|
||||
//Build any queued subpackets into basepackets and send
|
||||
conn.FlushQueuedSendPackets();
|
||||
|
||||
if (offset < bytesRead)
|
||||
//Need offset since not all bytes consumed
|
||||
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
|
||||
else
|
||||
//All bytes consumed, full buffer available
|
||||
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Log.Info("{0} has disconnected.", conn.currentUserId == 0 ? conn.GetAddress() : "User " + conn.currentUserId);
|
||||
|
||||
lock (mConnectionList)
|
||||
{
|
||||
conn.Disconnect();
|
||||
mConnectionList.Remove(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
if (conn.socket != null)
|
||||
{
|
||||
Program.Log.Info("{0} has disconnected.", conn.currentUserId == 0 ? conn.GetAddress() : "User " + conn.currentUserId);
|
||||
|
||||
lock (mConnectionList)
|
||||
{
|
||||
mConnectionList.Remove(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
|
||||
/// </summary>
|
||||
/// <param name="offset">Current offset in buffer.</param>
|
||||
/// <param name="buffer">Incoming buffer.</param>
|
||||
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
|
||||
public BasePacket BuildPacket(ref int offset, byte[] buffer, int bytesRead)
|
||||
{
|
||||
BasePacket newPacket = null;
|
||||
|
||||
//Too small to even get length
|
||||
if (bytesRead <= offset)
|
||||
return null;
|
||||
|
||||
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
|
||||
|
||||
//Too small to whole packet
|
||||
if (bytesRead < offset + packetSize)
|
||||
return null;
|
||||
|
||||
if (buffer.Length < offset + packetSize)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
newPacket = new BasePacket(buffer, ref offset);
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return newPacket;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
9
Lobby Server/app.config
Normal file
9
Lobby Server/app.config
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.data>
|
||||
<DbProviderFactories>
|
||||
<remove invariant="MySql.Data.MySqlClient"/>
|
||||
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"/>
|
||||
</DbProviderFactories>
|
||||
</system.data>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
|
9
Lobby Server/packages.config
Normal file
9
Lobby Server/packages.config
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Compilers" version="2.0.0-beta3" targetFramework="net45" developmentDependency="true" />
|
||||
<package id="MySql.Data" version="6.9.8" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.4" targetFramework="net45" />
|
||||
<package id="NLog.Config" version="4.3.4" targetFramework="net45" />
|
||||
<package id="NLog.Schema" version="4.3.4" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Add table
Add a link
Reference in a new issue