1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-22 20:57:47 +00:00

Merged in xdemolish/ffxiv-classic-server/lua_commands (pull request #16)

dynamic gm commands + packet logging fix
This commit is contained in:
Filip Maj 2016-07-16 19:52:54 -04:00
commit 302d7bd68a
51 changed files with 1853 additions and 1464 deletions

View file

@ -24,6 +24,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<RegisterForComInterop>false</RegisterForComInterop> <RegisterForComInterop>false</RegisterForComInterop>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@ -35,6 +36,14 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<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.5\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@ -49,10 +58,12 @@
<Compile Include="Blowfish.cs" /> <Compile Include="Blowfish.cs" />
<Compile Include="EfficientHashTables.cs" /> <Compile Include="EfficientHashTables.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sql.cs" />
<Compile Include="STA_INIFile.cs" /> <Compile Include="STA_INIFile.cs" />
<Compile Include="Utils.cs" /> <Compile Include="Utils.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using NLog;
using System.Data;
using System.Data.Common;
namespace FFXIVClassic.Common
{
// todo:
// havent decided whether it's worth wrapping every sql class
// so i'll just leave it with logger for now
public class Sql
{
public static Logger Log = LogManager.GetCurrentClassLogger();
}
}

View file

@ -10,10 +10,10 @@ namespace FFXIVClassic.Common
private static uint[] CreateLookup32() private static uint[] CreateLookup32()
{ {
var result = new uint[256]; var result = new uint[256];
for (int i = 0; i < 256; i++) for (var i = 0; i < 256; i++)
{ {
string s = i.ToString("X2"); var s = i.ToString("X2");
result[i] = ((uint)s[0]) + ((uint)s[1] << 16); result[i] = s[0] + ((uint) s[1] << 16);
} }
return result; return result;
} }
@ -22,25 +22,23 @@ namespace FFXIVClassic.Common
{ {
if (bytes == null) if (bytes == null)
{ {
return String.Empty; return string.Empty;
} }
char[] hexChars = "0123456789ABCDEF".ToCharArray(); var hexChars = "0123456789ABCDEF".ToCharArray();
// 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ var offsetBlock = 8 + 3;
// 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ var byteBlock = offsetBlock + bytesPerLine*3 + (bytesPerLine - 1)/8 + 2;
int offsetBlock = 8 + 3; var lineLength = byteBlock + bytesPerLine + Environment.NewLine.Length;
int byteBlock = offsetBlock + (bytesPerLine * 3) + ((bytesPerLine - 1) / 8) + 2;
int lineLength = byteBlock + bytesPerLine + Environment.NewLine.Length;
char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray(); var line = (new string(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
int numLines = (bytes.Length + bytesPerLine - 1) / bytesPerLine; var numLines = (bytes.Length + bytesPerLine - 1)/bytesPerLine;
StringBuilder sb = new StringBuilder(numLines * lineLength); var sb = new StringBuilder(numLines*lineLength);
for (int i = 0; i < bytes.Length; i += bytesPerLine) for (var i = 0; i < bytes.Length; i += bytesPerLine)
{ {
int h = i + offset; var h = i + offset;
line[0] = hexChars[(h >> 28) & 0xF]; line[0] = hexChars[(h >> 28) & 0xF];
line[1] = hexChars[(h >> 24) & 0xF]; line[1] = hexChars[(h >> 24) & 0xF];
@ -51,10 +49,10 @@ namespace FFXIVClassic.Common
line[6] = hexChars[(h >> 4) & 0xF]; line[6] = hexChars[(h >> 4) & 0xF];
line[7] = hexChars[(h >> 0) & 0xF]; line[7] = hexChars[(h >> 0) & 0xF];
int hexColumn = offsetBlock; var hexColumn = offsetBlock;
int charColumn = byteBlock; var charColumn = byteBlock;
for (int j = 0; j < bytesPerLine; j++) for (var j = 0; j < bytesPerLine; j++)
{ {
if (j > 0 && (j & 7) == 0) if (j > 0 && (j & 7) == 0)
{ {
@ -69,10 +67,10 @@ namespace FFXIVClassic.Common
} }
else else
{ {
byte by = bytes[i + j]; var by = bytes[i + j];
line[hexColumn] = hexChars[(by >> 4) & 0xF]; line[hexColumn] = hexChars[(by >> 4) & 0xF];
line[hexColumn + 1] = hexChars[by & 0xF]; line[hexColumn + 1] = hexChars[by & 0xF];
line[charColumn] = (by < 32 ? '.' : (char)by); line[charColumn] = by < 32 ? '.' : (char) by;
} }
hexColumn += 3; hexColumn += 3;
@ -82,41 +80,41 @@ namespace FFXIVClassic.Common
sb.Append(line); sb.Append(line);
} }
return sb.ToString(); return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
} }
public static UInt32 UnixTimeStampUTC() public static uint UnixTimeStampUTC()
{ {
UInt32 unixTimeStamp; uint unixTimeStamp;
DateTime currentTime = DateTime.Now; var currentTime = DateTime.Now;
DateTime zuluTime = currentTime.ToUniversalTime(); var zuluTime = currentTime.ToUniversalTime();
DateTime unixEpoch = new DateTime(1970, 1, 1); var unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (UInt32)(zuluTime.Subtract(unixEpoch)).TotalSeconds; unixTimeStamp = (uint) zuluTime.Subtract(unixEpoch).TotalSeconds;
return unixTimeStamp; return unixTimeStamp;
} }
public static UInt64 MilisUnixTimeStampUTC() public static ulong MilisUnixTimeStampUTC()
{ {
UInt64 unixTimeStamp; ulong unixTimeStamp;
DateTime currentTime = DateTime.Now; var currentTime = DateTime.Now;
DateTime zuluTime = currentTime.ToUniversalTime(); var zuluTime = currentTime.ToUniversalTime();
DateTime unixEpoch = new DateTime(1970, 1, 1); var unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (UInt64)(zuluTime.Subtract(unixEpoch)).TotalMilliseconds; unixTimeStamp = (ulong) zuluTime.Subtract(unixEpoch).TotalMilliseconds;
return unixTimeStamp; return unixTimeStamp;
} }
public static ulong SwapEndian(ulong input) public static ulong SwapEndian(ulong input)
{ {
return ((0x00000000000000FF) & (input >> 56) | return 0x00000000000000FF & (input >> 56) |
(0x000000000000FF00) & (input >> 40) | 0x000000000000FF00 & (input >> 40) |
(0x0000000000FF0000) & (input >> 24) | 0x0000000000FF0000 & (input >> 24) |
(0x00000000FF000000) & (input >> 8) | 0x00000000FF000000 & (input >> 8) |
(0x000000FF00000000) & (input << 8) | 0x000000FF00000000 & (input << 8) |
(0x0000FF0000000000) & (input << 24) | 0x0000FF0000000000 & (input << 24) |
(0x00FF000000000000) & (input << 40) | 0x00FF000000000000 & (input << 40) |
(0xFF00000000000000) & (input << 56)); 0xFF00000000000000 & (input << 56);
} }
public static uint SwapEndian(uint input) public static uint SwapEndian(uint input)
@ -129,7 +127,7 @@ namespace FFXIVClassic.Common
public static int SwapEndian(int input) public static int SwapEndian(int input)
{ {
uint inputAsUint = (uint)input; var inputAsUint = (uint) input;
input = (int) input = (int)
(((inputAsUint >> 24) & 0xff) | (((inputAsUint >> 24) & 0xff) |
@ -145,15 +143,15 @@ namespace FFXIVClassic.Common
// 'm' and 'r' are mixing constants generated offline. // 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well. // They're not really 'magic', they just happen to work well.
byte[] data = Encoding.ASCII.GetBytes(key); var data = Encoding.ASCII.GetBytes(key);
const uint m = 0x5bd1e995; const uint m = 0x5bd1e995;
const int r = 24; const int r = 24;
int len = key.Length; var len = key.Length;
int dataIndex = len - 4; var dataIndex = len - 4;
// Initialize the hash to a 'random' value // Initialize the hash to a 'random' value
uint h = seed ^ (uint)len; var h = seed ^ (uint) len;
// Mix 4 bytes at a time into the hash // Mix 4 bytes at a time into the hash
@ -162,7 +160,7 @@ namespace FFXIVClassic.Common
{ {
h *= m; h *= m;
uint k = (uint)BitConverter.ToInt32(data, dataIndex); var k = (uint) BitConverter.ToInt32(data, dataIndex);
k = ((k >> 24) & 0xff) | // move byte 3 to byte 0 k = ((k >> 24) & 0xff) | // move byte 3 to byte 0
((k << 8) & 0xff0000) | // move byte 1 to byte 2 ((k << 8) & 0xff0000) | // move byte 1 to byte 2
((k >> 8) & 0xff00) | // move byte 2 to byte 1 ((k >> 8) & 0xff00) | // move byte 2 to byte 1
@ -182,14 +180,17 @@ namespace FFXIVClassic.Common
switch (len) switch (len)
{ {
case 3: case 3:
h ^= (uint)data[0] << 16; goto case 2; h ^= (uint) data[0] << 16;
goto case 2;
case 2: case 2:
h ^= (uint)data[len - 2] << 8; goto case 1; h ^= (uint) data[len - 2] << 8;
goto case 1;
case 1: case 1:
h ^= data[len - 1]; h ^= data[len - 1];
h *= m; h *= m;
break; break;
}; }
;
// Do a few final mixes of the hash to ensure the last few // Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated. // bytes are well-incorporated.
@ -203,16 +204,16 @@ namespace FFXIVClassic.Common
public static byte[] ConvertBoolArrayToBinaryStream(bool[] array) public static byte[] ConvertBoolArrayToBinaryStream(bool[] array)
{ {
byte[] data = new byte[(array.Length / 8) + (array.Length % 8 != 0 ? 1 : 0)]; var data = new byte[array.Length/8 + (array.Length%8 != 0 ? 1 : 0)];
int dataCounter = 0; var dataCounter = 0;
for (int i = 0; i < array.Length; i += 8) for (var i = 0; i < array.Length; i += 8)
{ {
for (int bitCount = 0; bitCount < 8; bitCount++) for (var bitCount = 0; bitCount < 8; bitCount++)
{ {
if (i + bitCount >= array.Length) if (i + bitCount >= array.Length)
break; break;
data[dataCounter] = (byte)(((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]); data[dataCounter] = (byte) (((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]);
} }
dataCounter++; dataCounter++;
} }
@ -222,10 +223,10 @@ namespace FFXIVClassic.Common
public static string ToStringBase63(int number) public static string ToStringBase63(int number)
{ {
string lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; var lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
string secondDigit = lookup.Substring((int)Math.Floor((double)number / (double)lookup.Length), 1); var secondDigit = lookup.Substring((int) Math.Floor(number/(double) lookup.Length), 1);
string firstDigit = lookup.Substring(number % lookup.Length, 1); var firstDigit = lookup.Substring(number%lookup.Length, 1);
return secondDigit + firstDigit; return secondDigit + firstDigit;
} }

View 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>
</configuration>

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="MySql.Data" version="6.9.8" targetFramework="net45" />
<package id="NLog" version="4.3.5" targetFramework="net45" /> <package id="NLog" version="4.3.5" targetFramework="net45" />
</packages> </packages>

View file

@ -58,8 +58,9 @@
<Reference Include="FFXIVClassic.Common"> <Reference Include="FFXIVClassic.Common">
<HintPath>..\FFXIVClassic Common Class Lib\bin\Debug\FFXIVClassic.Common.dll</HintPath> <HintPath>..\FFXIVClassic Common Class Lib\bin\Debug\FFXIVClassic.Common.dll</HintPath>
</Reference> </Reference>
<Reference Include="MySql.Data"> <Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll</HintPath> <HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
@ -112,6 +113,7 @@
<Content Include="NLog.config"> <Content Include="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Include="app.config" />
<None Include="NLog.xsd"> <None Include="NLog.xsd">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>

View file

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true" autoReload="true"
throwExceptions="false" throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log" > internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- optional, add some variabeles <!-- optional, add some variabeles
https://github.com/nlog/NLog/wiki/Configuration-file#variables https://github.com/nlog/NLog/wiki/Configuration-file#variables
--> -->
<variable name="myvar" value="myvalue"/> <variable name="myvar" value="myvalue" />
<!-- <!--
See https://github.com/nlog/nlog/wiki/Configuration-file See https://github.com/nlog/nlog/wiki/Configuration-file
@ -29,14 +30,30 @@
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log" <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" /> layout="${longdate} ${uppercase:${level}} ${message}" />
--> -->
<target xsi:type="ColoredConsole" name="console" 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}/lobby.log" 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}', 'FFXIVClassic_Lobby_Server.packets.BasePacket') and equals('${event-context:item=color}', '6')"
backgroundColor="DarkYellow" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic_Lobby_Server.packets.SubPacket') and equals('${event-context:item=color}', '4')"
backgroundColor="DarkRed" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic_Lobby_Server.packets.SubPacket') and equals('${event-context:item=color}', '5')"
backgroundColor="DarkMagenta" foregroundColor="NoChange" />
</target>
</targets> </targets>
<rules> <rules>
<!-- add your logging rules here --> <!-- add your logging rules here -->
<logger name='*' minlevel='Trace' writeTo='file' /> <logger name='*' minlevel='Trace' writeTo='file' />
<logger name='*' minlevel='Trace' writeTo='console' /> <logger name='*' minlevel='Info' writeTo='console' />
<logger name='FFXIVClassic_Lobby_Server.packets.*' minlevel='Debug' writeTo='packets' />
<!-- <!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f" 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" /> <logger name="*" minlevel="Debug" writeTo="f" />

View file

@ -20,9 +20,7 @@ namespace FFXIVClassic_Lobby_Server
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out); TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter); Debug.Listeners.Add(myWriter);
#endif #endif
Console.ForegroundColor = ConsoleColor.Yellow; Program.Log.Info("--------FFXIV 1.0 Lobby Server--------");
Console.WriteLine("--------FFXIV 1.0 Lobby Server--------");
Console.ForegroundColor = ConsoleColor.Gray;
bool startServer = true; bool startServer = true;
@ -31,7 +29,7 @@ namespace FFXIVClassic_Lobby_Server
startServer = false; startServer = false;
//Test DB Connection //Test DB Connection
Program.Log.Info("Testing DB connection to \"{0}\"", ConfigConstants.DATABASE_HOST); 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))) 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 try

View file

@ -3,7 +3,7 @@
<system.data> <system.data>
<DbProviderFactories> <DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" /> <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.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" /> <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> </DbProviderFactories>
</system.data> </system.data>
</configuration> </configuration>

View file

@ -2,7 +2,7 @@
<packages> <packages>
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" /> <package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
<package id="Dapper" version="1.42" targetFramework="net45" /> <package id="Dapper" version="1.42" targetFramework="net45" />
<package id="MySql.Data" version="6.9.7" targetFramework="net45" /> <package id="MySql.Data" version="6.9.8" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" /> <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="NLog" version="4.3.4" targetFramework="net45" /> <package id="NLog" version="4.3.4" targetFramework="net45" />
<package id="NLog.Config" version="4.3.4" targetFramework="net45" /> <package id="NLog.Config" version="4.3.4" targetFramework="net45" />

View file

@ -1,13 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics; using System.Diagnostics;
using FFXIVClassic.Common;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
namespace FFXIVClassic_Lobby_Server.packets namespace FFXIVClassic_Lobby_Server.packets
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct BasePacketHeader public struct BasePacketHeader
{ {
@ -21,25 +22,25 @@ namespace FFXIVClassic_Lobby_Server.packets
public class BasePacket public class BasePacket
{ {
public const int TYPE_ZONE = 1; public const int TYPE_ZONE = 1;
public const int TYPE_CHAT = 2; public const int TYPE_CHAT = 2;
public const int BASEPACKET_SIZE = 0x10; public const int BASEPACKET_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public BasePacketHeader header;
public byte[] data; public byte[] data;
public BasePacketHeader header;
//Loads a sniffed packet from a file //Loads a sniffed packet from a file
public unsafe BasePacket(String path) public unsafe BasePacket(string path)
{ {
byte[] bytes = File.ReadAllBytes(path); var bytes = File.ReadAllBytes(path);
if (bytes.Length < BASEPACKET_SIZE) if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small"); throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
if (bytes.Length < header.packetSize) if (bytes.Length < header.packetSize)
@ -64,7 +65,7 @@ namespace FFXIVClassic_Lobby_Server.packets
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
if (bytes.Length < header.packetSize) if (bytes.Length < header.packetSize)
@ -83,7 +84,7 @@ namespace FFXIVClassic_Lobby_Server.packets
fixed (byte* pdata = &bytes[offset]) fixed (byte* pdata = &bytes[offset])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
int packetSize = header.packetSize; int packetSize = header.packetSize;
@ -105,9 +106,9 @@ namespace FFXIVClassic_Lobby_Server.packets
public List<SubPacket> GetSubpackets() public List<SubPacket> GetSubpackets()
{ {
List<SubPacket> subpackets = new List<SubPacket>(header.numSubpackets); var subpackets = new List<SubPacket>(header.numSubpackets);
int offset = 0; var offset = 0;
while (offset < data.Length) while (offset < data.Length)
subpackets.Add(new SubPacket(data, ref offset)); subpackets.Add(new SubPacket(data, ref offset));
@ -115,7 +116,7 @@ namespace FFXIVClassic_Lobby_Server.packets
return subpackets; return subpackets;
} }
public unsafe static BasePacketHeader GetHeader(byte[] bytes) public static unsafe BasePacketHeader GetHeader(byte[] bytes)
{ {
BasePacketHeader header; BasePacketHeader header;
if (bytes.Length < BASEPACKET_SIZE) if (bytes.Length < BASEPACKET_SIZE)
@ -123,7 +124,7 @@ namespace FFXIVClassic_Lobby_Server.packets
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
return header; return header;
@ -131,10 +132,10 @@ namespace FFXIVClassic_Lobby_Server.packets
public byte[] GetHeaderBytes() public byte[] GetHeaderBytes()
{ {
int size = Marshal.SizeOf(header); var size = Marshal.SizeOf(header);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true); Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -143,7 +144,7 @@ namespace FFXIVClassic_Lobby_Server.packets
public byte[] GetPacketBytes() public byte[] GetPacketBytes()
{ {
byte[] outBytes = new byte[header.packetSize]; var outBytes = new byte[header.packetSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE); Array.Copy(GetHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE);
Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length); Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length);
return outBytes; return outBytes;
@ -152,16 +153,17 @@ namespace FFXIVClassic_Lobby_Server.packets
//Replaces all instances of the sniffed actorID with the given one //Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint actorID) public void ReplaceActorID(uint actorID)
{ {
using (MemoryStream mem = new MemoryStream(data)) using (var mem = new MemoryStream(data))
{ {
using (BinaryWriter binWriter = new BinaryWriter(mem)) using (var binWriter = new BinaryWriter(mem))
{ {
using (BinaryReader binreader = new BinaryReader(mem)) using (var binreader = new BinaryReader(mem))
{ {
while (binreader.BaseStream.Position + 4 < data.Length) while (binreader.BaseStream.Position + 4 < data.Length)
{ {
uint read = binreader.ReadUInt32(); var read = binreader.ReadUInt32();
if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 || read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb) //Original ID if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 ||
read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb) //Original ID
{ {
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
binWriter.Write(actorID); binWriter.Write(actorID);
@ -175,15 +177,15 @@ namespace FFXIVClassic_Lobby_Server.packets
//Replaces all instances of the sniffed actorID with the given one //Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint fromActorID, uint actorID) public void ReplaceActorID(uint fromActorID, uint actorID)
{ {
using (MemoryStream mem = new MemoryStream(data)) using (var mem = new MemoryStream(data))
{ {
using (BinaryWriter binWriter = new BinaryWriter(mem)) using (var binWriter = new BinaryWriter(mem))
{ {
using (BinaryReader binreader = new BinaryReader(mem)) using (var binreader = new BinaryReader(mem))
{ {
while (binreader.BaseStream.Position + 4 < data.Length) while (binreader.BaseStream.Position + 4 < data.Length)
{ {
uint read = binreader.ReadUInt32(); var read = binreader.ReadUInt32();
if (read == fromActorID) //Original ID if (read == fromActorID) //Original ID
{ {
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
@ -195,49 +197,65 @@ namespace FFXIVClassic_Lobby_Server.packets
} }
} }
public void DebugPrintPacket()
{
#if DEBUG
logger.ColorDebug(
string.Format("IsAuth:{0} Size:0x{1:X}, NumSubpackets:{2}{3}{4}",
header.isAuthenticated, header.packetSize, header.numSubpackets,
Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkYellow);
foreach (var sub in GetSubpackets())
{
sub.DebugPrintSubPacket();
}
#endif
}
#region Utility Functions #region Utility Functions
public static BasePacket CreatePacket(List<SubPacket> subpackets, bool isAuthed, bool isEncrypted) public static BasePacket CreatePacket(List<SubPacket> subpackets, bool isAuthed, bool isEncrypted)
{ {
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
byte[] data = null; byte[] data = null;
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; header.isEncrypted = isEncrypted ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)subpackets.Count; header.numSubpackets = (ushort) subpackets.Count;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size //Get packet size
foreach (SubPacket subpacket in subpackets) foreach (var subpacket in subpackets)
header.packetSize += subpacket.header.subpacketSize; header.packetSize += subpacket.header.subpacketSize;
data = new byte[header.packetSize - 0x10]; data = new byte[header.packetSize - 0x10];
//Add Subpackets //Add Subpackets
int offset = 0; var offset = 0;
foreach (SubPacket subpacket in subpackets) foreach (var subpacket in subpackets)
{ {
byte[] subpacketData = subpacket.GetBytes(); var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, offset, subpacketData.Length); Array.Copy(subpacketData, 0, data, offset, subpacketData.Length);
offset += (ushort)subpacketData.Length; offset += (ushort) subpacketData.Length;
} }
Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset); Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset);
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static BasePacket CreatePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted) public static BasePacket CreatePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted)
{ {
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
byte[] data = null; byte[] data = null;
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; header.isEncrypted = isEncrypted ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)1; header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
@ -247,42 +265,41 @@ namespace FFXIVClassic_Lobby_Server.packets
data = new byte[header.packetSize - 0x10]; data = new byte[header.packetSize - 0x10];
//Add Subpackets //Add Subpackets
byte[] subpacketData = subpacket.GetBytes(); var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, 0, subpacketData.Length); Array.Copy(subpacketData, 0, data, 0, subpacketData.Length);
Debug.Assert(data != null); Debug.Assert(data != null);
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static BasePacket CreatePacket(byte[] data, bool isAuthed, bool isEncrypted) public static BasePacket CreatePacket(byte[] data, bool isAuthed, bool isEncrypted)
{ {
Debug.Assert(data != null); Debug.Assert(data != null);
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isEncrypted = isEncrypted ? (byte)1 : (byte)0; header.isEncrypted = isEncrypted ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)1; header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size //Get packet size
header.packetSize += (ushort)data.Length; header.packetSize += (ushort) data.Length;
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet) public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet)
{ {
byte[] data = packet.data; var data = packet.data;
int size = packet.header.packetSize; int size = packet.header.packetSize;
int offset = 0; var offset = 0;
while (offset < data.Length) while (offset < data.Length)
{ {
if (data.Length < offset + SubPacket.SUBPACKET_SIZE) if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
@ -291,7 +308,7 @@ namespace FFXIVClassic_Lobby_Server.packets
SubPacketHeader header; SubPacketHeader header;
fixed (byte* pdata = &data[offset]) fixed (byte* pdata = &data[offset])
{ {
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
} }
if (data.Length < offset + header.subpacketSize) if (data.Length < offset + header.subpacketSize)
@ -301,15 +318,14 @@ namespace FFXIVClassic_Lobby_Server.packets
offset += header.subpacketSize; offset += header.subpacketSize;
} }
} }
public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet) public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet)
{ {
byte[] data = packet.data; var data = packet.data;
int size = packet.header.packetSize; int size = packet.header.packetSize;
int offset = 0; var offset = 0;
while (offset < data.Length) while (offset < data.Length)
{ {
if (data.Length < offset + SubPacket.SUBPACKET_SIZE) if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
@ -318,7 +334,7 @@ namespace FFXIVClassic_Lobby_Server.packets
SubPacketHeader header; SubPacketHeader header;
fixed (byte* pdata = &data[offset]) fixed (byte* pdata = &data[offset])
{ {
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
} }
if (data.Length < offset + header.subpacketSize) if (data.Length < offset + header.subpacketSize)
@ -329,24 +345,17 @@ namespace FFXIVClassic_Lobby_Server.packets
offset += header.subpacketSize; offset += header.subpacketSize;
} }
} }
#endregion #endregion
public void DebugPrintPacket()
{
#if DEBUG
// todo: create new target for colourful packet logging
//Console.BackgroundColor = ConsoleColor.DarkYellow;
Program.Log.Debug("IsAuth: {0} Size: 0x{1:X}, NumSubpackets: {2}{3}{4}", header.isAuthenticated, header.packetSize, header.numSubpackets, Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes()));
foreach (SubPacket sub in GetSubpackets())
{
sub.DebugPrintSubPacket();
} }
//Console.BackgroundColor = ConsoleColor.Black; public static class LoggerExtensions
#endif {
public static void ColorDebug(this Logger logger, string message, ConsoleOutputColor color)
{
var logEvent = new LogEventInfo(LogLevel.Debug, logger.Name, message);
logEvent.Properties["color"] = (int) color;
logger.Log(logEvent);
} }
} }
} }

View file

@ -1,6 +1,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FFXIVClassic.Common; using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
namespace FFXIVClassic_Lobby_Server.packets namespace FFXIVClassic_Lobby_Server.packets
{ {
@ -28,10 +30,11 @@ namespace FFXIVClassic_Lobby_Server.packets
{ {
public const int SUBPACKET_SIZE = 0x10; public const int SUBPACKET_SIZE = 0x10;
public const int GAMEMESSAGE_SIZE = 0x10; public const int GAMEMESSAGE_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public byte[] data;
public GameMessageHeader gameMessage;
public SubPacketHeader header; public SubPacketHeader header;
public GameMessageHeader gameMessage;
public byte[] data;
public unsafe SubPacket(byte[] bytes, ref int offset) public unsafe SubPacket(byte[] bytes, ref int offset)
{ {
@ -40,14 +43,15 @@ namespace FFXIVClassic_Lobby_Server.packets
fixed (byte* pdata = &bytes[offset]) fixed (byte* pdata = &bytes[offset])
{ {
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
} }
if (header.type == 0x3) if (header.type == 0x3)
{ {
fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE]) fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE])
{ {
gameMessage = (GameMessageHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader)); gameMessage =
(GameMessageHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader));
} }
} }
@ -70,8 +74,8 @@ namespace FFXIVClassic_Lobby_Server.packets
public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data)
{ {
this.header = new SubPacketHeader(); header = new SubPacketHeader();
this.gameMessage = new GameMessageHeader(); gameMessage = new GameMessageHeader();
gameMessage.opcode = opcode; gameMessage.opcode = opcode;
header.sourceId = sourceId; header.sourceId = sourceId;
@ -87,13 +91,13 @@ namespace FFXIVClassic_Lobby_Server.packets
this.data = data; this.data = data;
header.subpacketSize = (ushort)(SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length); header.subpacketSize = (ushort) (SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length);
} }
public SubPacket(SubPacket original, uint newTargetId) public SubPacket(SubPacket original, uint newTargetId)
{ {
this.header = new SubPacketHeader(); header = new SubPacketHeader();
this.gameMessage = original.gameMessage; gameMessage = original.gameMessage;
header.subpacketSize = original.header.subpacketSize; header.subpacketSize = original.header.subpacketSize;
header.type = original.header.type; header.type = original.header.type;
header.sourceId = original.header.sourceId; header.sourceId = original.header.sourceId;
@ -103,10 +107,10 @@ namespace FFXIVClassic_Lobby_Server.packets
public byte[] GetHeaderBytes() public byte[] GetHeaderBytes()
{ {
int size = Marshal.SizeOf(header); var size = Marshal.SizeOf(header);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true); Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -115,10 +119,10 @@ namespace FFXIVClassic_Lobby_Server.packets
public byte[] GetGameMessageBytes() public byte[] GetGameMessageBytes()
{ {
int size = Marshal.SizeOf(gameMessage); var size = Marshal.SizeOf(gameMessage);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(gameMessage, ptr, true); Marshal.StructureToPtr(gameMessage, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -127,7 +131,7 @@ namespace FFXIVClassic_Lobby_Server.packets
public byte[] GetBytes() public byte[] GetBytes()
{ {
byte[] outBytes = new byte[header.subpacketSize]; var outBytes = new byte[header.subpacketSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE); Array.Copy(GetHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE);
if (header.type == 0x3) if (header.type == 0x3)
@ -140,17 +144,20 @@ namespace FFXIVClassic_Lobby_Server.packets
public void DebugPrintSubPacket() public void DebugPrintSubPacket()
{ {
#if DEBUG #if DEBUG
// todo: create new target for colourful packet logging logger.ColorDebug(
Program.Log.Debug("Size: 0x{0:X}{1}{2}", header.subpacketSize, Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes())); string.Format("Size:0x{0:X} Opcode:0x{1:X}{2}{3}", header.subpacketSize, gameMessage.opcode,
Environment.NewLine,
Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkRed);
if (header.type == 0x03) if (header.type == 0x03)
{ {
Program.Log.Debug("Opcode: 0x{0:X}{1}{2}", gameMessage.opcode, Environment.NewLine, Utils.ByteArrayToHex(GetGameMessageBytes(), SUBPACKET_SIZE)); logger.ColorDebug(Utils.ByteArrayToHex(GetGameMessageBytes(), SUBPACKET_SIZE),
} ConsoleOutputColor.DarkRed);
Program.Log.Debug("Data: {0}{1}", Environment.NewLine, Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE)); logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE),
ConsoleOutputColor.DarkMagenta);
}
#endif #endif
} }
} }
} }

View file

@ -6,6 +6,6 @@
<system.data> <system.data>
<DbProviderFactories> <DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" /> <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.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" /> <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> </DbProviderFactories>
</system.data></configuration> </system.data></configuration>

View file

@ -35,25 +35,6 @@ namespace FFXIVClassic_Map_Server
mConnectedPlayerList = playerList; mConnectedPlayerList = playerList;
} }
public void SendPacket(ConnectedPlayer client, string path)
{
BasePacket packet = new BasePacket(path);
if (client != null)
{
packet.ReplaceActorID(client.actorID);
client.QueuePacket(packet);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
packet.ReplaceActorID(entry.Value.actorID);
entry.Value.QueuePacket(packet);
}
}
}
public void ChangeProperty(uint id, uint value, string target) public void ChangeProperty(uint id, uint value, string target)
{ {
SetActorPropetyPacket ChangeProperty = new SetActorPropetyPacket(target); SetActorPropetyPacket ChangeProperty = new SetActorPropetyPacket(target);
@ -73,418 +54,6 @@ namespace FFXIVClassic_Map_Server
} }
} }
public void DoMusic(ConnectedPlayer client, string music)
{
ushort musicId;
if (music.ToLower().StartsWith("0x"))
musicId = Convert.ToUInt16(music, 16);
else
musicId = Convert.ToUInt16(music);
if (client != null)
client.QueuePacket(BasePacket.CreatePacket(SetMusicPacket.BuildPacket(client.actorID, musicId, 1), true, false));
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
BasePacket musicPacket = BasePacket.CreatePacket(SetMusicPacket.BuildPacket(entry.Value.actorID, musicId, 1), true, false);
entry.Value.QueuePacket(musicPacket);
}
}
}
/// <summary>
/// Teleports player to a location on a predefined list
/// </summary>
/// <param name="client">The current player</param>
/// <param name="id">Predefined list: &lt;ffxiv_database&gt;\server_zones_spawnlocations</param>
public void DoWarp(ConnectedPlayer client, uint id)
{
WorldManager worldManager = Server.GetWorldManager();
FFXIVClassic_Map_Server.WorldManager.ZoneEntrance ze = worldManager.GetZoneEntrance(id);
if (ze == null)
return;
if (client != null)
worldManager.DoZoneChange(client.GetActor(), ze.zoneId, ze.privateAreaName, ze.spawnType, ze.spawnX, ze.spawnY, ze.spawnZ, ze.spawnRotation);
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
worldManager.DoZoneChange(entry.Value.GetActor(), ze.zoneId, ze.privateAreaName, ze.spawnType, ze.spawnX, ze.spawnY, ze.spawnZ, ze.spawnRotation);
}
}
}
public void DoWarp(ConnectedPlayer client, uint zoneId, string privateArea, byte spawnType, float x, float y, float z, float r)
{
WorldManager worldManager = Server.GetWorldManager();
if (worldManager.GetZone(zoneId) == null)
{
if (client != null)
client.QueuePacket(BasePacket.CreatePacket(SendMessagePacket.BuildPacket(client.actorID, client.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", "Zone does not exist or setting isn't valid."), true, false));
Program.Log.Error("Zone does not exist or setting isn't valid.");
}
if (client != null)
worldManager.DoZoneChange(client.GetActor(), zoneId, privateArea, spawnType, x, y, z, r);
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
worldManager.DoZoneChange(entry.Value.GetActor(), zoneId, privateArea, spawnType, x, y, z, r);
}
}
}
public void PrintPos(ConnectedPlayer client)
{
if (client != null)
{
Player p = client.GetActor();
client.QueuePacket(BasePacket.CreatePacket(SendMessagePacket.BuildPacket(client.actorID, client.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", String.Format("{0}\'s position: ZoneID: {1}, X: {2}, Y: {3}, Z: {4}, Rotation: {5}", p.customDisplayName, p.zoneId, p.positionX, p.positionY, p.positionZ, p.rotation)), true, false));
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
Program.Log.Info(String.Format("{0}\'s position: ZoneID: {1}, X: {2}, Y: {3}, Z: {4}, Rotation: {5}", p.customDisplayName, p.zoneId, p.positionX, p.positionY, p.positionZ, p.rotation));
}
}
}
private void SetGraphic(ConnectedPlayer client, uint slot, uint wId, uint eId, uint vId, uint cId)
{
if (client != null)
{
Player p = client.GetActor();
p.GraphicChange(slot, wId, eId, vId, cId);
p.SendAppearance();
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GraphicChange(slot, wId, eId, vId, cId);
p.SendAppearance();
}
}
}
private void GiveItem(ConnectedPlayer client, uint itemId, int quantity)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.NORMAL).AddItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.NORMAL).AddItem(itemId, quantity);
}
}
}
private void GiveItem(ConnectedPlayer client, uint itemId, int quantity, ushort type)
{
if (client != null)
{
Player p = client.GetActor();
if (p.GetInventory(type) != null)
p.GetInventory(type).AddItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
if (p.GetInventory(type) != null)
p.GetInventory(type).AddItem(itemId, quantity);
}
}
}
private void RemoveItem(ConnectedPlayer client, uint itemId, int quantity)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.NORMAL).RemoveItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.NORMAL).RemoveItem(itemId, quantity);
}
}
}
private void RemoveItem(ConnectedPlayer client, uint itemId, int quantity, ushort type)
{
if (client != null)
{
Player p = client.GetActor();
if (p.GetInventory(type) != null)
p.GetInventory(type).RemoveItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
if (p.GetInventory(type) != null)
p.GetInventory(type).RemoveItem(itemId, quantity);
}
}
}
private void GiveCurrency(ConnectedPlayer client, uint itemId, int quantity)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.CURRENCY).AddItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.CURRENCY).AddItem(itemId, quantity);
}
}
}
// TODO: make RemoveCurrency() Remove all quantity of a currency if quantity_to_Remove > quantity_in_inventory instead of silently failing
private void RemoveCurrency(ConnectedPlayer client, uint itemId, int quantity)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.CURRENCY).RemoveItem(itemId, quantity);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.CURRENCY).RemoveItem(itemId, quantity);
}
}
}
private void GiveKeyItem(ConnectedPlayer client, uint itemId)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.KEYITEMS).AddItem(itemId, 1);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.KEYITEMS).AddItem(itemId, 1);
}
}
}
private void RemoveKeyItem(ConnectedPlayer client, uint itemId)
{
if (client != null)
{
Player p = client.GetActor();
p.GetInventory(Inventory.KEYITEMS).RemoveItem(itemId, 1);
}
else
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
Player p = entry.Value.GetActor();
p.GetInventory(Inventory.KEYITEMS).RemoveItem(itemId, 1);
}
}
}
private void ParseWarp(ConnectedPlayer client, string[] split)
{
float x = 0, y = 0, z = 0, r = 0.0f;
uint zoneId = 0;
string privatearea = null;
if (split.Length == 2) // Predefined list
{
// TODO: Handle !warp Playername
#region !warp (predefined list)
try
{
if (split[1].ToLower().StartsWith("0x"))
zoneId = Convert.ToUInt32(split[1], 16);
else
zoneId = Convert.ToUInt32(split[1]);
}
catch{return;}
#endregion
DoWarp(client, zoneId);
}
else if (split.Length == 4)
{
#region !warp X Y Z
if (split[1].StartsWith("@"))
{
split[1] = split[1].Replace("@", string.Empty);
if (String.IsNullOrEmpty(split[1]))
split[1] = "0";
try { x = Single.Parse(split[1]) + client.GetActor().positionX; }
catch{return;}
split[1] = x.ToString();
}
if (split[2].StartsWith("@"))
{
split[2] = split[2].Replace("@", string.Empty);
if (String.IsNullOrEmpty(split[2]))
split[2] = "0";
try { y = Single.Parse(split[2]) + client.GetActor().positionY; }
catch{return;}
split[2] = y.ToString();
}
if (split[3].StartsWith("@"))
{
split[3] = split[3].Replace("@", string.Empty);
if (String.IsNullOrEmpty(split[3]))
split[3] = "0";
try { z = Single.Parse(split[3]) + client.GetActor().positionZ; }
catch{return;}
split[3] = z.ToString();
}
try
{
x = Single.Parse(split[1]);
y = Single.Parse(split[2]);
z = Single.Parse(split[3]);
}
catch{return;}
zoneId = client.GetActor().zoneId;
r = client.GetActor().rotation;
#endregion
SendMessage(client, String.Format("Warping to: ZoneID: {0} X: {1}, Y: {2}, Z: {3}", zoneId, x, y, z));
DoWarp(client, zoneId, privatearea, 0x00, x, y, z, r);
}
else if (split.Length == 5)
{
#region !warp Zone X Y Z
try
{
x = Single.Parse(split[2]);
y = Single.Parse(split[3]);
z = Single.Parse(split[4]);
}
catch{return;}
if (split[1].ToLower().StartsWith("0x"))
{
try { zoneId = Convert.ToUInt32(split[1], 16); }
catch{return;}
}
else
{
try { zoneId = Convert.ToUInt32(split[1]); }
catch{return;}
}
#endregion
SendMessage(client, String.Format("Warping to: ZoneID: {0} X: {1}, Y: {2}, Z: {3}", zoneId, x, y, z));
DoWarp(client, zoneId, privatearea, 0x2, x, y, z, r);
}
else if (split.Length == 6)
{
#region !warp Zone Instance X Y Z
try
{
x = Single.Parse(split[3]);
y = Single.Parse(split[4]);
z = Single.Parse(split[5]);
}
catch{return;}
if (split[1].ToLower().StartsWith("0x"))
{
try { zoneId = Convert.ToUInt32(split[1], 16); }
catch{return;}
}
else
{
try { zoneId = Convert.ToUInt32(split[1]); }
catch{return;}
}
privatearea = split[2];
#endregion
SendMessage(client, String.Format("Warping to: ZoneID: {0} X: {1}, Y: {2}, Z: {3}", zoneId, x, y, z));
DoWarp(client, zoneId, privatearea, 0x2, x, y, z, r);
}
else
return; // catch any invalid warps here
}
private void doWeather(ConnectedPlayer client, string weatherID, string value)
{
ushort weather = Convert.ToUInt16(weatherID);
if (client != null)
{
client.QueuePacket(BasePacket.CreatePacket(SetWeatherPacket.BuildPacket(client.actorID, weather, Convert.ToUInt16(value)), true, false));
}
/*
* WIP: Change weather serverside, currently only clientside
*
uint currentZoneID;
if (client != null)
{
currentZoneID = client.GetActor().zoneId;
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
// Change the weather for everyone in the same zone
if (currentZoneID == entry.Value.GetActor().zoneId)
{
BasePacket weatherPacket = BasePacket.CreatePacket(SetWeatherPacket.BuildPacket(entry.Value.actorID, weather), true, false);
entry.Value.QueuePacket(weatherPacket);
}
}
}
*/
}
/// <summary> /// <summary>
/// We only use the default options for SendMessagePacket. /// We only use the default options for SendMessagePacket.
/// May as well make it less unwieldly to view /// May as well make it less unwieldly to view
@ -499,136 +68,61 @@ namespace FFXIVClassic_Map_Server
internal bool DoCommand(string input, ConnectedPlayer client) internal bool DoCommand(string input, ConnectedPlayer client)
{ {
if (!input.Any() || input.Equals(""))
return false;
input.Trim(); input.Trim();
if (input.StartsWith("!")) input = input.StartsWith("!") ? input.Substring(1) : input;
input = input.Substring(1);
var split = input.Split('"')
.Select((str, index) => index % 2 == 0
? str.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
: new String[] { str }
)
.SelectMany(str => str).ToArray();
String[] split = input.Split(' ');
split = split.Select(temp => temp.ToLower()).ToArray(); // Ignore case on commands split = split.Select(temp => temp.ToLower()).ToArray(); // Ignore case on commands
split = split.Where(temp => temp != "").ToArray(); // strips extra whitespace from commands
var cmd = split[0];
if (cmd.Any())
{
// if client isnt null, take player to be the player actor
var player = client?.GetActor();
if (cmd.Equals("help"))
{
// if there's another string after this, take it as the command we want the description for
if (split.Length > 1)
{
LuaEngine.RunGMCommand(player, split[1], null, true);
return true;
}
// print out all commands
foreach (var str in Directory.GetFiles("./scripts/commands/gm/"))
{
var c = str.Replace(".lua", "");
c = c.Replace("./scripts/commands/gm/", "");
LuaEngine.RunGMCommand(player, c, null, true);
}
return true;
}
LuaEngine.RunGMCommand(player, cmd.ToString(), split.ToArray());
return true;
}
// Debug // Debug
//SendMessage(client, string.Join(",", split)); //SendMessage(client, string.Join(",", split));
if (split.Length >= 1) if (split.Length >= 1)
{ {
#region !help
if (split[0].Equals("help"))
{
if (split.Length == 1)
{
SendMessage(client, Resources.CPhelp);
}
if (split.Length == 2)
{
if (split[1].Equals("mypos"))
SendMessage(client, Resources.CPmypos);
else if (split[1].Equals("music"))
SendMessage(client, Resources.CPmusic);
else if (split[1].Equals("warp"))
SendMessage(client, Resources.CPwarp);
else if (split[1].Equals("givecurrency"))
SendMessage(client, Resources.CPgivecurrency);
else if (split[1].Equals("giveitem"))
SendMessage(client, Resources.CPgiveitem);
else if (split[1].Equals("givekeyitem"))
SendMessage(client, Resources.CPgivekeyitem);
else if (split[1].Equals("Removecurrency"))
SendMessage(client, Resources.CPRemovecurrency);
else if (split[1].Equals("Removeitem"))
SendMessage(client, Resources.CPRemoveitem);
else if (split[1].Equals("Removekeyitem"))
SendMessage(client, Resources.CPRemovekeyitem);
else if (split[1].Equals("reloaditems"))
SendMessage(client, Resources.CPreloaditems);
else if (split[1].Equals("reloadzones"))
SendMessage(client, Resources.CPreloadzones);
/*
else if (split[1].Equals("property"))
SendMessage(client, Resources.CPproperty);
else if (split[1].Equals("property2"))
SendMessage(client, Resources.CPproperty2);
else if (split[1].Equals("sendpacket"))
SendMessage(client, Resources.CPsendpacket);
else if (split[1].Equals("setgraphic"))
SendMessage(client, Resources.CPsetgraphic);
*/
}
if (split.Length == 3)
{
if(split[1].Equals("test"))
{
if (split[2].Equals("weather"))
SendMessage(client, Resources.CPtestweather);
}
}
return true; // TODO: reloadzones
}
#endregion
#region !test
else if (split[0].Equals("test"))
{
if (split.Length == 1)
{
// catch invalid commands
SendMessage(client, Resources.CPhelp);
}
else if (split.Length >= 2)
{
#region !test weather
if (split[1].Equals("weather"))
{
try
{
doWeather(client, split[2], split[3]);
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not change weather: " + e);
}
}
#endregion
}
}
#endregion
#region !mypos
else if (split[0].Equals("mypos"))
{
try
{
PrintPos(client);
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not load packet: " + e);
}
}
#endregion
#region !reloadzones
else if (split[0].Equals("reloadzones"))
{
if (client != null)
{
Program.Log.Info(String.Format("Got request to reset zone: {0}", client.GetActor().zoneId));
client.GetActor().zone.Clear();
client.GetActor().zone.AddActorToZone(client.GetActor());
client.GetActor().SendInstanceUpdate();
client.QueuePacket(BasePacket.CreatePacket(SendMessagePacket.BuildPacket(client.actorID, client.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", String.Format("Reseting zone {0}...", client.GetActor().zoneId)), true, false));
}
Server.GetWorldManager().ReloadZone(client.GetActor().zoneId);
return true;
}
#endregion
#region !reloaditems #region !reloaditems
else if (split[0].Equals("reloaditems")) if (split[0].Equals("reloaditems"))
{ {
Program.Log.Info(String.Format("Got request to reload item gamedata")); Program.Log.Info(String.Format("Got request to reload item gamedata"));
SendMessage(client, "Reloading Item Gamedata..."); SendMessage(client, "Reloading Item Gamedata...");
@ -640,181 +134,6 @@ namespace FFXIVClassic_Map_Server
} }
#endregion #endregion
#region !sendpacket
else if (split[0].Equals("sendpacket"))
{
if (split.Length < 2)
return false;
try
{
SendPacket(client, "./packets/" + split[1]);
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not load packet: " + e);
}
}
#endregion
#region !graphic
else if (split[0].Equals("graphic"))
{
try
{
if (split.Length == 6)
SetGraphic(client, UInt32.Parse(split[1]), UInt32.Parse(split[2]), UInt32.Parse(split[3]), UInt32.Parse(split[4]), UInt32.Parse(split[5]));
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not give item.");
}
}
#endregion
#region !giveitem
else if (split[0].Equals("giveitem"))
{
try
{
if (split.Length == 2)
GiveItem(client, UInt32.Parse(split[1]), 1);
else if (split.Length == 3)
GiveItem(client, UInt32.Parse(split[1]), Int32.Parse(split[2]));
else if (split.Length == 4)
GiveItem(client, UInt32.Parse(split[1]), Int32.Parse(split[2]), UInt16.Parse(split[3]));
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not give item.");
}
}
#endregion
#region !Removeitem
else if (split[0].Equals("Removeitem"))
{
if (split.Length < 2)
return false;
try
{
if (split.Length == 2)
RemoveItem(client, UInt32.Parse(split[1]), 1);
else if (split.Length == 3)
RemoveItem(client, UInt32.Parse(split[1]), Int32.Parse(split[2]));
else if (split.Length == 4)
RemoveItem(client, UInt32.Parse(split[1]), Int32.Parse(split[2]), UInt16.Parse(split[3]));
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not Remove item.");
}
}
#endregion
#region !givekeyitem
else if (split[0].Equals("givekeyitem"))
{
try
{
if (split.Length == 2)
GiveKeyItem(client, UInt32.Parse(split[1]));
}
catch (Exception e)
{
Program.Log.Error("Could not give keyitem.");
}
}
#endregion
#region !Removekeyitem
else if (split[0].Equals("Removekeyitem"))
{
if (split.Length < 2)
return false;
try
{
if (split.Length == 2)
RemoveKeyItem(client, UInt32.Parse(split[1]));
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not Remove keyitem.");
}
}
#endregion
#region !givecurrency
else if (split[0].Equals("givecurrency"))
{
try
{
if (split.Length == 2)
GiveCurrency(client, ITEM_GIL, Int32.Parse(split[1]));
else if (split.Length == 3)
GiveCurrency(client, UInt32.Parse(split[1]), Int32.Parse(split[2]));
}
catch (Exception e)
{
Program.Log.Error("Could not give currency.");
}
}
#endregion
#region !Removecurrency
else if (split[0].Equals("Removecurrency"))
{
if (split.Length < 2)
return false;
try
{
if (split.Length == 2)
RemoveCurrency(client, ITEM_GIL, Int32.Parse(split[1]));
else if (split.Length == 3)
RemoveCurrency(client, UInt32.Parse(split[1]), Int32.Parse(split[2]));
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not Remove currency.");
}
}
#endregion
#region !music
else if (split[0].Equals("music"))
{
if (split.Length < 2)
return false;
try
{
DoMusic(client, split[1]);
return true;
}
catch (Exception e)
{
Program.Log.Error("Could not change music: " + e);
}
}
#endregion
#region !warp
else if (split[0].Equals("warp"))
{
ParseWarp(client, split);
return true;
}
#endregion
#region !property #region !property
else if (split[0].Equals("property")) else if (split[0].Equals("property"))
{ {

View file

@ -419,6 +419,8 @@ namespace FFXIVClassic_Map_Server
player.oldRotation = player.rotation = reader.GetFloat(4); player.oldRotation = player.rotation = reader.GetFloat(4);
player.currentMainState = reader.GetUInt16(5); player.currentMainState = reader.GetUInt16(5);
player.zoneId = reader.GetUInt32(6); player.zoneId = reader.GetUInt32(6);
player.isZoning = true;
player.zone = Server.GetWorldManager().GetZone(player.zoneId);
player.gcCurrent = reader.GetByte(7); player.gcCurrent = reader.GetByte(7);
player.gcRankLimsa = reader.GetByte(8); player.gcRankLimsa = reader.GetByte(8);
player.gcRankGridania = reader.GetByte(9); player.gcRankGridania = reader.GetByte(9);

View file

@ -49,8 +49,8 @@
<Reference Include="MoonSharp.Interpreter"> <Reference Include="MoonSharp.Interpreter">
<HintPath>..\packages\MoonSharp.1.2.1.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath> <HintPath>..\packages\MoonSharp.1.2.1.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
</Reference> </Reference>
<Reference Include="MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL"> <Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll</HintPath> <HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@ -119,6 +119,7 @@
<Compile Include="lua\LuaParam.cs" /> <Compile Include="lua\LuaParam.cs" />
<Compile Include="lua\LuaNpc.cs" /> <Compile Include="lua\LuaNpc.cs" />
<Compile Include="lua\LuaPlayer.cs" /> <Compile Include="lua\LuaPlayer.cs" />
<Compile Include="lua\LuaScript.cs" />
<Compile Include="PacketProcessor.cs" /> <Compile Include="PacketProcessor.cs" />
<Compile Include="packets\BasePacket.cs" /> <Compile Include="packets\BasePacket.cs" />
<Compile Include="packets\receive\ChatMessagePacket.cs" /> <Compile Include="packets\receive\ChatMessagePacket.cs" />

View file

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true" autoReload="true"
throwExceptions="false" throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log" > internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- optional, add some variabeles <!-- optional, add some variabeles
https://github.com/nlog/NLog/wiki/Configuration-file#variables https://github.com/nlog/NLog/wiki/Configuration-file#variables
--> -->
<variable name="myvar" value="myvalue"/> <variable name="myvar" value="myvalue" />
<!-- <!--
See https://github.com/nlog/nlog/wiki/Configuration-file See https://github.com/nlog/nlog/wiki/Configuration-file
@ -29,14 +30,30 @@
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log" <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" /> layout="${longdate} ${uppercase:${level}} ${message}" />
--> -->
<target xsi:type="ColoredConsole" name="console" 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="[${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}', 'FFXIVClassic_Map_Server.packets.BasePacket') and equals('${event-context:item=color}', '6')"
backgroundColor="DarkYellow" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic_Map_Server.packets.SubPacket') and equals('${event-context:item=color}', '4')"
backgroundColor="DarkRed" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic_Map_Server.packets.SubPacket') and equals('${event-context:item=color}', '5')"
backgroundColor="DarkMagenta" foregroundColor="NoChange" />
</target>
</targets> </targets>
<rules> <rules>
<!-- add your logging rules here --> <!-- add your logging rules here -->
<logger name='*' minlevel='Trace' writeTo='file' /> <logger name='*' minlevel='Trace' writeTo='file' />
<logger name='*' minlevel='Trace' writeTo='console' /> <logger name='*' minlevel='Info' writeTo='console' />
<logger name='FFXIVClassic_Map_Server.packets.*' minlevel='Debug' writeTo='packets' />
<!-- <!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f" 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" /> <logger name="*" minlevel="Debug" writeTo="f" />

View file

@ -112,7 +112,7 @@ namespace FFXIVClassic_Map_Server
if (packet.header.connectionType == BasePacket.TYPE_ZONE) if (packet.header.connectionType == BasePacket.TYPE_ZONE)
{ {
while (!mPlayers.ContainsKey(client.owner)) while (mPlayers != null && !mPlayers.ContainsKey(client.owner))
{ } { }
player = mPlayers[client.owner]; player = mPlayers[client.owner];
} }
@ -127,9 +127,9 @@ namespace FFXIVClassic_Map_Server
player.SetConnection(packet.header.connectionType, client); player.SetConnection(packet.header.connectionType, client);
if (packet.header.connectionType == BasePacket.TYPE_ZONE) if (packet.header.connectionType == BasePacket.TYPE_ZONE)
Program.Log.Debug("Got {0} connection for ActorID {1} @ {2}.", "zone", actorID, client.GetAddress()); Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "zone", actorID, client.GetAddress());
else if (packet.header.connectionType == BasePacket.TYPE_CHAT) else if (packet.header.connectionType == BasePacket.TYPE_CHAT)
Program.Log.Debug("Got {0} connection for ActorID {1} @ {2}.", "chat", actorID, client.GetAddress()); Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "chat", actorID, client.GetAddress());
//Create player actor //Create player actor
reply1.DebugPrintPacket(); reply1.DebugPrintPacket();
@ -139,12 +139,14 @@ namespace FFXIVClassic_Map_Server
} }
else if (subpacket.header.type == 0x07) else if (subpacket.header.type == 0x07)
{ {
BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC()); BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x08);
//client.QueuePacket(init); //client.QueuePacket(init);
} }
else if (subpacket.header.type == 0x08) else if (subpacket.header.type == 0x08)
{ {
//Response, client's current [actorID][time] //Response, client's current [actorID][time]
//BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x07);
//client.QueuePacket(init);
packet.DebugPrintPacket(); packet.DebugPrintPacket();
} }
else if (subpacket.header.type == 0x03) else if (subpacket.header.type == 0x03)
@ -154,7 +156,7 @@ namespace FFXIVClassic_Map_Server
if(mPlayers.ContainsKey(client.owner)) if(mPlayers.ContainsKey(client.owner))
player = mPlayers[client.owner]; player = mPlayers[client.owner];
if (player == null) if (player == null || !player.IsClientConnectionsReady())
return; return;
//Normal Game Opcode //Normal Game Opcode

View file

@ -28,9 +28,7 @@ namespace FFXIVClassic_Map_Server
#endif #endif
bool startServer = true; bool startServer = true;
Console.ForegroundColor = ConsoleColor.Cyan; Program.Log.Info("---------FFXIV 1.0 Map Server---------");
Console.WriteLine("---------FFXIV 1.0 Map Server---------");
Console.ForegroundColor = ConsoleColor.Gray;
//Load Config //Load Config
if (!ConfigConstants.Load()) if (!ConfigConstants.Load())

View file

@ -335,7 +335,7 @@ namespace FFXIVClassic_Map_Server.Actors
className = className.Substring(0, 20 - zoneName.Length); className = className.Substring(0, 20 - zoneName.Length);
} }
catch (ArgumentOutOfRangeException e) catch (ArgumentOutOfRangeException e)
{} { }
//Convert actor number to base 63 //Convert actor number to base 63
string classNumber = Utils.ToStringBase63(actorNumber); string classNumber = Utils.ToStringBase63(actorNumber);
@ -349,6 +349,44 @@ namespace FFXIVClassic_Map_Server.Actors
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel); actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
} }
public List<float> GetPos()
{
List<float> pos = new List<float>();
pos.Add(positionX);
pos.Add(positionY);
pos.Add(positionZ);
pos.Add(rotation);
pos.Add(zoneId);
return pos;
}
public void SetPos(float x, float y, float z, float rot = 0, uint zoneId = 0)
{
oldPositionX = positionX;
oldPositionY = positionY;
oldPositionZ = positionZ;
oldRotation = rotation;
positionX = x;
positionY = y;
positionZ = z;
rotation = rot;
// todo: handle zone?
zone.BroadcastPacketAroundActor(this, MoveActorToPositionPacket.BuildPacket(this.actorId, this.actorId, x, y, z, rot, moveState));
}
public Area GetZone()
{
return zone;
}
public uint GetZoneID()
{
return zoneId;
}
} }
} }

View file

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFXIVClassic_Map_Server.packets.send;
namespace FFXIVClassic_Map_Server.Actors namespace FFXIVClassic_Map_Server.Actors
{ {
@ -35,7 +36,7 @@ namespace FFXIVClassic_Map_Server.Actors
protected Dictionary<uint, Actor> mActorList = new Dictionary<uint, Actor>(); protected Dictionary<uint, Actor> mActorList = new Dictionary<uint, Actor>();
protected List<Actor>[,] mActorBlock; protected List<Actor>[,] mActorBlock;
Script areaScript; LuaScript areaScript;
public Area(uint id, string zoneName, ushort regionId, string className, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid) public Area(uint id, string zoneName, ushort regionId, string className, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
: base(id) : base(id)
@ -292,7 +293,7 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
if (a is Player) if (a is Player)
{ {
if (((Player)a).customDisplayName.Equals(name)) if (((Player)a).customDisplayName.ToLower().Equals(name.ToLower()))
return (Player)a; return (Player)a;
} }
} }
@ -352,5 +353,25 @@ namespace FFXIVClassic_Map_Server.Actors
AddActorToZone(npc); AddActorToZone(npc);
} }
public void ChangeWeather(ushort weather, ushort transitionTime, Player player, bool zoneWide = false)
{
weatherNormal = weather;
if (player != null && !zoneWide)
{
player.QueuePacket(BasePacket.CreatePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime), true, false));
}
if (zoneWide)
{
foreach (var actor in mActorList)
{
if (actor.Value is Player)
{
player = ((Player)actor.Value);
player.QueuePacket(BasePacket.CreatePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime), true, false));
}
}
}
}
} }
} }

View file

@ -301,7 +301,7 @@ namespace FFXIVClassic_Map_Server.Actors
public List<LuaParam> DoActorInit(Player player) public List<LuaParam> DoActorInit(Player player)
{ {
Script parent = null, child = null; LuaScript parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua")) if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua"); parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua");
@ -329,7 +329,7 @@ namespace FFXIVClassic_Map_Server.Actors
public Coroutine GetEventStartCoroutine(Player player) public Coroutine GetEventStartCoroutine(Player player)
{ {
Script parent = null, child = null; LuaScript parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua")) if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua"); parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua");
@ -357,7 +357,7 @@ namespace FFXIVClassic_Map_Server.Actors
public void DoEventUpdate(Player player, EventUpdatePacket eventUpdate) public void DoEventUpdate(Player player, EventUpdatePacket eventUpdate)
{ {
Script parent = null, child = null; LuaScript parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua")) if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua"); parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua");
@ -391,7 +391,7 @@ namespace FFXIVClassic_Map_Server.Actors
internal void DoOnActorSpawn(Player player) internal void DoOnActorSpawn(Player player)
{ {
Script parent = null, child = null; LuaScript parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua")) if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua"); parent = LuaEngine.LoadScript("./scripts/base/" + classPath + ".lua");

View file

@ -87,15 +87,21 @@ namespace FFXIVClassic_Map_Server.actors.chara.player
AddItem(itemId, quantity, 1); AddItem(itemId, quantity, 1);
} }
public void AddItem(uint itemId, int quantity, byte quality) public bool AddItem(uint itemId, int quantity, byte quality)
{ {
if (!IsSpaceForAdd(itemId, quantity)) if (!IsSpaceForAdd(itemId, quantity))
return; return false;
Item gItem = Server.GetItemGamedata(itemId); Item gItem = Server.GetItemGamedata(itemId);
List<ushort> slotsToUpdate = new List<ushort>(); List<ushort> slotsToUpdate = new List<ushort>();
List<SubPacket> addItemPackets = new List<SubPacket>(); List<SubPacket> addItemPackets = new List<SubPacket>();
if (gItem == null)
{
Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId);
return false;
}
//Check if item id exists //Check if item id exists
int quantityCount = quantity; int quantityCount = quantity;
for (int i = 0; i < list.Count; i++) for (int i = 0; i < list.Count; i++)
@ -152,6 +158,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
return true;
} }
public void AddItem(uint[] itemId) public void AddItem(uint[] itemId)

View file

@ -600,6 +600,21 @@ namespace FFXIVClassic_Map_Server.Actors
playerSession.QueuePacket(subpacket, true, false); playerSession.QueuePacket(subpacket, true, false);
} }
public void SendPacket(string path)
{
try
{
BasePacket packet = new BasePacket(path);
packet.ReplaceActorID(actorId);
QueuePacket(packet);
}
catch (Exception e)
{
this.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "[SendPacket]", "Unable to send packet.");
}
}
public void BroadcastPacket(SubPacket packet, bool sendToSelf) public void BroadcastPacket(SubPacket packet, bool sendToSelf)
{ {
if (sendToSelf) if (sendToSelf)

View file

@ -1,5 +1,4 @@
using FFXIVClassic.Common; using FFXIVClassic_Map_Server.packets;
using FFXIVClassic_Map_Server.packets;
using FFXIVClassic_Map_Server.actors.director; using FFXIVClassic_Map_Server.actors.director;
using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.dataobjects;
@ -12,6 +11,8 @@ using MoonSharp.Interpreter.Loaders;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Diagnostics;
using FFXIVClassic_Map_Server.lua;
namespace FFXIVClassic_Map_Server.lua namespace FFXIVClassic_Map_Server.lua
{ {
@ -37,7 +38,7 @@ namespace FFXIVClassic_Map_Server.lua
luaPath = String.Format(FILEPATH_NPCS, target.zoneId, target.GetName()); luaPath = String.Format(FILEPATH_NPCS, target.zoneId, target.GetName());
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return null; return null;
@ -73,7 +74,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return null; return null;
@ -97,7 +98,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;
@ -132,7 +133,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;
@ -160,7 +161,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;
@ -175,7 +176,7 @@ namespace FFXIVClassic_Map_Server.lua
{ {
if (File.Exists(FILEPATH_PLAYER)) if (File.Exists(FILEPATH_PLAYER))
{ {
Script script = LoadScript(FILEPATH_PLAYER); LuaScript script = LoadScript(FILEPATH_PLAYER);
if (script == null) if (script == null)
return; return;
@ -190,7 +191,7 @@ namespace FFXIVClassic_Map_Server.lua
{ {
if (File.Exists(FILEPATH_PLAYER)) if (File.Exists(FILEPATH_PLAYER))
{ {
Script script = LoadScript(FILEPATH_PLAYER); LuaScript script = LoadScript(FILEPATH_PLAYER);
if (script == null) if (script == null)
return; return;
@ -201,24 +202,170 @@ namespace FFXIVClassic_Map_Server.lua
} }
} }
public static Script LoadScript(string filename) #region RunGMCommand
public static void RunGMCommand(Player player, String cmd, string[] param, bool help = false)
{ {
Script script = new Script(); // load from scripts/commands/gm/ directory
var path = String.Format("./scripts/commands/gm/{0}.lua", cmd.ToString().ToLower());
// check if the file exists
if (File.Exists(path))
{
// load global functions
LuaScript script = LoadGlobals();
// see if this script has any syntax errors
try
{
script.DoFile(path);
}
catch (Exception e)
{
Program.Log.Error("LuaEngine.RunGMCommand: {0}.", e.Message);
return;
}
// can we run this script
if (!script.Globals.Get("onTrigger").IsNil())
{
// can i run this command
var permissions = 0;
// parameter types (string, integer, double, float)
var parameters = "";
var description = "!" + cmd + ": ";
// get the properties table
var res = script.Globals.Get("properties");
// make sure properties table exists
if (!res.IsNil())
{
try
{
// returns table if one is found
var table = res.Table;
// find each key/value pair
foreach (var pair in table.Pairs)
{
if (pair.Key.String == "permissions")
{
permissions = (int)pair.Value.Number;
}
else if (pair.Key.String == "parameters")
{
parameters = pair.Value.String;
}
else if (pair.Key.String == "description")
{
description = pair.Value.String;
}
}
}
catch (Exception e) { LuaScript.Log.Error("LuaEngine.RunGMCommand: " + e.Message); return; }
}
// if this isnt a console command, make sure player exists
if (player != null)
{
if (permissions > 0 && !player.isGM)
{
Program.Log.Info("LuaEngine.RunGMCommand: {0}'s GM level is too low to use command {1}.", player.actorName, cmd);
return;
}
// i hate to do this, but cant think of a better way to keep !help
else if (help)
{
player.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, String.Format("[Commands] [{0}]", cmd), description);
return;
}
}
else if (help)
{
LuaScript.Log.Info("[Commands] [{0}]: {1}", cmd, description);
return;
}
// we'll push our lua params here
List<object> LuaParam = new List<object>();
var i = 0;
for (; i < parameters.Length; ++i)
{
try
{
// convert chat parameters to command parameters
switch (parameters[i])
{
case 'i':
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(int)));
continue;
case 'd':
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(double)));
continue;
case 'f':
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(float)));
continue;
case 's':
LuaParam.Add(param[i + 1]);
continue;
default:
LuaScript.Log.Info("LuaEngine.RunGMCommand: {0} unknown parameter {1}.", path, parameters[i]);
LuaParam.Add(param[i + 1]);
continue;
}
}
catch (Exception e)
{
if (e is IndexOutOfRangeException) break;
LuaParam.Add(param[i + 1]);
}
}
// the script can double check the player exists, we'll push them anyways
LuaParam.Insert(0, player);
// push the arg count too
LuaParam.Insert(1, i);
// run the script
script.Call(script.Globals["onTrigger"], LuaParam.ToArray());
return;
}
}
LuaScript.Log.Error("LuaEngine.RunGMCommand: Unable to find script {0}", path);
return;
}
#endregion
public static LuaScript LoadScript(string filename)
{
LuaScript script = LoadGlobals();
try
{
script.DoFile(filename);
}
catch (SyntaxErrorException e)
{
Program.Log.Error("LUAERROR: {0}.", e.DecoratedMessage);
return null;
}
return script;
}
public static LuaScript LoadGlobals(LuaScript script = null)
{
script = script ?? new LuaScript();
// register and load all global functions here
((FileSystemScriptLoader)script.Options.ScriptLoader).ModulePaths = FileSystemScriptLoader.UnpackStringPaths("./scripts/?;./scripts/?.lua"); ((FileSystemScriptLoader)script.Options.ScriptLoader).ModulePaths = FileSystemScriptLoader.UnpackStringPaths("./scripts/?;./scripts/?.lua");
script.Globals["GetWorldManager"] = (Func<WorldManager>)Server.GetWorldManager; script.Globals["GetWorldManager"] = (Func<WorldManager>)Server.GetWorldManager;
script.Globals["GetStaticActor"] = (Func<string, Actor>)Server.GetStaticActors; script.Globals["GetStaticActor"] = (Func<string, Actor>)Server.GetStaticActors;
script.Globals["GetWorldMaster"] = (Func<Actor>)Server.GetWorldManager().GetActor; script.Globals["GetWorldMaster"] = (Func<Actor>)Server.GetWorldManager().GetActor;
script.Globals["GetItemGamedata"] = (Func<uint, Item>)Server.GetItemGamedata; script.Globals["GetItemGamedata"] = (Func<uint, Item>)Server.GetItemGamedata;
try script.Options.DebugPrint = s => { Program.Log.Debug(s); };
{
script.DoFile(filename);
}
catch(SyntaxErrorException e)
{
Program.Log.Error("LUAERROR: {0}.", e.DecoratedMessage);
return null;
}
return script; return script;
} }
@ -237,7 +384,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;
@ -258,7 +405,7 @@ namespace FFXIVClassic_Map_Server.lua
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;

View file

@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MoonSharp;
using MoonSharp.Interpreter;
using NLog;
using MoonSharp.Interpreter.Debugging;
using System.IO;
namespace FFXIVClassic_Map_Server.lua
{
class LuaScript : Script
{
public static Logger Log = LogManager.GetCurrentClassLogger();
public LuaScript()
{
this.Options.DebugPrint = s => { Log.Debug(s); };
}
public new static DynValue RunFile(string filename)
{
try
{
return Script.RunFile(filename);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new void AttachDebugger(IDebugger debugger)
{
try
{
((Script)this).AttachDebugger(debugger);
}
catch (Exception e)
{
Log.Debug(e.Message);
}
}
public new DynValue Call(DynValue function)
{
try
{
return ((Script)this).Call(function);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue Call(object function)
{
try
{
return ((Script)this).Call(function);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue Call(object function, params object[] args)
{
try
{
return ((Script)this).Call(function, args);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue Call(DynValue function, params DynValue[] args)
{
try
{
return ((Script)this).Call(function, args);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue Call(DynValue function, params object[] args)
{
try
{
return ((Script)this).Call(function, args);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue CreateCoroutine(DynValue function)
{
try
{
return ((Script)this).CreateCoroutine(function);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue CreateCoroutine(object function)
{
try
{
return ((Script)this).CreateCoroutine(function);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue DoString(string code, Table globalContext = null)
{
try
{
return ((Script)this).DoString(code, globalContext);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new DynValue DoFile(string filename, Table globalContext = null)
{
try
{
return ((Script)this).DoFile(filename, globalContext);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new void Dump(DynValue function, Stream stream)
{
try
{
((Script)this).Dump(function, stream);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
}
}
public new DynValue RequireModule(string modname, Table globalContext = null)
{
try
{
return ((Script)this).RequireModule(modname, globalContext);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
return null;
}
}
public new void SetTypeMetatable(DataType type, Table metatable)
{
try
{
((Script)this).SetTypeMetatable(type, metatable);
}
catch (Exception e)
{
if (e is InterpreterException)
Log.Debug(((InterpreterException)e).DecoratedMessage);
else
Log.Debug(e.Message);
}
}
}
}

View file

@ -3,7 +3,7 @@
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" /> <package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
<package id="Dapper" version="1.42" targetFramework="net45" /> <package id="Dapper" version="1.42" targetFramework="net45" />
<package id="MoonSharp" version="1.2.1.0" targetFramework="net45" /> <package id="MoonSharp" version="1.2.1.0" targetFramework="net45" />
<package id="MySql.Data" version="6.9.7" targetFramework="net45" /> <package id="MySql.Data" version="6.9.8" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" /> <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="NLog" version="4.3.5" targetFramework="net45" /> <package id="NLog" version="4.3.5" targetFramework="net45" />
<package id="NLog.Config" version="4.3.5" targetFramework="net45" /> <package id="NLog.Config" version="4.3.5" targetFramework="net45" />

View file

@ -1,13 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using FFXIVClassic.Common; using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
namespace FFXIVClassic_Map_Server.packets namespace FFXIVClassic_Map_Server.packets
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct BasePacketHeader public struct BasePacketHeader
{ {
@ -19,26 +20,27 @@ namespace FFXIVClassic_Map_Server.packets
public ulong timestamp; //Miliseconds public ulong timestamp; //Miliseconds
} }
public class BasePacket{ public class BasePacket
{
public const int TYPE_ZONE = 1; public const int TYPE_ZONE = 1;
public const int TYPE_CHAT = 2; public const int TYPE_CHAT = 2;
public const int BASEPACKET_SIZE = 0x10; public const int BASEPACKET_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public BasePacketHeader header;
public byte[] data; public byte[] data;
public BasePacketHeader header;
//Loads a sniffed packet from a file //Loads a sniffed packet from a file
public unsafe BasePacket(String path) public unsafe BasePacket(string path)
{ {
byte[] bytes = File.ReadAllBytes(path); var bytes = File.ReadAllBytes(path);
if (bytes.Length < BASEPACKET_SIZE) if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small"); throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
if (bytes.Length < header.packetSize) if (bytes.Length < header.packetSize)
@ -63,7 +65,7 @@ namespace FFXIVClassic_Map_Server.packets
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
if (bytes.Length < header.packetSize) if (bytes.Length < header.packetSize)
@ -82,7 +84,7 @@ namespace FFXIVClassic_Map_Server.packets
fixed (byte* pdata = &bytes[offset]) fixed (byte* pdata = &bytes[offset])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
int packetSize = header.packetSize; int packetSize = header.packetSize;
@ -104,9 +106,9 @@ namespace FFXIVClassic_Map_Server.packets
public List<SubPacket> GetSubpackets() public List<SubPacket> GetSubpackets()
{ {
List<SubPacket> subpackets = new List<SubPacket>(header.numSubpackets); var subpackets = new List<SubPacket>(header.numSubpackets);
int offset = 0; var offset = 0;
while (offset < data.Length) while (offset < data.Length)
subpackets.Add(new SubPacket(data, ref offset)); subpackets.Add(new SubPacket(data, ref offset));
@ -114,7 +116,7 @@ namespace FFXIVClassic_Map_Server.packets
return subpackets; return subpackets;
} }
public unsafe static BasePacketHeader GetHeader(byte[] bytes) public static unsafe BasePacketHeader GetHeader(byte[] bytes)
{ {
BasePacketHeader header; BasePacketHeader header;
if (bytes.Length < BASEPACKET_SIZE) if (bytes.Length < BASEPACKET_SIZE)
@ -122,7 +124,7 @@ namespace FFXIVClassic_Map_Server.packets
fixed (byte* pdata = &bytes[0]) fixed (byte* pdata = &bytes[0])
{ {
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader)); header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
} }
return header; return header;
@ -130,10 +132,10 @@ namespace FFXIVClassic_Map_Server.packets
public byte[] GetHeaderBytes() public byte[] GetHeaderBytes()
{ {
int size = Marshal.SizeOf(header); var size = Marshal.SizeOf(header);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true); Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -142,7 +144,7 @@ namespace FFXIVClassic_Map_Server.packets
public byte[] GetPacketBytes() public byte[] GetPacketBytes()
{ {
byte[] outBytes = new byte[header.packetSize]; var outBytes = new byte[header.packetSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE); Array.Copy(GetHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE);
Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length); Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length);
return outBytes; return outBytes;
@ -151,16 +153,18 @@ namespace FFXIVClassic_Map_Server.packets
//Replaces all instances of the sniffed actorID with the given one //Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint actorID) public void ReplaceActorID(uint actorID)
{ {
using (MemoryStream mem = new MemoryStream(data)) using (var mem = new MemoryStream(data))
{ {
using (BinaryWriter binWriter = new BinaryWriter(mem)) using (var binWriter = new BinaryWriter(mem))
{ {
using (BinaryReader binreader = new BinaryReader(mem)) using (var binreader = new BinaryReader(mem))
{ {
while (binreader.BaseStream.Position + 4 < data.Length) while (binreader.BaseStream.Position + 4 < data.Length)
{ {
uint read = binreader.ReadUInt32(); var read = binreader.ReadUInt32();
if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 || read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb || read == 0x6c) //Original ID if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 ||
read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb || read == 0x6c)
//Original ID
{ {
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
binWriter.Write(actorID); binWriter.Write(actorID);
@ -174,15 +178,15 @@ namespace FFXIVClassic_Map_Server.packets
//Replaces all instances of the sniffed actorID with the given one //Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint fromActorID, uint actorID) public void ReplaceActorID(uint fromActorID, uint actorID)
{ {
using (MemoryStream mem = new MemoryStream(data)) using (var mem = new MemoryStream(data))
{ {
using (BinaryWriter binWriter = new BinaryWriter(mem)) using (var binWriter = new BinaryWriter(mem))
{ {
using (BinaryReader binreader = new BinaryReader(mem)) using (var binreader = new BinaryReader(mem))
{ {
while (binreader.BaseStream.Position + 4 < data.Length) while (binreader.BaseStream.Position + 4 < data.Length)
{ {
uint read = binreader.ReadUInt32(); var read = binreader.ReadUInt32();
if (read == fromActorID) //Original ID if (read == fromActorID) //Original ID
{ {
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin); binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
@ -194,49 +198,65 @@ namespace FFXIVClassic_Map_Server.packets
} }
} }
public void DebugPrintPacket()
{
#if DEBUG
logger.ColorDebug(
string.Format("IsAuth:{0} IsEncrypted:{1}, Size:0x{2:X}, NumSubpackets:{3}{4}{5}",
header.isAuthenticated, header.isCompressed, header.packetSize, header.numSubpackets,
Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkYellow);
foreach (var sub in GetSubpackets())
{
sub.DebugPrintSubPacket();
}
#endif
}
#region Utility Functions #region Utility Functions
public static BasePacket CreatePacket(List<SubPacket> subpackets, bool isAuthed, bool isCompressed) public static BasePacket CreatePacket(List<SubPacket> subpackets, bool isAuthed, bool isCompressed)
{ {
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
byte[] data = null; byte[] data = null;
header.isAuthenticated = isAuthed?(byte)1:(byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed?(byte)1:(byte)0; header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)subpackets.Count; header.numSubpackets = (ushort) subpackets.Count;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size //Get packet size
foreach (SubPacket subpacket in subpackets) foreach (var subpacket in subpackets)
header.packetSize += subpacket.header.subpacketSize; header.packetSize += subpacket.header.subpacketSize;
data = new byte[header.packetSize-0x10]; data = new byte[header.packetSize - 0x10];
//Add Subpackets //Add Subpackets
int offset = 0; var offset = 0;
foreach (SubPacket subpacket in subpackets) foreach (var subpacket in subpackets)
{ {
byte[] subpacketData = subpacket.GetBytes(); var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, offset, subpacketData.Length); Array.Copy(subpacketData, 0, data, offset, subpacketData.Length);
offset += (ushort)subpacketData.Length; offset += (ushort) subpacketData.Length;
} }
Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset); Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset);
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static BasePacket CreatePacket(SubPacket subpacket, bool isAuthed, bool isCompressed) public static BasePacket CreatePacket(SubPacket subpacket, bool isAuthed, bool isCompressed)
{ {
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
byte[] data = null; byte[] data = null;
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed ? (byte)1 : (byte)0; header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)1; header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
@ -246,68 +266,41 @@ namespace FFXIVClassic_Map_Server.packets
data = new byte[header.packetSize - 0x10]; data = new byte[header.packetSize - 0x10];
//Add Subpackets //Add Subpackets
byte[] subpacketData = subpacket.GetBytes(); var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, 0, subpacketData.Length); Array.Copy(subpacketData, 0, data, 0, subpacketData.Length);
Debug.Assert(data != null); Debug.Assert(data != null);
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static BasePacket CreatePacket(byte[] data, bool isAuthed, bool isCompressed) public static BasePacket CreatePacket(byte[] data, bool isAuthed, bool isCompressed)
{ {
Debug.Assert(data != null); Debug.Assert(data != null);
//Create Header //Create Header
BasePacketHeader header = new BasePacketHeader(); var header = new BasePacketHeader();
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0; header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed ? (byte)1 : (byte)0; header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort)1; header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE; header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC(); header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size //Get packet size
header.packetSize += (ushort)data.Length; header.packetSize += (ushort) data.Length;
BasePacket packet = new BasePacket(header, data); var packet = new BasePacket(header, data);
return packet; return packet;
} }
public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet) public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet)
{ {
byte[] data = packet.data; var data = packet.data;
int size = packet.header.packetSize; int size = packet.header.packetSize;
int offset = 0; var offset = 0;
while (offset < data.Length) {
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Encipher(data, offset + 0x10, header.subpacketSize-0x10);
offset += header.subpacketSize;
}
}
public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet)
{
byte[] data = packet.data;
int size = packet.header.packetSize;
int offset = 0;
while (offset < data.Length) while (offset < data.Length)
{ {
if (data.Length < offset + SubPacket.SUBPACKET_SIZE) if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
@ -316,35 +309,54 @@ namespace FFXIVClassic_Map_Server.packets
SubPacketHeader header; SubPacketHeader header;
fixed (byte* pdata = &data[offset]) fixed (byte* pdata = &data[offset])
{ {
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
} }
if (data.Length < offset + header.subpacketSize) if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data"); throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Decipher(data, offset + 0x10, header.subpacketSize-0x10); blowfish.Encipher(data, offset + 0x10, header.subpacketSize - 0x10);
offset += header.subpacketSize; offset += header.subpacketSize;
} }
} }
public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet)
{
var data = packet.data;
int size = packet.header.packetSize;
var offset = 0;
while (offset < data.Length)
{
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Decipher(data, offset + 0x10, header.subpacketSize - 0x10);
offset += header.subpacketSize;
}
}
#endregion #endregion
public void DebugPrintPacket()
{
#if DEBUG
// todo: create new target for colourful packet logging
//Console.BackgroundColor = ConsoleColor.DarkYellow;
Program.Log.Debug("IsAuth: {0} IsEncrypted: {1}, Size: 0x{2:X}, NumSubpackets: {3}{4}{5}", header.isAuthenticated, header.isCompressed, header.packetSize, header.numSubpackets, Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes()));
foreach (SubPacket sub in GetSubpackets())
{
sub.DebugPrintSubPacket();
} }
//Console.BackgroundColor = ConsoleColor.Black; public static class LoggerExtensions
#endif {
public static void ColorDebug(this Logger logger, string message, ConsoleOutputColor color)
{
var logEvent = new LogEventInfo(LogLevel.Debug, logger.Name, message);
logEvent.Properties["color"] = (int) color;
logger.Log(logEvent);
} }
} }
} }

View file

@ -1,6 +1,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FFXIVClassic.Common; using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
namespace FFXIVClassic_Map_Server.packets namespace FFXIVClassic_Map_Server.packets
{ {
@ -28,10 +30,11 @@ namespace FFXIVClassic_Map_Server.packets
{ {
public const int SUBPACKET_SIZE = 0x10; public const int SUBPACKET_SIZE = 0x10;
public const int GAMEMESSAGE_SIZE = 0x10; public const int GAMEMESSAGE_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public byte[] data;
public GameMessageHeader gameMessage;
public SubPacketHeader header; public SubPacketHeader header;
public GameMessageHeader gameMessage;
public byte[] data;
public unsafe SubPacket(byte[] bytes, ref int offset) public unsafe SubPacket(byte[] bytes, ref int offset)
{ {
@ -40,14 +43,15 @@ namespace FFXIVClassic_Map_Server.packets
fixed (byte* pdata = &bytes[offset]) fixed (byte* pdata = &bytes[offset])
{ {
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader)); header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
} }
if (header.type == 0x3) if (header.type == 0x3)
{ {
fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE]) fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE])
{ {
gameMessage = (GameMessageHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader)); gameMessage =
(GameMessageHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader));
} }
} }
@ -70,8 +74,8 @@ namespace FFXIVClassic_Map_Server.packets
public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data) public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data)
{ {
this.header = new SubPacketHeader(); header = new SubPacketHeader();
this.gameMessage = new GameMessageHeader(); gameMessage = new GameMessageHeader();
gameMessage.opcode = opcode; gameMessage.opcode = opcode;
header.sourceId = sourceId; header.sourceId = sourceId;
@ -87,13 +91,13 @@ namespace FFXIVClassic_Map_Server.packets
this.data = data; this.data = data;
header.subpacketSize = (ushort)(SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length); header.subpacketSize = (ushort) (SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length);
} }
public SubPacket(SubPacket original, uint newTargetId) public SubPacket(SubPacket original, uint newTargetId)
{ {
this.header = new SubPacketHeader(); header = new SubPacketHeader();
this.gameMessage = original.gameMessage; gameMessage = original.gameMessage;
header.subpacketSize = original.header.subpacketSize; header.subpacketSize = original.header.subpacketSize;
header.type = original.header.type; header.type = original.header.type;
header.sourceId = original.header.sourceId; header.sourceId = original.header.sourceId;
@ -103,10 +107,10 @@ namespace FFXIVClassic_Map_Server.packets
public byte[] GetHeaderBytes() public byte[] GetHeaderBytes()
{ {
int size = Marshal.SizeOf(header); var size = Marshal.SizeOf(header);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true); Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -115,10 +119,10 @@ namespace FFXIVClassic_Map_Server.packets
public byte[] GetGameMessageBytes() public byte[] GetGameMessageBytes()
{ {
int size = Marshal.SizeOf(gameMessage); var size = Marshal.SizeOf(gameMessage);
byte[] arr = new byte[size]; var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(gameMessage, ptr, true); Marshal.StructureToPtr(gameMessage, ptr, true);
Marshal.Copy(ptr, arr, 0, size); Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
@ -127,7 +131,7 @@ namespace FFXIVClassic_Map_Server.packets
public byte[] GetBytes() public byte[] GetBytes()
{ {
byte[] outBytes = new byte[header.subpacketSize]; var outBytes = new byte[header.subpacketSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE); Array.Copy(GetHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE);
if (header.type == 0x3) if (header.type == 0x3)
@ -140,23 +144,20 @@ namespace FFXIVClassic_Map_Server.packets
public void DebugPrintSubPacket() public void DebugPrintSubPacket()
{ {
#if DEBUG #if DEBUG
// todo: create new target for colourful packet logging logger.ColorDebug(
//Console.BackgroundColor = ConsoleColor.DarkRed; string.Format("Size:0x{0:X} Opcode:0x{1:X}{2}{3}", header.subpacketSize, gameMessage.opcode,
Environment.NewLine,
Program.Log.Debug("Size: 0x{0:X}{1}{2}", header.subpacketSize, Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes())); Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkRed);
if (header.type == 0x03) if (header.type == 0x03)
{ {
Program.Log.Debug("Opcode: 0x{0:X}{1}{2}", gameMessage.opcode, Environment.NewLine, Utils.ByteArrayToHex(GetGameMessageBytes(), SUBPACKET_SIZE)); logger.ColorDebug(Utils.ByteArrayToHex(GetGameMessageBytes(), SUBPACKET_SIZE),
ConsoleOutputColor.DarkRed);
//Console.BackgroundColor = ConsoleColor.DarkMagenta; logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE),
ConsoleOutputColor.DarkMagenta);
Program.Log.Debug("Data: {0}{1}", Environment.NewLine, Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE));
} }
//Console.BackgroundColor = ConsoleColor.Black;
#endif #endif
} }
} }
} }

View file

@ -5,7 +5,7 @@ namespace FFXIVClassic_Map_Server.packets.send.login
{ {
class Login0x7ResponsePacket class Login0x7ResponsePacket
{ {
public static BasePacket BuildPacket(uint actorID, uint time) public static BasePacket BuildPacket(uint actorID, uint time, uint type)
{ {
byte[] data = new byte[0x18]; byte[] data = new byte[0x18];
@ -16,7 +16,7 @@ namespace FFXIVClassic_Map_Server.packets.send.login
try try
{ {
binWriter.Write((short)0x18); binWriter.Write((short)0x18);
binWriter.Write((short)0x8); binWriter.Write((short)type);
binWriter.Write((uint)0); binWriter.Write((uint)0);
binWriter.Write((uint)0); binWriter.Write((uint)0);
binWriter.Write((uint)0xFFFFFD7F); binWriter.Write((uint)0xFFFFFD7F);

View file

@ -1,5 +1,5 @@
[General] [General]
server_ip=127.0.0.1 server_ip=192.168.0.2
showtimestamp = true showtimestamp = true
[Database] [Database]
@ -8,4 +8,4 @@ host=127.0.0.1
port=3306 port=3306
database=ffxiv_server database=ffxiv_server
username=root username=root
password= password=root

View file

@ -1,5 +1,5 @@
[General] [General]
server_ip=127.0.0.1 server_ip=192.168.0.2
showtimestamp = true showtimestamp = true
[Database] [Database]
@ -8,4 +8,4 @@ host=127.0.0.1
port=3306 port=3306
database=ffxiv_server database=ffxiv_server
username=root username=root
password= password=root

View file

@ -0,0 +1,37 @@
require("global");
properties = {
permissions = 0,
parameters = "sssss",
description = "removes <currency> <qty> from <target>, currency is removed from user if <target> is nil",
}
function onTrigger(player, argc, currency, qty, location, name, lastName)
local sender = "[delcurrency] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
currency = tonumber(currency) or nil;
qty = 1;
location = INVENTORY_CURRENCY;
local removed = player:GetInventory(location):removecurrency(currency, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to remove currency";
if currency and removed then
message = string.format("removed currency %u from %s", currency, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to remove currency, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,37 @@
require("global");
properties = {
permissions = 0,
parameters = "sssss",
description = "removes <item> <qty> from <location> for <target>. <qty> and <location> are optional, item is removed from user if <target> is nil",
}
function onTrigger(player, argc, item, qty, location, name, lastName)
local sender = "[delitem] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
item = tonumber(item) or nil;
qty = tonumber(qty) or 1;
location = tonumber(itemtype) or INVENTORY_NORMAL;
local removed = player:GetInventory(location):removeItem(item, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to remove item";
if item and removed then
message = string.format("removed item %u from %s", item, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to remove item, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,37 @@
require("global");
properties = {
permissions = 0,
parameters = "ssss",
description = "removes <keyitem> <qty> from <target>, keyitem is removed from user if <target> is nil",
}
function onTrigger(player, argc, keyitem, qty, name, lastName)
local sender = "[delkeyitem] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
keyitem = tonumber(keyitem) or nil;
qty = 1;
location = INVENTORY_KEYITEMS;
local removed = player:GetInventory(location):removeItem(item, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to remove keyitem";
if keyitem and removed then
message = string.format("removed keyitem %u from %s", keyitem, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to remove keyitem, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,37 @@
require("global");
properties = {
permissions = 0,
parameters = "sss",
description = "adds <currency> to self or <target>.",
}
function onTrigger(player, argc, currency, name, lastName)
local sender = "[givecurrency] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
currency = tonumber(currency) or nil;
qty = 1;
location = INVENTORY_CURRENCY;
local added = player:GetInventory(location):AddItem(currency, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to add currency";
if currency and added then
message = string.format("added currency %u to %s", currency, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to add currency, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,36 @@
require("global");
properties = {
permissions = 0,
parameters = "sssss",
description = "adds <item> <qty> to <location> for <target>. <qty> and <location> are optional, item is added to user if <target> is nil",
}
function onTrigger(player, argc, item, qty, location, name, lastName)
local sender = "[giveitem] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
item = tonumber(item) or nil;
qty = tonumber(qty) or 1;
location = tonumber(itemtype) or INVENTORY_NORMAL;
local added = player:GetInventory(location):AddItem(item, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to add item";
if item and added then
message = string.format("added item %u to %s", item, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to add item, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,37 @@
require("global");
properties = {
permissions = 0,
parameters = "sss",
description = "adds <keyitem> to self or <target>.",
}
function onTrigger(player, argc, keyitem, name, lastName)
local sender = "[givekeyitem] ";
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if player then
keyitem = tonumber(keyitem) or nil;
qty = 1;
location = INVENTORY_KEYITEMS;
local added = player:GetInventory(location):AddItem(keyitem, qty);
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local message = "unable to add keyitem";
if keyitem and added then
message = string.format("added keyitem %u to %s", keyitem, player:GetName());
end
player:SendMessage(messageID, sender, message);
print(message);
else
print(sender.."unable to add keyitem, ensure player name is valid.");
end;
end;

View file

@ -0,0 +1,18 @@
properties = {
permissions = 0,
parameters = "sssss",
description = "changes appearance for equipment in <slot>. Parameters: <slot> <wId> <eId> <vId> <cId>, (idk what any of those mean either)",
}
function onTrigger(player, argc, slot, wId, eId, vId, cId)
slot = tonumber(slot) or 0;
wId = tonumber(wId) or 0;
eId = tonumber(eId) or 0;
vId = tonumber(vId) or 0;
cId = tonumber(cId) or 0;
if player then
player:GraphicChange(slot, wId, eId, vId, cId);
player:SendAppearance();
end;
end;

View file

@ -0,0 +1,10 @@
properties = {
permissions = 0,
parameters = "s",
description = "plays music <id> to player",
}
function onTrigger(player, argc, music)
music = tonumber(music) or 0;
player:ChangeMusic(music);
end;

View file

@ -0,0 +1,22 @@
require("global");
properties = {
permissions = 0,
parameters = "",
description = "prints your current in-game position (different to map coords)",
}
function onTrigger(player)
local pos = player:GetPos();
local x = pos[0];
local y = pos[1];
local z = pos[2];
local rot = pos[3];
local zone = pos[4];
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local sender = "[mypos] ";
local message = string.format("current position X:%d Y:%d Z:%d (Rotation: %d) Zone:%d", x, y, z, rot, zone);
player:SendMessage(messageID, sender, message);
end;

View file

@ -0,0 +1,32 @@
require("global");
properties = {
permissions = 0,
parameters = "s",
description = "reloads <zone>",
}
function onTrigger(player, argc, zone)
if not player and not zone or tonumber(zone) == 0 then
printf("No valid zone specified!");
return;
end;
local sender = "[reloadzones] ";
zone = tonumber(zone);
if player then
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
zone = zone or player:GetZoneID();
player:SendMessage(messageID, "[reloadzones] ", string.format("Reloading zone: %u", zone));
--[[ todo: get this working legit
player:GetZone():Clear();
player:GetZone():AddActorToZone(player);
player:SendInstanceUpdate();
]]
end;
GetWorldManager():ReloadZone(zone);
printf("%s reloaded zone %u", sender, zone);
end;

View file

@ -0,0 +1,24 @@
properties = {
permissions = 0,
parameters = "ssss",
description = "<name> <target name>",
}
function onTrigger(player, argc, path, name, lastName)
local sender = "[sendpacket ]";
lastName = lastName or "";
path = "./packets/"..path;
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
value = tonumber(value) or 0;
if player and argc > 0 then
player:SendPacket(path)
end;
end;

View file

@ -0,0 +1,13 @@
properties = {
permissions = 0,
parameters = "sss",
description = "<stop> <walk> <run> speed",
}
function onTrigger(player, argc, stop, walk, run)
stop = tonumber(stop) or 0;
walk = tonumber(walk) or 2;
run = tonumber(run) or 5;
player:ChangeSpeed(stop, walk, run);
end;

View file

@ -0,0 +1,79 @@
require("global");
properties = {
permissions = 0,
parameters = "sssssss",
description =
[[
<zone> |
<zone> <x> <y> <z> |
<x> <y> <z> <zone> <privateArea> <target name>.
]],
}
function onTrigger(player, argc, p1, p2, p3, p4, privateArea, name, lastName)
if name then
if lastName then
player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil;
else
player = GetWorldManager():GetPCInWorld(name) or nil;
end;
end;
if not player then
printf("[Command] [warp] error! No target or player specified!");
return;
end;
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local sender = "[warp] ";
-- we're getting a list/array from c# so 0 index
local pos = player:GetPos();
local player_x = pos[0];
local player_y = pos[1];
local player_z = pos[2];
local player_rot = pos[3];
local player_zone = pos[4];
local worldManager = GetWorldManager();
-- treat this as a predefined warp list
if argc == 1 then
zone = tonumber(p1) or player_zone;
player:SendMessage(messageID, sender, string.format("warping to zone:%u", zone));
worldManager:DoZoneChange(player, zone);
elseif argc >= 3 then
if argc == 3 then
local x = tonumber(applyPositionOffset(p1, player_x)) or player_x;
local y = tonumber(applyPositionOffset(p2, player_y)) or player_y;
local z = tonumber(applyPositionOffset(p3, player_z)) or player_z;
player:SendMessage(messageID, sender, string.format("setting coordinates X:%d Y:%d Z:%d within current zone (%d)", x, y, z, player_zone));
worldManager:DoPlayerMoveInZone(player, x, y, z, 0x0F);
else
local zone = tonumber(applyPositionOffset(p1, player_zone)) or player_zone;
local x = tonumber(applyPositionOffset(p2, player_x)) or player_x;
local y = tonumber(applyPositionOffset(p3, player_y)) or player_y;
local z = tonumber(applyPositionOffset(p4, player_z)) or player_z;
if privateArea == "" then privateArea = nil end;
player:SendMessage(messageID, sender, string.format("setting coordinates X:%d Y:%d Z:%d to new zone (%d) private area:%s", x, y, z, zone, privateArea or "unspecified"));
worldManager:DoZoneChange(player, zone, privateArea, 0x02, x, y, z, 0.00);
end
else
player:SendMessage(messageID, sender, "Unknown parameters! Usage: "..properties.description);
end;
end;
function applyPositionOffset(str, offset)
local s = str;
print(s);
if s:find("@") then
s = tonumber(s:sub(s:find("@") + 1, s:len())) + offset;
end
return s;
end;

View file

@ -0,0 +1,30 @@
require("global");
properties = {
permissions = 0,
parameters = "ssss",
description = "usage: <id> <updateTime> <zonewide>.",
}
function onTrigger(player, argc, weather, updateTime, zonewide)
-- todo: change weather
local messageID = MESSAGE_TYPE_SYSTEM_ERROR;
local sender = "[weather] ";
local message = "unable to change weather";
if player then
weather = tonumber(weather) or 0;
updateTime = tonumber(updateTime) or 0;
zonewide = tonumber(zonewide) or 0;
message = "changed weather to %u ";
if zonewide ~= 0 then
message = string.format(message.."for zone %u", player:GetZoneID());
else
message = string.format(message.."%s", player:GetName());
end;
-- weatherid, updateTime
player:GetZone():ChangeWeather(weather, updateTime, player, zonewide ~= 0);
player:SendMessage(messageID, sender, message);
end;
print(sender..message);
end;

View file

@ -4,7 +4,7 @@ Globals referenced in all of the lua scripts
--]] --]]
--ACTOR STATES -- ACTOR STATES
ACTORSTATE_PASSIVE = 0; ACTORSTATE_PASSIVE = 0;
ACTORSTATE_DEAD1 = 1; ACTORSTATE_DEAD1 = 1;
@ -14,6 +14,43 @@ ACTORSTATE_SITTING_ONOBJ = 11;
ACTORSTATE_SITTING_ONFLOOR = 13; ACTORSTATE_SITTING_ONFLOOR = 13;
ACTORSTATE_MOUNTED = 15; ACTORSTATE_MOUNTED = 15;
-- MESSAGE
MESSAGE_TYPE_NONE = 0;
MESSAGE_TYPE_SAY = 1;
MESSAGE_TYPE_SHOUT = 2;
MESSAGE_TYPE_TELL = 3;
MESSAGE_TYPE_PARTY = 4;
MESSAGE_TYPE_LINKSHELL1 = 5;
MESSAGE_TYPE_LINKSHELL2 = 6;
MESSAGE_TYPE_LINKSHELL3 = 7;
MESSAGE_TYPE_LINKSHELL4 = 8;
MESSAGE_TYPE_LINKSHELL5 = 9;
MESSAGE_TYPE_LINKSHELL6 = 10;
MESSAGE_TYPE_LINKSHELL7 = 11;
MESSAGE_TYPE_LINKSHELL8 = 12;
MESSAGE_TYPE_SAY_SPAM = 22;
MESSAGE_TYPE_SHOUT_SPAM = 23;
MESSAGE_TYPE_TELL_SPAM = 24;
MESSAGE_TYPE_CUSTOM_EMOTE = 25;
MESSAGE_TYPE_EMOTE_SPAM = 26;
MESSAGE_TYPE_STANDARD_EMOTE = 27;
MESSAGE_TYPE_URGENT_MESSAGE = 28;
MESSAGE_TYPE_GENERAL_INFO = 29;
MESSAGE_TYPE_SYSTEM = 32;
MESSAGE_TYPE_SYSTEM_ERROR = 33;
-- INVENTORY
INVENTORY_NORMAL = 0x0000; --Max 0xC8
INVENTORY_LOOT = 0x0004; --Max 0xA
INVENTORY_MELDREQUEST = 0x0005; --Max 0x04
INVENTORY_BAZAAR = 0x0007; --Max 0x0A
INVENTORY_CURRENCY = 0x0063; --Max 0x140
INVENTORY_KEYITEMS = 0x0064; --Max 0x500
INVENTORY_EQUIPMENT = 0x00FE; --Max 0x23
INVENTORY_EQUIPMENT_OTHERPLAYER = 0x00F9; --Max 0x23
--UTILS --UTILS
function callClientFunction(player, functionName, ...) function callClientFunction(player, functionName, ...)
@ -21,3 +58,11 @@ function callClientFunction(player, functionName, ...)
result = coroutine.yield(); result = coroutine.yield();
return result; return result;
end end
function printf(s, ...)
if ... then
print(s:format(...));
else
print(s);
end;
end;