Added implementations of the event receive/send packets. Added lua scripting stuff. Added some utils.

This commit is contained in:
Filip Maj 2016-01-01 14:03:55 -05:00
parent d60938346b
commit 42b77010e5
15 changed files with 630 additions and 254 deletions

View File

@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server
{
class ScriptParamReader
{
List<int> types = new List<int>();
List<Object> values = new List<Object>();
public ScriptParamReader(BinaryReader reader)
{
while (true)
{
byte code = reader.ReadByte();
switch (code)
{
case 0x0: //INT32
types.Add(6);
values.Add(reader.ReadUInt32());
break;
case 0x1: //????
continue;
case 0x2: //Null Termed String
types.Add(2);
List<byte> list = new List<byte>();
while(true){
byte readByte = reader.ReadByte();
if (readByte == 0)
break;
list.Add(readByte);
}
values.Add(Encoding.ASCII.GetString(list.ToArray()));
break;
case 0x4: //BYTE
types.Add(4);
values.Add(reader.ReadByte());
break;
case 0x5: //NULL???
types.Add(5);
values.Add(new Object());
continue;
case 0x6: //INT32
types.Add(6);
values.Add(reader.ReadUInt32());
break;
case 0xF: //End
return;
}
}
}
public int getType(int index)
{
return types[index];
}
public Object getValue(int index)
{
return values[index];
}
public int getCount()
{
return values.Count;
}
}
}

View File

@ -67,6 +67,14 @@ namespace FFXIVClassic_Lobby_Server.common
return unixTimeStamp; return unixTimeStamp;
} }
public static uint swapEndian(uint input)
{
return ((input >> 24) & 0xff) |
((input << 8) & 0xff0000) |
((input >> 8) & 0xff00) |
((input << 24) & 0xff000000);
}
public static uint MurmurHash2(string key, uint seed) public static uint MurmurHash2(string key, uint seed)
{ {
// 'm' and 'r' are mixing constants generated offline. // 'm' and 'r' are mixing constants generated offline.

View File

@ -0,0 +1,77 @@
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.dataobjects.actors;
using FFXIVClassic_Map_Server.dataobjects.chara;
using FFXIVClassic_Map_Server.dataobjects.chara.npc;
using FFXIVClassic_Map_Server.packets.receive.events;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.packets.send.events;
using NLua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.lua
{
class LuaEngine
{
const string FILEPATH_COMMANDS = "./scripts/command/{0}.lua";
const string FILEPATH_EVENTS = "./scripts/talk/{0}.lua";
Lua lstate = new Lua();
public LuaEngine()
{
}
public void doEventStart(ConnectedPlayer player, Actor target, EventStartPacket packet)
{
string luaPath;
if (target is Command)
luaPath = String.Format(FILEPATH_COMMANDS, target.getName());
else if (target is Npc)
luaPath = String.Format(FILEPATH_EVENTS, target.getName());
else
luaPath = "";
if (File.Exists(luaPath))
{
lstate.DoFile(luaPath);
var eventStarted = lstate["eventStarted"] as LuaFunction;
eventStarted.Call(new LuaPlayer(player), player.eventCurrentOwner, LuaUtils.createLuaParamObjectList(packet.luaParams));
}
else
{
List<SubPacket> sendError = new List<SubPacket>();
sendError.Add(EndEventPacket.buildPacket(player.actorID, player.eventCurrentOwner, player.eventCurrentStarter));
sendError.Add(SendMessagePacket.buildPacket(player.actorID, player.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", "ERROR: Could not find script for event"));
player.queuePacket(BasePacket.createPacket(sendError, true, false));
}
}
public void doEventUpdated(ConnectedPlayer player, Actor target, EventUpdatePacket packet)
{
string luaPath = String.Format(FILEPATH_EVENTS, ((Command)target).getName());
if (File.Exists(luaPath))
{
lstate.DoFile(luaPath);
var eventStarted = lstate["eventUpdated"] as LuaFunction;
eventStarted.Call(new LuaPlayer(player), player.eventCurrentOwner, packet.step, LuaUtils.createLuaParamObjectList(packet.luaParams));
}
else
{
List<SubPacket> sendError = new List<SubPacket>();
sendError.Add(EndEventPacket.buildPacket(player.actorID, player.eventCurrentOwner, player.eventCurrentStarter));
sendError.Add(SendMessagePacket.buildPacket(player.actorID, player.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", "ERROR: Could not find script for event"));
player.queuePacket(BasePacket.createPacket(sendError, true, false));
}
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.lua
{
class LuaEvent
{
public static void getStep()
{
}
public static void getParam()
{
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.lua
{
class LuaParam
{
public int typeID;
public Object value;
public LuaParam(int type, Object value)
{
this.typeID = type;
this.value = value;
}
}
}

View File

@ -0,0 +1,74 @@
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.packets.send.events;
using NLua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.lua
{
class LuaPlayer
{
private ConnectedPlayer player;
public LuaPlayer(ConnectedPlayer player)
{
this.player = player;
}
public void setMusic(ushort musicID, ushort playMode)
{
player.queuePacket(SetMusicPacket.buildPacket(player.actorID, musicID, playMode), true, false);
}
public void setWeather(uint weatherID)
{
player.queuePacket(SetWeatherPacket.buildPacket(player.actorID, weatherID), true, false);
}
public void getParameter(string paramName)
{
}
public void setParameter(string paramName, object value, string uiToRefresh)
{
}
public void getAttributePoints()
{
}
public void setAttributePoints(int str, int vit, int dex, int inte, int min, int pie)
{
}
public void logout()
{
player.queuePacket(LogoutPacket.buildPacket(player.actorID), true, false);
}
public void quitGame()
{
player.queuePacket(QuitPacket.buildPacket(player.actorID), true, false);
}
public void runEvent(string functionName, params object[] parameters)
{
List<LuaParam> lParams = LuaUtils.createLuaParamList(parameters);
player.getConnection1().queuePacket(RunEventFunctionPacket.buildPacket(player.actorID, player.eventCurrentOwner, player.eventCurrentStarter, functionName, lParams), true, false);
}
public void endEvent()
{
player.getConnection1().queuePacket(EndEventPacket.buildPacket(player.actorID, player.eventCurrentOwner, player.eventCurrentStarter), true, false);
}
}
}

View File

@ -0,0 +1,241 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.lua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server
{
class LuaUtils
{
public static List<LuaParam> readLuaParams(BinaryReader reader)
{
List<LuaParam> luaParams = new List<LuaParam>();
bool isDone = false;
while (true)
{
byte code = reader.ReadByte();
object value = null;
bool wasNil = false;
switch (code)
{
case 0x0: //Int32
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x1: //Int32
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x2: //Null Termed String
List<byte> list = new List<byte>();
while(true){
byte readByte = reader.ReadByte();
if (readByte == 0)
break;
list.Add(readByte);
}
value = Encoding.ASCII.GetString(list.ToArray());
break;
case 0x3: //Boolean False
value = false;
break;
case 0x4: //Boolean True
value = true;
break;
case 0x5: //Nil
wasNil = true;
break;
case 0x6: //Actor (By Id)
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x10: //Byte?
value = reader.ReadByte();
break;
case 0x1B: //Short?
value = reader.ReadUInt16();
break;
case 0xF: //End
isDone = true;
continue;
}
if (isDone)
break;
if (value != null)
luaParams.Add(new LuaParam(code, value));
else if (wasNil)
luaParams.Add(new LuaParam(code, value));
}
return luaParams;
}
public static List<LuaParam> readLuaParams(byte[] bytesIn)
{
List<LuaParam> luaParams = new List<LuaParam>();
using (MemoryStream memStream = new MemoryStream(bytesIn))
{
using (BinaryReader reader = new BinaryReader(memStream))
{
bool isDone = false;
while (true)
{
byte code = reader.ReadByte();
object value = null;
bool wasNil = false;
switch (code)
{
case 0x0: //Int32
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x1: //Int32
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x2: //Null Termed String
List<byte> list = new List<byte>();
while (true)
{
byte readByte = reader.ReadByte();
if (readByte == 0)
break;
list.Add(readByte);
}
value = Encoding.ASCII.GetString(list.ToArray());
break;
case 0x3: //Boolean False
value = false;
break;
case 0x4: //Boolean True
value = true;
break;
case 0x5: //Nil
wasNil = true;
break;
case 0x6: //Actor (By Id)
value = Utils.swapEndian(reader.ReadUInt32());
break;
case 0x10: //Byte?
value = reader.ReadByte();
break;
case 0x1B: //Short?
value = reader.ReadUInt16();
break;
case 0xF: //End
isDone = true;
continue;
}
if (isDone)
break;
if (value != null)
luaParams.Add(new LuaParam(code, value));
else if (wasNil)
luaParams.Add(new LuaParam(code, value));
}
}
}
return luaParams;
}
public static List<LuaParam> createLuaParamList(object[] list)
{
List<LuaParam> luaParams = new List<LuaParam>();
foreach (object o in list)
{
if (o is uint)
{
luaParams.Add(new LuaParam(0x0, (uint)o));
}
else if (o is string)
{
luaParams.Add(new LuaParam(0x2, (string)o));
}
else if (o is bool)
{
if (((bool)o))
luaParams.Add(new LuaParam(0x4, null));
else
luaParams.Add(new LuaParam(0x3, null));
}
else if (o == null)
{
luaParams.Add(new LuaParam(0x5, null));
}
else if (o is Actor)
{
luaParams.Add(new LuaParam(0x6, ((Actor)o).actorID));
}
}
return luaParams;
}
public static object[] createLuaParamObjectList(List <LuaParam> luaParams)
{
object[] list = new object[luaParams.Count];
for (int i = 0; i < list.Length; i++)
list[i] = luaParams[i].value;
return list;
}
public static string dumpParams(List<LuaParam> lParams)
{
string dumpString = "";
for (int i = 0; i < lParams.Count; i++)
{
switch (lParams[i].typeID)
{
case 0x0: //Int32
dumpString += String.Format("0x{0:X}", (uint)lParams[i].value);
break;
case 0x1: //Int32
dumpString += String.Format("0x{0:X}", (uint)lParams[i].value);
break;
case 0x2: //Null Termed String
dumpString += String.Format("\"{0}\"", (string)lParams[i].value);
break;
case 0x3: //Boolean False
dumpString += "false";
break;
case 0x4: //Boolean True
dumpString += "true";
break;
case 0x5: //NULL???
dumpString += "nil";
break;
case 0x6: //Actor (By Id)
dumpString += String.Format("0x{0:X}", (uint)lParams[i].value);
break;
case 0x10: //Byte?
dumpString += String.Format("0x{0:X}", (byte)lParams[i].value);
break;
case 0x1B: //Short?
dumpString += String.Format("0x{0:X}", (ushort)lParams[i].value);
break;
case 0xF: //End
break;
}
if (i != lParams.Count - 1)
dumpString += ", ";
}
return dumpString;
}
}
}

View File

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.receive
{
class StartScriptPacket
{
public uint sourceActor;
public uint targetActor;
public string scriptName;
public bool invalidPacket = false;
public StartScriptPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try{
sourceActor = binReader.ReadUInt32();
targetActor = binReader.ReadUInt32();
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@ -0,0 +1,66 @@
using FFXIVClassic_Map_Server.lua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.receive.events
{
class EventStartPacket
{
public const ushort OPCODE = 0x012D;
public const uint PACKET_SIZE = 0xD8;
public bool invalidPacket = false;
public uint actorID;
public uint scriptOwnerActorID;
public uint val1;
public uint val2;
public byte val3;
public string eventStarter;
public List<LuaParam> luaParams;
public EventStartPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try{
actorID = binReader.ReadUInt32();
scriptOwnerActorID = binReader.ReadUInt32();
val1 = binReader.ReadUInt32();
val2 = binReader.ReadUInt32();
val3 = binReader.ReadByte();
List<byte> strList = new List<byte>();
byte curByte;
while ((curByte = binReader.ReadByte())!=0)
{
strList.Add(curByte);
}
eventStarter = Encoding.ASCII.GetString(strList.ToArray());
binReader.ReadUInt32();
binReader.ReadUInt32();
binReader.ReadUInt32();
binReader.ReadUInt32();
binReader.ReadByte();
luaParams = LuaUtils.readLuaParams(binReader);
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@ -1,13 +1,14 @@
using System; using FFXIVClassic_Map_Server.lua;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.script namespace FFXIVClassic_Map_Server.packets.receive.events
{ {
class CommandStartRequestPacket class EventUpdatePacket
{ {
public const ushort OPCODE = 0x012E; public const ushort OPCODE = 0x012E;
public const uint PACKET_SIZE = 0x78; public const uint PACKET_SIZE = 0x78;
@ -18,9 +19,10 @@ namespace FFXIVClassic_Map_Server.packets.send.script
public uint scriptOwnerActorID; public uint scriptOwnerActorID;
public uint val1; public uint val1;
public uint val2; public uint val2;
public ScriptParamReader reader; public byte step;
public List<LuaParam> luaParams;
public CommandStartRequestPacket(byte[] data) public EventUpdatePacket(byte[] data)
{ {
using (MemoryStream mem = new MemoryStream(data)) using (MemoryStream mem = new MemoryStream(data))
{ {
@ -31,10 +33,8 @@ namespace FFXIVClassic_Map_Server.packets.send.script
scriptOwnerActorID = binReader.ReadUInt32(); scriptOwnerActorID = binReader.ReadUInt32();
val1 = binReader.ReadUInt32(); val1 = binReader.ReadUInt32();
val2 = binReader.ReadUInt32(); val2 = binReader.ReadUInt32();
binReader.ReadByte(); step = binReader.ReadByte();
luaParams = LuaUtils.readLuaParams(binReader);
binReader.BaseStream.Seek(0x31, SeekOrigin.Begin);
reader = new ScriptParamReader(binReader);
} }
catch (Exception){ catch (Exception){
invalidPacket = true; invalidPacket = true;

View File

@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.script
{
class ScriptResultPacket
{
public const ushort OPCODE = 0x012E;
public const uint PACKET_SIZE = 0xD8;
public bool invalidPacket = false;
public uint actorID;
public uint scriptOwnerActorID;
public uint val1;
public uint val2;
ScriptParamReader reader;
public ScriptResultPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try{
actorID = binReader.ReadUInt32();
scriptOwnerActorID = binReader.ReadUInt32();
val1 = binReader.ReadUInt32();
val2 = binReader.ReadUInt32();
binReader.ReadByte();
reader = new ScriptParamReader(binReader);
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@ -0,0 +1,35 @@
using FFXIVClassic_Lobby_Server.packets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.events
{
class EndEventPacket
{
public const ushort OPCODE = 0x0131;
public const uint PACKET_SIZE = 0x50;
public static SubPacket buildPacket(uint playerActorID, uint eventOwnerActorID, string eventStarter)
{
byte[] data = new byte[PACKET_SIZE - 0x20];
int maxBodySize = data.Length - 0x80;
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryWriter binWriter = new BinaryWriter(mem))
{
binWriter.Write((UInt32)playerActorID);
binWriter.Write((UInt32)eventOwnerActorID);
binWriter.Write((Byte)0);
binWriter.Write(Encoding.ASCII.GetBytes(eventStarter), 0, Encoding.ASCII.GetByteCount(eventStarter) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(eventStarter));
}
}
return new SubPacket(OPCODE, playerActorID, playerActorID, data);
}
}
}

View File

@ -0,0 +1,78 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.lua;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.events
{
class RunEventFunctionPacket
{
public const ushort OPCODE = 0x0130;
public const uint PACKET_SIZE = 0x2B8;
public static SubPacket buildPacket(uint playerActorID, uint eventOwnerActorID, string eventStarter, string callFunction, List<LuaParam> luaParams)
{
byte[] data = new byte[PACKET_SIZE - 0x20];
int maxBodySize = data.Length - 0x80;
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryWriter binWriter = new BinaryWriter(mem))
{
binWriter.Write((UInt32)playerActorID);
binWriter.Write((UInt32)eventOwnerActorID);
binWriter.Write((Byte)0);
binWriter.Write(Encoding.ASCII.GetBytes(eventStarter), 0, Encoding.ASCII.GetByteCount(eventStarter) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(eventStarter));
binWriter.Seek(0x29, SeekOrigin.Begin);
binWriter.Write(Encoding.ASCII.GetBytes(callFunction), 0, Encoding.ASCII.GetByteCount(callFunction) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(callFunction));
binWriter.Seek(0x49, SeekOrigin.Begin);
//Write out params
foreach (LuaParam p in luaParams)
{
binWriter.Write((Byte)p.typeID);
switch (p.typeID)
{
case 0x0: //Int32
binWriter.Write(Utils.swapEndian((UInt32)p.value));
break;
case 0x1: //Int32
binWriter.Write(Utils.swapEndian((UInt32)p.value));
break;
case 0x2: //Null Termed String
string svalue = (string)p.value;
binWriter.Write(Encoding.ASCII.GetBytes(svalue), 0, Encoding.ASCII.GetByteCount(svalue));
if (svalue[svalue.Length - 1] != '\0')
binWriter.Write((Byte)0);
break;
case 0x3: //Boolean False
break;
case 0x4: //Boolean True
break;
case 0x5: //Nil
break;
case 0x6: //Actor (By Id)
binWriter.Write(Utils.swapEndian((UInt32)p.value));
break;
case 0x10: //Byte?
//value = reader.ReadByte();
break;
case 0x1B: //Short?
//value = reader.ReadUInt16();
break;
}
}
binWriter.Write((Byte)0xF);
}
}
return new SubPacket(OPCODE, playerActorID, playerActorID, data);
}
}
}

View File

@ -1,41 +0,0 @@
using FFXIVClassic_Lobby_Server.packets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.login
{
class InitPacket
{
public static BasePacket buildPacket(uint actorID, uint time)
{
byte[] data = new byte[0x18];
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryWriter binWriter = new BinaryWriter(mem))
{
try
{
binWriter.Write((short)0x18);
binWriter.Write((short)0x7);
binWriter.Write((uint)0);
binWriter.Write((uint)0);
binWriter.Write((uint)0xFFFFFD7F);
binWriter.Write((uint)actorID);
binWriter.Write((uint)time);
}
catch (Exception)
{
}
}
}
return BasePacket.createPacket(data, false, false);
}
}
}

View File

@ -1,52 +0,0 @@
using FFXIVClassic_Lobby_Server.packets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.packets.send.script
{
class ScriptStartPacket
{
public const ushort OPCODE = 0x0130;
public const uint PACKET_SIZE = 0xB0;
//Known types
public const byte TYPE_END = 0xF;
public const byte TYPE_UINT = 0x6;
public const byte TYPE_BYTE = 0x5;
public const byte TYPE_STRING = 0x2;
public const byte TYPE_STRING_20B = 0x1;
public const byte TYPE_STRING_NULLTERM = 0x0;
public static SubPacket buildPacket(uint playerActorID, uint scriptOwnerActorID, string startName, string scriptName)
{
byte[] data = new byte[PACKET_SIZE - 0x20];
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryWriter binWriter = new BinaryWriter(mem))
{
binWriter.Write((uint)playerActorID);
binWriter.Write((uint)scriptOwnerActorID);
binWriter.Write((byte)1);
binWriter.Write(Encoding.Unicode.GetBytes(startName));
binWriter.Seek(0x29, SeekOrigin.Begin);
binWriter.Write(Encoding.Unicode.GetBytes(scriptName));
binWriter.Seek(0x29 + 0x20, SeekOrigin.Begin);
binWriter.Write((byte)6);
byte[] actorID = BitConverter.GetBytes(playerActorID);
Array.Reverse(actorID);
binWriter.Write(actorID);
binWriter.Write((byte)0x0F);
}
}
return new SubPacket(OPCODE, playerActorID, playerActorID, data);
}
}
}