1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-22 12:47:46 +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

@ -1,233 +1,234 @@
using System; using System;
using System.Text; using System.Text;
namespace FFXIVClassic.Common namespace FFXIVClassic.Common
{ {
public static class Utils public static class Utils
{ {
private static readonly uint[] _lookup32 = CreateLookup32(); private static readonly uint[] _lookup32 = CreateLookup32();
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;
} }
public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16) public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16)
{ {
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; var line = (new string(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
var numLines = (bytes.Length + bytesPerLine - 1)/bytesPerLine;
char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
int numLines = (bytes.Length + bytesPerLine - 1) / bytesPerLine; var sb = new StringBuilder(numLines*lineLength);
StringBuilder sb = new StringBuilder(numLines * lineLength); for (var i = 0; i < bytes.Length; i += bytesPerLine)
{
for (int i = 0; i < bytes.Length; i += bytesPerLine) var h = i + offset;
{
int h = i + offset; line[0] = hexChars[(h >> 28) & 0xF];
line[1] = hexChars[(h >> 24) & 0xF];
line[0] = hexChars[(h >> 28) & 0xF]; line[2] = hexChars[(h >> 20) & 0xF];
line[1] = hexChars[(h >> 24) & 0xF]; line[3] = hexChars[(h >> 16) & 0xF];
line[2] = hexChars[(h >> 20) & 0xF]; line[4] = hexChars[(h >> 12) & 0xF];
line[3] = hexChars[(h >> 16) & 0xF]; line[5] = hexChars[(h >> 8) & 0xF];
line[4] = hexChars[(h >> 12) & 0xF]; line[6] = hexChars[(h >> 4) & 0xF];
line[5] = hexChars[(h >> 8) & 0xF]; line[7] = hexChars[(h >> 0) & 0xF];
line[6] = hexChars[(h >> 4) & 0xF];
line[7] = hexChars[(h >> 0) & 0xF]; var hexColumn = offsetBlock;
var charColumn = byteBlock;
int hexColumn = offsetBlock;
int charColumn = byteBlock; for (var j = 0; j < bytesPerLine; j++)
{
for (int j = 0; j < bytesPerLine; j++) if (j > 0 && (j & 7) == 0)
{ {
if (j > 0 && (j & 7) == 0) hexColumn++;
{ }
hexColumn++;
} if (i + j >= bytes.Length)
{
if (i + j >= bytes.Length) line[hexColumn] = ' ';
{ line[hexColumn + 1] = ' ';
line[hexColumn] = ' '; line[charColumn] = ' ';
line[hexColumn + 1] = ' '; }
line[charColumn] = ' '; else
} {
else var by = bytes[i + j];
{ line[hexColumn] = hexChars[(by >> 4) & 0xF];
byte by = bytes[i + j]; line[hexColumn + 1] = hexChars[by & 0xF];
line[hexColumn] = hexChars[(by >> 4) & 0xF]; line[charColumn] = by < 32 ? '.' : (char) by;
line[hexColumn + 1] = hexChars[by & 0xF]; }
line[charColumn] = (by < 32 ? '.' : (char)by);
} hexColumn += 3;
charColumn++;
hexColumn += 3; }
charColumn++;
} sb.Append(line);
}
sb.Append(line);
} return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
}
return sb.ToString();
} public static uint UnixTimeStampUTC()
{
public static UInt32 UnixTimeStampUTC() uint unixTimeStamp;
{ var currentTime = DateTime.Now;
UInt32 unixTimeStamp; var zuluTime = currentTime.ToUniversalTime();
DateTime currentTime = DateTime.Now; var unixEpoch = new DateTime(1970, 1, 1);
DateTime zuluTime = currentTime.ToUniversalTime(); unixTimeStamp = (uint) zuluTime.Subtract(unixEpoch).TotalSeconds;
DateTime unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (UInt32)(zuluTime.Subtract(unixEpoch)).TotalSeconds; return unixTimeStamp;
}
return unixTimeStamp;
} public static ulong MilisUnixTimeStampUTC()
{
public static UInt64 MilisUnixTimeStampUTC() ulong unixTimeStamp;
{ var currentTime = DateTime.Now;
UInt64 unixTimeStamp; var zuluTime = currentTime.ToUniversalTime();
DateTime currentTime = DateTime.Now; var unixEpoch = new DateTime(1970, 1, 1);
DateTime zuluTime = currentTime.ToUniversalTime(); unixTimeStamp = (ulong) zuluTime.Subtract(unixEpoch).TotalMilliseconds;
DateTime unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (UInt64)(zuluTime.Subtract(unixEpoch)).TotalMilliseconds; return unixTimeStamp;
}
return unixTimeStamp;
} public static ulong SwapEndian(ulong input)
{
public static ulong SwapEndian(ulong input) return 0x00000000000000FF & (input >> 56) |
{ 0x000000000000FF00 & (input >> 40) |
return ((0x00000000000000FF) & (input >> 56) | 0x0000000000FF0000 & (input >> 24) |
(0x000000000000FF00) & (input >> 40) | 0x00000000FF000000 & (input >> 8) |
(0x0000000000FF0000) & (input >> 24) | 0x000000FF00000000 & (input << 8) |
(0x00000000FF000000) & (input >> 8) | 0x0000FF0000000000 & (input << 24) |
(0x000000FF00000000) & (input << 8) | 0x00FF000000000000 & (input << 40) |
(0x0000FF0000000000) & (input << 24) | 0xFF00000000000000 & (input << 56);
(0x00FF000000000000) & (input << 40) | }
(0xFF00000000000000) & (input << 56));
} public static uint SwapEndian(uint input)
{
public static uint SwapEndian(uint input) return ((input >> 24) & 0xff) |
{ ((input << 8) & 0xff0000) |
return ((input >> 24) & 0xff) | ((input >> 8) & 0xff00) |
((input << 8) & 0xff0000) | ((input << 24) & 0xff000000);
((input >> 8) & 0xff00) | }
((input << 24) & 0xff000000);
} public static int SwapEndian(int input)
{
public static int SwapEndian(int input) var inputAsUint = (uint) input;
{
uint inputAsUint = (uint)input; input = (int)
(((inputAsUint >> 24) & 0xff) |
input = (int) ((inputAsUint << 8) & 0xff0000) |
(((inputAsUint >> 24) & 0xff) | ((inputAsUint >> 8) & 0xff00) |
((inputAsUint << 8) & 0xff0000) | ((inputAsUint << 24) & 0xff000000));
((inputAsUint >> 8) & 0xff00) |
((inputAsUint << 24) & 0xff000000)); return input;
}
return input;
} public static uint MurmurHash2(string key, uint seed)
{
public static uint MurmurHash2(string key, uint seed) // 'm' and 'r' are mixing constants generated offline.
{ // They're not really 'magic', they just happen to work well.
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well. var data = Encoding.ASCII.GetBytes(key);
const uint m = 0x5bd1e995;
byte[] data = Encoding.ASCII.GetBytes(key); const int r = 24;
const uint m = 0x5bd1e995; var len = key.Length;
const int r = 24; var dataIndex = len - 4;
int len = key.Length;
int dataIndex = len - 4; // Initialize the hash to a 'random' value
// Initialize the hash to a 'random' value var h = seed ^ (uint) len;
uint h = seed ^ (uint)len; // Mix 4 bytes at a time into the hash
// Mix 4 bytes at a time into the hash
while (len >= 4)
{
while (len >= 4) h *= m;
{
h *= m; var k = (uint) BitConverter.ToInt32(data, dataIndex);
k = ((k >> 24) & 0xff) | // move byte 3 to byte 0
uint k = (uint)BitConverter.ToInt32(data, dataIndex); ((k << 8) & 0xff0000) | // move byte 1 to byte 2
k = ((k >> 24) & 0xff) | // move byte 3 to byte 0 ((k >> 8) & 0xff00) | // move byte 2 to byte 1
((k << 8) & 0xff0000) | // move byte 1 to byte 2 ((k << 24) & 0xff000000); // byte 0 to byte 3
((k >> 8) & 0xff00) | // move byte 2 to byte 1
((k << 24) & 0xff000000); // byte 0 to byte 3 k *= m;
k ^= k >> r;
k *= m; k *= m;
k ^= k >> r;
k *= m; h ^= k;
h ^= k; dataIndex -= 4;
len -= 4;
dataIndex -= 4; }
len -= 4;
} // Handle the last few bytes of the input array
switch (len)
// Handle the last few bytes of the input array {
switch (len) case 3:
{ h ^= (uint) data[0] << 16;
case 3: goto case 2;
h ^= (uint)data[0] << 16; goto case 2; case 2:
case 2: h ^= (uint) data[len - 2] << 8;
h ^= (uint)data[len - 2] << 8; goto case 1; 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
// bytes are well-incorporated. // Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m; h ^= h >> 13;
h ^= h >> 15; h *= m;
h ^= h >> 15;
return h;
} return h;
}
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;
for (int i = 0; i < array.Length; i += 8) var dataCounter = 0;
{ 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) {
break; if (i + bitCount >= array.Length)
data[dataCounter] = (byte)(((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]); break;
} data[dataCounter] = (byte) (((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]);
dataCounter++; }
} dataCounter++;
}
return data;
return data;
} }
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,45 +1,62 @@
<?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
for information on customizing logging rules and outputs. for information on customizing logging rules and outputs.
--> -->
<targets async="true"> <targets async="true">
<!-- <!--
add your targets here add your targets here
See https://github.com/nlog/NLog/wiki/Targets for possible targets. See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers. See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
--> -->
<!-- <!--
Write events to a file with the date in the filename. Write events to a file with the date in the filename.
<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"
</targets> 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>
<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" />
--> -->
</rules> </rules>
</nlog> </nlog>

View file

@ -1,65 +1,63 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Reflection; using System.Reflection;
using FFXIVClassic.Common; using FFXIVClassic.Common;
using NLog; using NLog;
namespace FFXIVClassic_Lobby_Server namespace FFXIVClassic_Lobby_Server
{ {
class Program class Program
{ {
public static Logger Log; public static Logger Log;
static void Main(string[] args) static void Main(string[] args)
{ {
// set up logging // set up logging
Log = LogManager.GetCurrentClassLogger(); Log = LogManager.GetCurrentClassLogger();
#if DEBUG #if DEBUG
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; //Load Config
if (!ConfigConstants.Load())
//Load Config startServer = false;
if (!ConfigConstants.Load())
startServer = false; //Test DB Connection
Program.Log.Info("Testing DB connection to \"{0}\"... ", ConfigConstants.DATABASE_HOST);
//Test DB Connection 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)))
Program.Log.Info("Testing DB connection to \"{0}\"", ConfigConstants.DATABASE_HOST); {
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) try
{ {
try conn.Open();
{ conn.Close();
conn.Open();
conn.Close(); Program.Log.Info("Connection ok.");
}
Program.Log.Info("Connection ok."); catch (MySqlException e)
} {
catch (MySqlException e) Program.Log.Error(e.ToString());
{ startServer = false;
Program.Log.Error(e.ToString()); }
startServer = false; }
}
} //Start Server if A-OK
if (startServer)
//Start Server if A-OK {
if (startServer) Server server = new Server();
{ server.StartServer();
Server server = new Server();
server.StartServer();
while (true) Thread.Sleep(10000); while (true) Thread.Sleep(10000);
} }
Program.Log.Info("Press any key to continue..."); Program.Log.Info("Press any key to continue...");
Console.ReadKey(); Console.ReadKey();
} }
} }
} }

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<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;
#endif
}
} }
}
public static class LoggerExtensions
{
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

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
<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
// Debug var cmd = split[0];
//SendMessage(client, string.Join(",", split));
if (cmd.Any())
if (split.Length >= 1)
{ {
#region !help // if client isnt null, take player to be the player actor
if (split[0].Equals("help")) var player = client?.GetActor();
if (cmd.Equals("help"))
{ {
if (split.Length == 1) // if there's another string after this, take it as the command we want the description for
if (split.Length > 1)
{ {
SendMessage(client, Resources.CPhelp); LuaEngine.RunGMCommand(player, split[1], null, true);
}
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;
}
#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; return true;
} }
catch (Exception e)
{
Program.Log.Error("Could not load packet: " + e);
}
}
#endregion
#region !reloadzones // print out all commands
else if (split[0].Equals("reloadzones")) foreach (var str in Directory.GetFiles("./scripts/commands/gm/"))
{
if (client != null)
{ {
Program.Log.Info(String.Format("Got request to reset zone: {0}", client.GetActor().zoneId)); var c = str.Replace(".lua", "");
client.GetActor().zone.Clear(); c = c.Replace("./scripts/commands/gm/", "");
client.GetActor().zone.AddActorToZone(client.GetActor());
client.GetActor().SendInstanceUpdate(); LuaEngine.RunGMCommand(player, c, null, true);
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; return true;
} }
#endregion
#region !reloaditems LuaEngine.RunGMCommand(player, cmd.ToString(), split.ToArray());
else if (split[0].Equals("reloaditems")) return true;
}
// Debug
//SendMessage(client, string.Join(",", split));
if (split.Length >= 1)
{
// TODO: reloadzones
#region !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
@ -23,20 +24,36 @@
See https://github.com/nlog/NLog/wiki/Targets for possible targets. See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers. See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
--> -->
<!-- <!--
Write events to a file with the date in the filename. Write events to a file with the date in the filename.
<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

@ -1,85 +1,83 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Text; using System.Text;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Reflection; using System.Reflection;
using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic.Common; using FFXIVClassic.Common;
using NLog; using NLog;
using NLog.Targets; using NLog.Targets;
using NLog.Targets.Wrappers; using NLog.Targets.Wrappers;
using NLog.Config; using NLog.Config;
namespace FFXIVClassic_Map_Server namespace FFXIVClassic_Map_Server
{ {
class Program class Program
{ {
public static Logger Log; public static Logger Log;
static void Main(string[] args) static void Main(string[] args)
{ {
// set up logging // set up logging
Log = LogManager.GetCurrentClassLogger(); Log = LogManager.GetCurrentClassLogger();
#if DEBUG #if DEBUG
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out); TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter); Debug.Listeners.Add(myWriter);
#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
if (!ConfigConstants.Load())
//Load Config startServer = false;
if (!ConfigConstants.Load())
startServer = false; //Test DB Connection
Program.Log.Info("Testing DB connection... ");
//Test DB Connection 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)))
Program.Log.Info("Testing DB connection... "); {
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 conn.Open();
{ conn.Close();
conn.Open();
conn.Close(); Program.Log.Info("Connection ok.");
}
Program.Log.Info("Connection ok."); catch (MySqlException e)
} {
catch (MySqlException e) Program.Log.Error(e.ToString());
{ startServer = false;
Program.Log.Error(e.ToString()); }
startServer = false; }
}
} //Check World ID
DBWorld thisWorld = Database.GetServer(ConfigConstants.DATABASE_WORLDID);
//Check World ID if (thisWorld != null)
DBWorld thisWorld = Database.GetServer(ConfigConstants.DATABASE_WORLDID); Program.Log.Info("Successfully pulled world info from DB. Server name is {0}.", thisWorld.name);
if (thisWorld != null) else
Program.Log.Info("Successfully pulled world info from DB. Server name is {0}.", thisWorld.name); Program.Log.Info("World info could not be retrieved from the DB. Welcome and MOTD will not be displayed.");
else
Program.Log.Info("World info could not be retrieved from the DB. Welcome and MOTD will not be displayed."); //Start server if A-OK
if (startServer)
//Start server if A-OK {
if (startServer) Server server = new Server();
{ CommandProcessor cp = new CommandProcessor(server.GetConnectedPlayerList());
Server server = new Server(); server.StartServer();
CommandProcessor cp = new CommandProcessor(server.GetConnectedPlayerList());
server.StartServer(); while (startServer)
{
while (startServer) String input = Console.ReadLine();
{ Log.Info("[Console Input] " + input);
String input = Console.ReadLine(); cp.DoCommand(input, null);
Log.Info("[Console Input] " + input); }
cp.DoCommand(input, null); }
}
} Program.Log.Info("Press any key to continue...");
Console.ReadKey();
Program.Log.Info("Press any key to continue..."); }
Console.ReadKey();
}
}
}
}
}

View file

@ -11,7 +11,7 @@ using FFXIVClassic_Map_Server.actors.area;
namespace FFXIVClassic_Map_Server.Actors namespace FFXIVClassic_Map_Server.Actors
{ {
class Actor class Actor
{ {
public uint actorId; public uint actorId;
public string actorName; public string actorName;
@ -58,12 +58,12 @@ namespace FFXIVClassic_Map_Server.Actors
public SubPacket CreateAddActorPacket(uint playerActorId, byte val) public SubPacket CreateAddActorPacket(uint playerActorId, byte val)
{ {
return AddActorPacket.BuildPacket(actorId, playerActorId, val); return AddActorPacket.BuildPacket(actorId, playerActorId, val);
} }
public SubPacket CreateNamePacket(uint playerActorId) public SubPacket CreateNamePacket(uint playerActorId)
{ {
return SetActorNamePacket.BuildPacket(actorId, playerActorId, displayNameId, displayNameId == 0xFFFFFFFF | displayNameId == 0x0 ? customDisplayName : ""); return SetActorNamePacket.BuildPacket(actorId, playerActorId, displayNameId, displayNameId == 0xFFFFFFFF | displayNameId == 0x0 ? customDisplayName : "");
} }
public SubPacket CreateSpeedPacket(uint playerActorId) public SubPacket CreateSpeedPacket(uint playerActorId)
{ {
@ -95,10 +95,10 @@ namespace FFXIVClassic_Map_Server.Actors
{ {
SubPacket spawnPacket; SubPacket spawnPacket;
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, playerActorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, false); spawnPacket = SetActorPositionPacket.BuildPacket(actorId, playerActorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, false);
//return SetActorPositionPacket.BuildPacket(actorId, playerActorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE); //return SetActorPositionPacket.BuildPacket(actorId, playerActorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
spawnPacket.DebugPrintSubPacket(); spawnPacket.DebugPrintSubPacket();
return spawnPacket; return spawnPacket;
@ -124,8 +124,8 @@ namespace FFXIVClassic_Map_Server.Actors
if (eventConditions.talkEventConditions != null) if (eventConditions.talkEventConditions != null)
{ {
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions) foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetTalkEventCondition.BuildPacket(playerActorId, actorId, condition)); subpackets.Add(SetTalkEventCondition.BuildPacket(playerActorId, actorId, condition));
} }
if (eventConditions.noticeEventConditions != null) if (eventConditions.noticeEventConditions != null)
@ -171,8 +171,8 @@ namespace FFXIVClassic_Map_Server.Actors
if (eventConditions.talkEventConditions != null) if (eventConditions.talkEventConditions != null)
{ {
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions) foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(playerActorId, actorId, true, 1, condition.conditionName)); subpackets.Add(SetEventStatus.BuildPacket(playerActorId, actorId, true, 1, condition.conditionName));
} }
if (eventConditions.noticeEventConditions != null) if (eventConditions.noticeEventConditions != null)
@ -219,7 +219,7 @@ namespace FFXIVClassic_Map_Server.Actors
} }
public virtual BasePacket GetSpawnPackets(uint playerActorId) public virtual BasePacket GetSpawnPackets(uint playerActorId)
{ {
return GetSpawnPackets(playerActorId, 0x1); return GetSpawnPackets(playerActorId, 0x1);
} }
@ -229,13 +229,13 @@ namespace FFXIVClassic_Map_Server.Actors
subpackets.Add(CreateAddActorPacket(playerActorId, 8)); subpackets.Add(CreateAddActorPacket(playerActorId, 8));
subpackets.AddRange(GetEventConditionPackets(playerActorId)); subpackets.AddRange(GetEventConditionPackets(playerActorId));
subpackets.Add(CreateSpeedPacket(playerActorId)); subpackets.Add(CreateSpeedPacket(playerActorId));
subpackets.Add(CreateSpawnPositonPacket(playerActorId, spawnType)); subpackets.Add(CreateSpawnPositonPacket(playerActorId, spawnType));
subpackets.Add(CreateNamePacket(playerActorId)); subpackets.Add(CreateNamePacket(playerActorId));
subpackets.Add(CreateStatePacket(playerActorId)); subpackets.Add(CreateStatePacket(playerActorId));
subpackets.Add(CreateIsZoneingPacket(playerActorId)); subpackets.Add(CreateIsZoneingPacket(playerActorId));
subpackets.Add(CreateScriptBindPacket(playerActorId)); subpackets.Add(CreateScriptBindPacket(playerActorId));
return BasePacket.CreatePacket(subpackets, true, false); return BasePacket.CreatePacket(subpackets, true, false);
} }
public virtual BasePacket GetInitPackets(uint playerActorId) public virtual BasePacket GetInitPackets(uint playerActorId)
{ {
@ -301,7 +301,7 @@ namespace FFXIVClassic_Map_Server.Actors
SubPacket ChangeSpeedPacket = SetActorSpeedPacket.BuildPacket(actorId, actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2]); SubPacket ChangeSpeedPacket = SetActorSpeedPacket.BuildPacket(actorId, actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2]);
zone.BroadcastPacketAroundActor(this, ChangeSpeedPacket); zone.BroadcastPacketAroundActor(this, ChangeSpeedPacket);
} }
public void GenerateActorName(int actorNumber) public void GenerateActorName(int actorNumber)
{ {
//Format Class Name //Format Class Name
@ -311,7 +311,7 @@ namespace FFXIVClassic_Map_Server.Actors
.Replace("MapObj", "Map") .Replace("MapObj", "Map")
.Replace("Object", "Obj") .Replace("Object", "Obj")
.Replace("Retainer", "Rtn") .Replace("Retainer", "Rtn")
.Replace("Standard", "Std"); .Replace("Standard", "Std");
className = Char.ToLowerInvariant(className[0]) + className.Substring(1); className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
//Format Zone Name //Format Zone Name
@ -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,7 +13,8 @@ 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
{ {
class Area : Actor class Area : Actor
@ -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

@ -70,7 +70,7 @@ namespace FFXIVClassic_Map_Server.dataobjects
} }
public void QueuePacket(SubPacket subPacket, bool isAuthed, bool isEncrypted) public void QueuePacket(SubPacket subPacket, bool isAuthed, bool isEncrypted)
{ {
zoneConnection.QueuePacket(subPacket, isAuthed, isEncrypted); zoneConnection.QueuePacket(subPacket, isAuthed, isEncrypted);
} }

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
{ {
@ -36,8 +37,8 @@ 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;
@ -54,8 +55,8 @@ namespace FFXIVClassic_Map_Server.lua
} }
return null; return null;
} }
public static Coroutine DoActorOnEventStarted(Player player, Actor target, EventStartPacket eventStart) public static Coroutine DoActorOnEventStarted(Player player, Actor target, EventStartPacket eventStart)
{ {
string luaPath; string luaPath;
@ -68,12 +69,12 @@ namespace FFXIVClassic_Map_Server.lua
{ {
luaPath = String.Format(FILEPATH_DIRECTORS, target.GetName()); luaPath = String.Format(FILEPATH_DIRECTORS, target.GetName());
} }
else else
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;
@ -87,8 +88,8 @@ namespace FFXIVClassic_Map_Server.lua
{ {
SendError(player, String.Format("ERROR: Could not find script for actor {0}.", target.GetName())); SendError(player, String.Format("ERROR: Could not find script for actor {0}.", target.GetName()));
return null; return null;
} }
} }
public static void DoActorOnSpawn(Player player, Npc target) public static void DoActorOnSpawn(Player player, Npc target)
@ -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;
@ -121,18 +122,18 @@ namespace FFXIVClassic_Map_Server.lua
return; return;
} }
string luaPath; string luaPath;
if (target is Command) if (target is Command)
luaPath = String.Format(FILEPATH_COMMANDS, target.GetName()); luaPath = String.Format(FILEPATH_COMMANDS, target.GetName());
else if (target is Director) else if (target is Director)
luaPath = String.Format(FILEPATH_DIRECTORS, target.GetName()); luaPath = String.Format(FILEPATH_DIRECTORS, target.GetName());
else else
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; return;
@ -151,16 +152,16 @@ namespace FFXIVClassic_Map_Server.lua
else else
{ {
SendError(player, String.Format("ERROR: Could not find script for actor {0}.", target.GetName())); SendError(player, String.Format("ERROR: Could not find script for actor {0}.", target.GetName()));
} }
} }
public static void OnZoneIn(Player player) public static void OnZoneIn(Player player)
{ {
string luaPath = String.Format(FILEPATH_ZONE, player.GetZone().actorId); string luaPath = String.Format(FILEPATH_ZONE, player.GetZone().actorId);
if (File.Exists(luaPath)) if (File.Exists(luaPath))
{ {
Script script = LoadScript(luaPath); LuaScript script = LoadScript(luaPath);
if (script == null) if (script == null)
return; return;
@ -168,14 +169,14 @@ namespace FFXIVClassic_Map_Server.lua
//Run Script //Run Script
if (!script.Globals.Get("onZoneIn").IsNil()) if (!script.Globals.Get("onZoneIn").IsNil())
script.Call(script.Globals["onZoneIn"], player); script.Call(script.Globals["onZoneIn"], player);
} }
} }
public static void OnBeginLogin(Player player) public static void OnBeginLogin(Player player)
{ {
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,25 +202,171 @@ 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)
{
// 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)
{ {
Script script = new Script(); LuaScript script = LoadGlobals();
((FileSystemScriptLoader)script.Options.ScriptLoader).ModulePaths = FileSystemScriptLoader.UnpackStringPaths("./scripts/?;./scripts/?.lua");
script.Globals["GetWorldManager"] = (Func<WorldManager>)Server.GetWorldManager;
script.Globals["GetStaticActor"] = (Func<string, Actor>)Server.GetStaticActors;
script.Globals["GetWorldMaster"] = (Func<Actor>)Server.GetWorldManager().GetActor;
script.Globals["GetItemGamedata"] = (Func<uint, Item>)Server.GetItemGamedata;
try try
{ {
script.DoFile(filename); script.DoFile(filename);
} }
catch(SyntaxErrorException e) catch (SyntaxErrorException e)
{ {
Program.Log.Error("LUAERROR: {0}.", e.DecoratedMessage); Program.Log.Error("LUAERROR: {0}.", e.DecoratedMessage);
return null; return null;
} }
return script; 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");
script.Globals["GetWorldManager"] = (Func<WorldManager>)Server.GetWorldManager;
script.Globals["GetStaticActor"] = (Func<string, Actor>)Server.GetStaticActors;
script.Globals["GetWorldMaster"] = (Func<Actor>)Server.GetWorldManager().GetActor;
script.Globals["GetItemGamedata"] = (Func<uint, Item>)Server.GetItemGamedata;
script.Options.DebugPrint = s => { Program.Log.Debug(s); };
return script;
} }
public static void SendError(Player player, string message) public static void SendError(Player player, string message)
@ -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,44 +1,46 @@
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
{ {
public byte isAuthenticated; public byte isAuthenticated;
public byte isCompressed; public byte isCompressed;
public ushort connectionType; public ushort connectionType;
public ushort packetSize; public ushort packetSize;
public ushort numSubpackets; public ushort numSubpackets;
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 byte[] data;
public BasePacketHeader header; public BasePacketHeader header;
public byte[] data;
//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)
@ -72,7 +74,7 @@ namespace FFXIVClassic_Map_Server.packets
int packetSize = header.packetSize; int packetSize = header.packetSize;
data = new byte[packetSize - BASEPACKET_SIZE]; data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE); Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
} }
public unsafe BasePacket(byte[] bytes, ref int offset) public unsafe BasePacket(byte[] bytes, ref int offset)
@ -82,13 +84,13 @@ 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;
if (bytes.Length < offset + header.packetSize) if (bytes.Length < offset + header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size"); throw new OverflowException("Packet Error: Packet size didn't equal given size");
data = new byte[packetSize - BASEPACKET_SIZE]; data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, offset + BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE); Array.Copy(bytes, offset + BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
@ -97,24 +99,24 @@ namespace FFXIVClassic_Map_Server.packets
} }
public BasePacket(BasePacketHeader header, byte[] data) public BasePacket(BasePacketHeader header, byte[] data)
{ {
this.header = header; this.header = header;
this.data = data; this.data = data;
} }
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));
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;
} }
} }
#endregion
public void DebugPrintPacket() public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet)
{ {
#if DEBUG var data = packet.data;
// todo: create new target for colourful packet logging int size = packet.header.packetSize;
//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())); var offset = 0;
while (offset < data.Length)
foreach (SubPacket sub in GetSubpackets())
{ {
sub.DebugPrintSubPacket(); if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
} throw new OverflowException("Packet Error: Subpacket was too small");
//Console.BackgroundColor = ConsoleColor.Black; SubPacketHeader header;
#endif 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
} }
}
public static class LoggerExtensions
{
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,37 +1,40 @@
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
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SubPacketHeader public struct SubPacketHeader
{ {
public ushort subpacketSize; public ushort subpacketSize;
public ushort type; public ushort type;
public uint sourceId; public uint sourceId;
public uint targetId; public uint targetId;
public uint unknown1; public uint unknown1;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct GameMessageHeader public struct GameMessageHeader
{ {
public ushort unknown4; //Always 0x14 public ushort unknown4; //Always 0x14
public ushort opcode; public ushort opcode;
public uint unknown5; public uint unknown5;
public uint timestamp; public uint timestamp;
public uint unknown6; public uint unknown6;
} }
public class SubPacket public class SubPacket
{ {
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 SubPacketHeader header; public byte[] data;
public GameMessageHeader gameMessage; public GameMessageHeader gameMessage;
public byte[] data;
public SubPacketHeader header;
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,
Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkRed);
Program.Log.Debug("Size: 0x{0:X}{1}{2}", header.subpacketSize, Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes()));
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,10 +14,55 @@ 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, ...)
player:RunEventFunction(functionName, ...); player:RunEventFunction(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;