{(
)) MEOW.dpr
(( Mini EO? WOW!
))
(( A simple 65k EO server created by Sordie out of pure bordem
)}
program MEOW;
{$APPTYPE CONSOLE}
{$DEFINE SILENTUNHANDLED}
uses
Windows, WinSock;
const
Range = 10;
PacketFamilyRaw = 255;
PacketFamilyConnection = 1;
PacketFamilyGameData = 5;
PacketFamilyWalk = 6;
PacketFamilyFace = 7;
PacketFamilyTalk = 18;
PacketFamilyPlayers = 22;
PacketFamilyRequest = 27;
PacketActionRaw = 255;
PacketActionRequest = 1;
PacketActionAccept = 2;
PacketActionReply = 3;
PacketActionRemove = 4;
PacketActionAgree = 5;
PacketActionPlayer = 8;
PacketActionMessage = 15;
PacketActionSpecial = 16;
PacketActionAdmin = 17;
PacketActionReport = 21;
RequiredVersion: array[0..2] of Byte = (0, 0, 28);
FileIDMap = 1;
FileIDItem = 2;
FileIDNPC = 3;
FileIDSpell = 4;
FileIDClass = 5;
DirectionDown = 0;
DirectionLeft = 1;
DirectionUp = 2;
DirectionRight = 3;
type
TPacket = packed record
Family: Byte;
Action: Byte;
Data: AnsiString;
procedure SetID(AFamily, AAction: Byte);
procedure Reset; inline;
procedure Discard(Count: Integer = 1); inline;
procedure AddByte(v: Byte); inline;
procedure AddInt1(v: Byte); inline;
procedure AddInt2(v: Word); inline;
procedure AddInt3(v: Cardinal); inline;
procedure AddInt4(v: Cardinal); inline;
procedure AddBreakString(v: AnsiString); inline;
procedure AddString (v: AnsiString); inline;
function GetByte: Byte;
function GetInt1: Byte;
function GetInt2: Word;
function GetInt3: Cardinal;
function GetInt4: Cardinal;
function GetBreakString: AnsiString;
function GetString(Len: Integer = -1): AnsiString;
end;{TPacket}
TGameData = class
class function DataID: Byte; virtual; abstract;
var Data: AnsiString;
var CRC: array[0..3] of Byte;
var Len: array[0..1] of Byte;
constructor Create(FileName: String);
end;{TGameData}
TItemData = class(TGameData)
class function DataID: Byte; override;
end;{TItemData}
TNPCData = class(TGameData)
class function DataID: Byte; override;
end;{TNPCData}
TSpellData = class(TGameData)
class function DataID: Byte; override;
end;{TSpellData}
TClassData = class(TGameData)
class function DataID: Byte; override;
end;{TClassData}
TMapData = class(TGameData)
class function DataID: Byte; override;
end;{TMapData}
TSession = class
class var Sessions: array of TSession;
class var SessionsLock: TRTLCriticalSection;
class constructor Create;
class destructor Destroy;
class procedure ThreadSafe; inline;
class procedure ThreadSafeDone; inline;
class function GetSession(ID: Cardinal): TSession;
class procedure SendAll(Packet: TPacket; Ignore: TSession = nil; Raw: Boolean = False);
var Socket: TSocket;
var ThreadID: Cardinal;
var ID: Cardinal;
var Buffer: AnsiString;
var PacketIn: TPacket;
var PacketOut: TPacket;
var Initialized: Boolean;
var IPStr: AnsiString;
var IPInt: Integer;
var HDDSerial: AnsiString;
var Name: AnsiString;
var X, Y, D: Integer;
constructor Create(ASocket: TSocket);
destructor Destroy; override;
procedure Log(S: String);
function Index: Integer;
procedure Send (Packet: TPacket; Raw: Boolean = False);
procedure SendInRange(Packet: TPacket; Raw: Boolean = False);
procedure SendGameData(Data: TGameData);
procedure SendGameState;
procedure BuildCharacterPacket(var Packet: TPacket);
function Walk(Direction: Integer; Admin: Boolean = False; Ghost: Boolean = False): Boolean;
function Face(Direction: Integer): Boolean;
function Say(Text: AnsiString): Boolean;
function Execute: Boolean;
procedure DefaultHandler(var Param); override;
procedure HandleRaw (var Param); message PacketFamilyRaw;
procedure HandleConnection(var Param); message PacketFamilyConnection;
procedure HandleGameData (var Param); message PacketFamilyGameData;
procedure HandleWalk (var Param); message PacketFamilyWalk;
procedure HandleFace (var Param); message PacketFamilyFace;
procedure HandleRequest (var Param); message PacketFamilyRequest;
procedure HandleTalk (var Param); message PacketFamilyTalk;
end;{TSession}
const
RecieveKey = 8;
SendKey = 10;
EOInt1Max = 253;
EOInt2Max = 64009;
EOInt3Max = 16194277;
var
ItemData: TItemData;
NPCData: TNPCData;
SpellData: TSpellData;
ClassData: TClassData;
MapData: TMapData;
function Str(i: Integer): String;
var
x: Integer;
f: Boolean;
c: Char;
v: Integer;
begin
v := i;
if v = 0 then exit('0');
if v < 0 then
begin
Result := '-';
v := abs(v);
end{if v < 0}
else Result := '';
f := True;
x := 1000000000;
repeat
c := #48;
while v >= x do
begin
c := chr(Byte(c) + 1);
v := v - x;
end;{while v >= x}
if f and (c <> #48) then f := False;
if not f then Result := Result + c;
x := x div 10;
until x = 0;
end;{Str}
function Int(s: String; Default: Integer = 0): Integer;
var
i: Integer;
n: Boolean;
w: String;
begin
if length(s) = 0 then exit(Default);
n := s[1] = '-';
if n or (s[1] = '+') then w := copy(s, 2, length(s)) else w := s;
if length(w) = 0 then exit(Default);
for i := 1 to length(w) do
if pos(w[i], '0123456789') = 0 then exit(Default);
Result := 0;
for i := 1 to length(w) do
begin
Result := Result * 10;
Result := Result + ord(w[i]) - 48;
end;{for i}
if n then Result := -Result;
end;{Int}
function PackEOInt(b1: Byte = 0; b2: Byte = 0; b3: Byte = 0; b4: Byte = 0): Cardinal;
begin
if b1 = 254 then b1 := 0 else if b1 > 0 then dec(b1);
if b2 = 254 then b2 := 0 else if b2 > 0 then dec(b2);
if b3 = 254 then b3 := 0 else if b3 > 0 then dec(b3);
if b4 = 254 then b4 := 0 else if b4 > 0 then dec(b4);
Result := (b4 * EOInt3Max) + (b3 * EOInt2Max) + (b2 * EOInt1Max) + b1;
end;{PackEOInt}
function UnpackEOInt(Num: Cardinal): AnsiString;
var
i: Cardinal;
begin
Result := #254#254#254#254;
i := Num;
if i >= EOInt3Max then
begin
Result[4] := AnsiChar(Num div EOInt3Max + 1);
Num := Num mod EOInt3Max;
end;{if i >= EOInt3Max}
if i >= EOInt2Max then
begin
Result[3] := AnsiChar(Num div EOInt2Max + 1);
Num := Num mod EOInt2Max;
end;{if i >= EOInt2Max}
if i >= EOInt1Max then
begin
Result[2] := AnsiChar(Num div EOInt1Max + 1);
Num := Num mod EOInt1Max;
end;{if i >= EOInt3Max}
Result[1] := AnsiChar(Num + 1);
end;{UnpackEOInt}
function FoldData(Str: AnsiString; Key: Byte): AnsiString;
var
i, j: Integer;
c: Byte;
Buffer: AnsiString;
begin
if Key = 0 then exit(Str);
Result := '';
Buffer := '';
for i := 1 to length(Str) do
begin
c := ord(Str[i]);
if (c mod Key) = 0 then Buffer := Buffer + AnsiChar(c)
else
begin
if length(Buffer) > 0 then
begin
for j := length(Buffer) downto 1 do
Result := Result + Buffer[j];
Buffer := '';
end;{if length(Buffer)}
Result := Result + AnsiChar(c);
end;{else}
end;{for i}
if length(Buffer) > 0 then
for j := length(Buffer) downto 1 do
Result := Result + Buffer[j];
end;{FoldData}
procedure TPacket.SetID(AFamily, AAction: Byte);
begin
Family := AFamily;
Action := AAction;
end;{TPacket.SetID}
procedure TPacket.Reset;
begin
Data := '';
end;{TPacket.Reset}
procedure TPacket.Discard;
begin
Data := copy(Data, Count + 1, length(Data));
end;{TPacket.Discard}
procedure TPacket.AddByte;
begin
Data := Data + AnsiChar(v);
end;{TPacket.AddByte}
procedure TPacket.AddInt1;
begin
Data := Data + UnpackEOInt(v)[1];
end;{{TPacket.AddInt1}
procedure TPacket.AddInt2;
begin
Data := Data + copy(UnpackEOInt(v), 1, 2);
end;{{TPacket.AddInt2}
procedure TPacket.AddInt3;
begin
Data := Data + copy(UnpackEOInt(v), 1, 3);
end;{TPacket.AddInt3}
procedure TPacket.AddInt4;
begin
Data := Data + UnpackEOInt(v);
end;{TPacket.AddInt4}
procedure TPacket.AddBreakString;
begin
Data := Data + v + #$FF;
end;{TPacket.AddBreakString}
procedure TPacket.AddString;
begin
Data := Data + v;
end;{TPacket.AddString}
function TPacket.GetByte;
begin
if length(Data) = 0 then exit(0);
Result := ord(Data[1]);
Data := copy(Data, 2, length(Data));
end;{TPacket.GetByte}
function TPacket.GetInt1;
begin
if length(Data) = 0 then exit(0);
Result := PackEOInt(ord(Data[1]));
Data := copy(Data, 2, length(Data));
end;{TPacket.GetInt1}
function TPacket.GetInt2;
begin
if length(Data) = 0 then exit(0);
if length(Data) < 2 then exit(GetInt1);
Result := PackEOInt(ord(Data[1]), ord(Data[2]));
Data := copy(Data, 3, length(Data));
end;{TPacket.GetInt2}
function TPacket.GetInt3;
begin
if length(Data) = 0 then exit(0);
if length(Data) < 2 then exit(GetInt1);
if length(Data) < 3 then exit(GetInt2);
Result := PackEOInt(ord(Data[1]), ord(Data[2]), ord(Data[3]));
Data := copy(Data, 4, length(Data));
end;{TPacket.GetInt3}
function TPacket.GetInt4;
begin
if length(Data) = 0 then exit(0);
if length(Data) < 2 then exit(GetInt1);
if length(Data) < 3 then exit(GetInt2);
if length(Data) < 4 then exit(GetInt3);
Result := PackEOInt(ord(Data[1]), ord(Data[2]), ord(Data[3]), ord(Data[4]));
Data := copy(Data, 5, length(Data));
end;{TPacketGetInt4}
function TPacket.GetBreakString;
var
i: Integer;
begin
for i := 1 to length(Data) do
if Data[i] = #$FF then break;
Result := copy(Data, 1, i - 1);
Data := copy(Data, i + 1, length(Data));
end;{TPacket.GetBreakString}
function TPacket.GetString;
begin
if Len = -1 then
begin
Result := Data;
Data := '';
end{if Len = -1}
else
begin
Result := copy(Data, 1, Len);
Data := copy(Data, Len + 1, length(Data));
end;{else}
end;{TPacket.GetString}
constructor TGameData.Create;
var
l: Cardinal;
f: THandle;
begin
inherited Create;
f := CreateFile(PChar(FileName), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if f = 0 then exit;
try
l := SetFilePointer(f, 0, nil, FILE_END);
if l = Cardinal(-1) then exit;
SetLength(Data, l);
SetFilePointer(f, 0, nil, FILE_BEGIN);
ReadFile(f, Data[1], length(Data), l, nil);
CRC[0] := ord(Data[4]);
CRC[1] := ord(Data[5]);
CRC[2] := ord(Data[6]);
CRC[3] := ord(Data[7]);
Len[0] := ord(Data[8]);
Len[1] := ord(Data[9]);
finally
CloseHandle(f);
end;{try...finally}
end;{TGameData.Load}
class function TItemData.DataID;
begin
Result := 5;
end;{TItemData.DataID}
class function TNPCData.DataID;
begin
Result := 6;
end;{TNPCData.DataID}
class function TSpellData.DataID;
begin
Result := 7;
end;{TSpellData.DataID}
class function TClassData.DataID;
begin
Result := 11;
end;{TClassData.DataID}
class function TMapData.DataID;
begin
Result := 4;
end;{TMapData.DataID}
function SessionThread(Session: TSession): Integer;
begin
Result := 0;
try
try
while Session.Execute do
sleep(1);
Session.Free;
except
Writeln('Exception in session thread');
Result := -1;
end;{try...except}
finally
EndThread(Result);
end;{try...finally}
end;{SessionThread}
class constructor TSession.Create;
begin
SetLength(Sessions, 0);
InitializeCriticalSection(SessionsLock);
end;{class)TSession.Create}
class destructor TSession.Destroy;
begin
DeleteCriticalSection(SessionsLock);
end;{class)TSession.Destroy}
class procedure TSession.ThreadSafe;
begin
EnterCriticalSection(SessionsLock);
end;{class)TSession.ThreadSafe}
class procedure TSession.ThreadSafeDone;
begin
LeaveCriticalSection(SessionsLock);
end;{class)TSession.ThreadSafeDone}
class function TSession.GetSession;
var
Session: TSession;
begin
for Session in Sessions do
if Session.ID = ID then exit(Session);
Result := nil;
end;{class)TSession.GetSession}
class procedure TSession.SendAll(Packet: TPacket; Ignore: TSession = nil; Raw: Boolean = False);
var
Session: TSession;
begin
ThreadSafe;
try
for Session in Sessions do
if Session <> Ignore then
Session.Send(Packet, Raw);
finally
ThreadSafeDone;
end;{try...finally}
end;{class)TSession.SendAll}
constructor TSession.Create;
var
i: Integer;
Addr: TSockAddr;
begin
inherited Create;
Initialized := False;
Buffer := '';
Socket := ASocket;
FillChar(Addr, sizeof(Addr), 0);
i := sizeof(addr);
getpeername(Socket, Addr, i);
IPStr := AnsiString(inet_ntoa(Addr.sin_addr));
IPInt := Addr.sin_addr.S_addr;
Name := IPStr;
X := 12;
Y := 12;
D := 0;
i := 1;
ioctlsocket(Socket, FIONBIO, i);
ID := 100;
ThreadSafe;
try
while GetSession(ID) <> nil do inc(ID);
SetLength(Sessions, length(Sessions) + 1);
Sessions[high(Sessions)] := Self;
finally
ThreadSafeDone;
end;{try...finally}
Log('Created');
BeginThread(nil, 0, @SessionThread, Pointer(Self), 0, ThreadID);
end;{TSession.Create}
destructor TSession.Destroy;
var
i: Integer;
Packet: TPacket;
begin
if Socket <> 0 then
begin
closesocket(Socket);
Socket := 0;
end;{if Socket}
ThreadSafe;
try
i := Index;
if i > -1 then
begin
if i < high(Sessions) then
move(Sessions[i + 1], Sessions[i], sizeof(TSession) * (length(Sessions) - 1));
SetLength(Sessions, length(Sessions) - 1);
end;{if i > -1}
finally
ThreadSafeDone;
end;{try...finally}
Packet.SetID(PacketFamilyPlayers, PacketActionRemove);
Packet.AddInt2(ID);
SendAll(Packet);
Log('Destroyed');
inherited;
end;{TSession.Destroy}
procedure TSession.Log;
begin
Writeln('Session[', ID, '] ', IPStr, ': ', S);
end;{TSession.Log}
function TSession.Index;
var
i: Integer;
begin
for i := 0 to length(Sessions) - 1 do
if Sessions[i] = self then exit(i);
Result := -1;
end;{TSession.Index}
procedure TSession.Send(Packet: TPacket; Raw: Boolean = False);
var
i, j, Size: Integer;
Encoded: AnsiString;
EncodeBuf: AnsiString;
begin
Encoded := copy(UnpackEOInt(length(Packet.Data) + 2), 1, 2) +
AnsiChar(Packet.Action) +
AnsiChar(Packet.Family) +
Packet.Data;
Size := length(Encoded);
if not Raw then
begin
Encoded := FoldData(Encoded, SendKey);
SetLength(EncodeBuf, Size);
EncodeBuf[1] := Encoded[1];
EncodeBuf[2] := Encoded[2];
i := 2; j := 2;
while i < Size do
begin
EncodeBuf[i + 1] := AnsiChar(ord(Encoded[j + 1]) xor $80);
inc(j);
inc(i, 2);
end;{while i < Size}
i := Size - 1;
if Boolean(Size mod 2) then dec(i);
while i >= 2 do
begin
EncodeBuf[i + 1] := AnsiChar(ord(Encoded[j + 1]) xor $80);
inc(j);
dec(i, 2);
end;{while i >= 2}
for i := 3 to Size do
if EncodeBuf[i] = #128 then EncodeBuf[i] := #0
else if EncodeBuf[i] = #0 then EncodeBuf[i] := #128;
Encoded := EncodeBuf;
end;{if not Raw}
WinSock.send(Socket, Encoded[1], length(Encoded), 0);
end;{TSession.Send}
procedure TSession.SendInRange;
var
Session: TSession;
begin
ThreadSafe;
try
for Session in Sessions do
if (Session <> Self) and
(Session.X >= (X - Range)) and (Session.X <= (X + Range)) and
(Session.Y >= (Y - Range)) and (Session.Y <= (Y + Range)) then
Session.Send(Packet, Raw);
finally
ThreadSafeDone;
end;{try...finally}
end;{TSession.SendInRange}
procedure TSession.SendGameData;
var
Packet: TPacket;
begin
Log('Sending game data: ' + Str(Data.DataID));
Packet.SetID(PacketFamilyRaw, PacketActionRaw);
Packet.AddInt1(Data.DataID);
if Data.DataID <> 4 then
Packet.AddInt1(1);
Packet.AddString(Data.Data);
Send(Packet, True);
end;{TSession.SendGameData}
procedure TSession.SendGameState;
var
Packet: TPacket;
begin
Packet.SetID(PacketFamilyGameData, PacketActionReply);
Packet.AddInt2(1);
Packet.AddInt2(ID);
Packet.AddInt4(ID);
Packet.AddInt2(1); // Map ID
Packet.AddByte(MapData.CRC[0]);
Packet.AddByte(MapData.CRC[1]);
Packet.AddByte(MapData.CRC[2]);
Packet.AddByte(MapData.CRC[3]);
Packet.AddInt3(length(MapData.Data));
Packet.AddByte(ItemData.CRC[0]);
Packet.AddByte(ItemData.CRC[1]);
Packet.AddByte(ItemData.CRC[2]);
Packet.AddByte(ItemData.CRC[3]);
Packet.AddByte(ItemData.Len[0]);
Packet.AddByte(ItemData.Len[1]);
Packet.AddByte(NPCData.CRC[0]);
Packet.AddByte(NPCData.CRC[1]);
Packet.AddByte(NPCData.CRC[2]);
Packet.AddByte(NPCData.CRC[3]);
Packet.AddByte(NPCData.Len[0]);
Packet.AddByte(NPCData.Len[1]);
Packet.AddByte(SpellData.CRC[0]);
Packet.AddByte(SpellData.CRC[1]);
Packet.AddByte(SpellData.CRC[2]);
Packet.AddByte(SpellData.CRC[3]);
Packet.AddByte(SpellData.Len[0]);
Packet.AddByte(SpellData.Len[1]);
Packet.AddByte(ClassData.CRC[0]);
Packet.AddByte(ClassData.CRC[1]);
Packet.AddByte(ClassData.CRC[2]);
Packet.AddByte(ClassData.CRC[3]);
Packet.AddByte(ClassData.Len[0]);
Packet.AddByte(ClassData.Len[1]);
Packet.AddBreakString(Name); // Name
Packet.AddBreakString(IPStr); // Title
Packet.AddBreakString(''); // Guild
Packet.AddBreakString(''); // Rank
Packet.AddInt1(0); // Class index
Packet.AddString(' '); // Tag
Packet.AddInt1(1); // Admin
Packet.AddInt1(0); // Level
Packet.AddInt4(0); // Exp
Packet.AddInt4(0); // Usage
Packet.AddInt2(10); // HP
Packet.AddInt2(10); // MaxHP
Packet.AddInt2(10); // TP
Packet.AddInt2(10); // MaxTP
Packet.AddInt2(10); // MaxSP
Packet.AddInt2(0); // Stat points
Packet.AddInt2(0); // Skill points
Packet.AddInt2(0); // Karma
Packet.AddInt2(0); // Min damage
Packet.AddInt2(0); // Max damage
Packet.AddInt2(0); // Accuracy
Packet.AddInt2(0); // Evade
Packet.AddInt2(0); // Armour
Packet.AddInt2(0); // Str
Packet.AddInt2(0); // Int
Packet.AddInt2(0); // Wis
Packet.AddInt2(0); // Agi
Packet.AddInt2(0); // Con
Packet.AddInt2(0); // Cha
Packet.AddInt2(0); // Elements
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt1(0); // Guild Rank
Packet.AddInt2(1); // Jail map
Packet.AddInt2(4);
Packet.AddInt1($F0);
Packet.AddInt1($F0);
Packet.AddInt2($FFF0);
Packet.AddInt2($FFF0);
Packet.AddInt2(1);
Packet.AddInt2(1);
Packet.AddInt1(0);
Packet.AddByte(255);
Send(Packet);
end;{TSession.SendGameState}
procedure TSession.BuildCharacterPacket;
begin
Packet.AddBreakString(Name);
Packet.AddInt2(ID);
Packet.AddInt2(1); // Map ID
Packet.AddInt2(X);
Packet.AddInt2(Y);
Packet.AddInt1(D);
Packet.AddInt1(0); // Class
Packet.AddString(' '); // Tag
Packet.AddInt1(0); // Level
Packet.AddInt1(0); // Sex
Packet.AddInt1(1); // Hair style
Packet.AddInt1(1); // Hair colour
Packet.AddInt1(0); // Race
Packet.AddInt2(10); // MaxHP
Packet.AddInt2(10); // HP
Packet.AddInt2(10); // MaxTP
Packet.AddInt2(10); // TP
Packet.AddInt2(0); // Boots
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0);
Packet.AddInt2(0); // Armour
Packet.AddInt2(0);
Packet.AddInt2(0); // Hat
Packet.AddInt2(0); // Shield
Packet.AddInt2(0); // Weapon
Packet.AddInt1(0); // Sittting
Packet.AddInt1(0); // Hidden
end;{TSession.BuildCharacterPacket}
function TSession.Walk;
var
i: Integer;
State: Integer;
NewX, NewY: Integer;
PacketShow: TPacket;
PacketHide: TPacket;
PacketWalk: TPacket;
PacketChar: TPacket;
Session: TSession;
NewCoords: array[-Range..Range] of TPoint;
OldCoords: array[-Range..Range] of TPoint;
begin
NewX := X;
NewY := Y;
case Direction of
DirectionDown: inc(NewY);
DirectionLeft: dec(NewX);
DirectionUp: dec(NewY);
DirectionRight: inc(NewX);
else
Log('Invalid walk direction ' + Str(Direction));
exit(False);
end;{case Direction}
D := Direction;
X := NewX;
Y := NewY;
PacketShow.SetID(PacketFamilyPlayers, PacketActionAgree);
PacketShow.AddByte(255);
BuildCharacterPacket(PacketShow);
PacketShow.AddByte(255);
PacketShow.AddInt1(1);
PacketHide.SetID(PacketFamilyPlayers, PacketActionRemove);
PacketHide.AddInt2(ID);
PacketWalk.SetID(PacketFamilyWalk, PacketActionPlayer);
PacketWalk.AddInt2(ID);
PacketWalk.AddInt1(D);
PacketWalk.AddInt1(X);
PacketWalk.AddInt1(Y);
for i := -Range to Range do
case Direction of
DirectionDown:
begin
NewCoords[i].X := X + i;
NewCoords[i].Y := Y + Range - abs(i);
OldCoords[i].X := X + i;
OldCoords[i].Y := Y - Range - 1 + abs(i);
end;{DirectionDown:}
DirectionLeft:
begin
NewCoords[i].X := X - Range + abs(i);
NewCoords[i].Y := Y + i;
OldCoords[i].X := X + Range + 1 - abs(i);
OldCoords[i].Y := Y + i;
end;{DirectionLeft:}
DirectionUp:
begin
NewCoords[i].X := X + i;
NewCoords[i].Y := Y - Range + abs(i);
OldCoords[i].X := X + i;
OldCoords[i].Y := Y + Range + 1 - abs(i);
end;{DirectionUp:}
DirectionRight:
begin
NewCoords[i].X := X + Range - abs(i);
NewCoords[i].Y := Y + i;
OldCoords[i].X := X - Range - 1 + abs(i);
OldCoords[i].Y := Y + i;
end;{DirectionRight:}
end;{case Direction}
ThreadSafe;
try
for Session in Sessions do
if (Session <> Self) and
(Session.X >= (X - (Range + 1))) and (Session.X <= (X + (Range + 1))) and
(Session.Y >= (Y - (Range + 1))) and (Session.Y <= (Y + (Range + 1))) then
begin
State := 0;
for i := -Range to Range do
if (Session.X = NewCoords[i].X) and (Session.Y = NewCoords[i].Y) then
begin
State := 1;
break;
end{if (Session.X...}
else if (Session.X = OldCoords[i].X) and (Session.Y = OldCoords[i].Y) then
begin
State := -1;
break;
end;{else if (Session.X...}
case State of
1:
begin
Log('add ' + Str(Session.ID));
PacketChar.Reset;
PacketChar.SetID(PacketFamilyPlayers, PacketActionAgree);
PacketChar.AddByte(255);
Session.BuildCharacterPacket(PacketChar);
PacketChar.AddByte(255);
PacketChar.AddInt1(1);
Session.Send(PacketShow);
Send(PacketChar);
end;{1:}
-1:
begin
Log('remove ' + Str(Session.ID));
PacketChar.Reset;
PacketChar.SetID(PacketFamilyPlayers, PacketActionRemove);
PacketChar.AddInt2(Session.ID);
Session.Send(PacketHide);
Send(PacketChar);
end;{-1:}
else
Session.Send(PacketWalk);
end;{case State}
end;{if Session <> Self}
finally
ThreadSafeDone;
end;{try...finally}
Result := True;
end;{TSession.Walk}
function TSession.Face;
var
Packet: TPacket;
begin
if (Direction < 0) or (Direction > 3) then
begin
Log('Invalid face direction ' + Str(Direction));
exit(False);
end;{if (Direction...}
D := Direction;
Packet.SetID(PacketFamilyFace, PacketActionPlayer);
Packet.AddInt2(ID);
Packet.AddInt1(D);
SendInRange(Packet);
Result := True;
end;{TSession.Face}
function TSession.Say;
var
Packet: TPacket;
begin
if length(Text) = 0 then exit(False);
Packet.SetID(PacketFamilyTalk, PacketActionPlayer);
Packet.AddInt2(ID);
Packet.AddString(Text);
SendInRange(Packet);
Result := True;
end;{TSession.Say}
function TSession.Execute;
const
BufSize = 1024;
var
i: Integer;
Size: Integer;
ReadLen: Integer;
ReadBuf: AnsiString;
begin
if (Socket = 0) or (recv(Socket, nil^, 0, MSG_OOB) = 0) then exit(False);
if ioctlsocket(Socket, FIONREAD, i) = 0 then
begin
SetLength(ReadBuf, BufSize);
repeat
ReadLen := recv(Socket, ReadBuf[1], BufSize, 0);
if ReadLen < 1 then break;
Buffer := Buffer + copy(ReadBuf, 1, ReadLen);
until False;
end;{if ioctlsocket}
if length(Buffer) < 2 then exit(True);
Size := PackEOInt(ord(Buffer[1]), ord(Buffer[2]));
if length(Buffer) < (Size + 2) then exit(True);
PacketIn.Data := copy(Buffer, 3, Size);
Buffer := copy(Buffer, Size + 3, length(Buffer));
if Size < 3 then exit(true);
if Initialized then
begin
ReadBuf := '';
i := 1;
while i <= length(PacketIn.Data) do
begin
ReadBuf := ReadBuf + AnsiChar(ord(PacketIn.Data[i]) xor $80);
inc(i, 2);
end;{while i <= length(PacketIn.Data)}
dec(i);
if Boolean(length(PacketIn.Data) mod 2) then dec(i, 2);
repeat
ReadBuf := ReadBuf + AnsiChar(ord(PacketIn.Data[i]) xor $80);
dec(i, 2);
until i <= 0;
for i := 3 to length(PacketIn.Data) do
if ReadBuf[i] = #128 then ReadBuf[i] := #0
else if ReadBuf[i] = #0 then ReadBuf[i] := #128;
PacketIn.Data := FoldData(ReadBuf, RecieveKey);
end;{if Initialized}
PacketIn.Family := ord(PacketIn.Data[2]);
PacketIn.Action := ord(PacketIn.Data[1]);
PacketIn.Data := copy(PacketIn.Data, 3, length(PacketIn.Data));
if PacketIn.Family <> PacketFamilyRaw then
begin
// Sequence
PacketIn.GetByte;
end;{if PackeyIn.Family}
PacketOut.Reset;
PacketOut.SetID(PacketIn.Family, PacketActionReply);
//Log('Packet family:' + Str(PacketIn.Family) + ' action:' + Str(PacketIn.Action));
i := PacketIn.Family;
Dispatch(i);
Result := Initialized;
end;{TSession.Execute}
procedure TSession.DefaultHandler;
begin
{$IFNDEF SILENTUNHANDLED}
Log('Unhandled packet family ' + Str(PacketIn.Family));
{$ENDIF SILENTUNHANDLED}
end;{TSession.DefaultHandler}
procedure TSession.HandleRaw;
function AuthClient(Auth: Integer): Integer;
begin
inc(Auth);
Result := (Auth mod 11 + 1) * 119;
if Result = 0 then exit;
Result := 110905 + (Auth mod 9 + 1) * ((11092004 - Auth) mod Result) * 119 + Auth mod 2004;
end;
var
Auth: Integer;
s1, s2: Byte;
Ver: array[0..2] of Byte;
Seq: Byte;
begin
PacketOut.SetID(PacketFamilyRaw, PacketActionRaw);
Auth := PacketIn.GetInt3;
Ver[0] := PacketIn.GetInt1;
Ver[1] := PacketIn.GetInt1;
Ver[2] := PacketIn.GetInt1;
if (Ver[0] < RequiredVersion[0])
or (Ver[1] < RequiredVersion[1])
or (Ver[2] < RequiredVersion[2]) then
begin
PacketOut.AddByte(1);
PacketOut.AddByte(RequiredVersion[0] + 1);
PacketOut.AddByte(RequiredVersion[1] + 1);
PacketOut.AddByte(RequiredVersion[2] + 1);
Log('Invalid client version');
Send(PacketOut, True);
exit;
end;{if (Ver...}
PacketIn.Discard(2);
HDDSerial := PacketIn.GetString;
PacketOut.AddByte(2);
Seq := 1 + Random(220);
s1 := (Seq + 12) div 7;
s2 := (Seq + 5) mod 7;
PacketOut.AddByte(s1);
PacketOut.AddByte(s2);
Log('Initialized s1:' + Str(s1) + ' s2:' + Str(s2));
PacketOut.AddByte(SendKey);
PacketOut.AddByte(RecieveKey);
PacketOut.AddInt2(ID);
PacketOut.AddInt3(AuthClient(Auth));
Send(PacketOut, True);
Initialized := True;
end;{TSession.HandleRaw}
procedure TSession.HandleConnection;
procedure HandleConnectionAccept;
begin
SendGameData(ItemData);
SendGameData(NPCData);
SendGameData(SpellData);
SendGameData(ClassData);
// For some reason the client can't handle all this data at once, slow down a bit
Sleep(500);
SendGameState;
end;{HandleConnectionAccept}
procedure HandleConnectionPing;
begin
{}
end;{HandleConnectionPing}
begin
case PacketIn.Action of
PacketActionAccept: HandleConnectionAccept;
else
{$IFNDEF SILENTUNHANDLED}
Log('Unhandled connection action ' + Str(PacketIn.Action));
{$ENDIF SILENTUNHANDLED}
end;{case PacketIn.Action}
end;{TSession.HandleConnection}
procedure TSession.HandleGameData;
procedure HandleGameDataAgree;
var
FileID: Byte;
begin
FileID := PacketIn.GetInt1;
case FileID of
FileIDMap: SendGameData(MapData);
FileIDItem: SendGameData(ItemData);
FileIDNPC: SendGameData(NPCData);
FileIDSpell: SendGameData(SpellData);
FileIDClass: SendGameData(ClassData);
else
Log('Unknown file ID ' + Str(FileID));
end;{case FileID}
end;{HandleGameDataAgree}
procedure HandleGameDataMessage;
var
i: Integer;
Session: TSession;
begin
PacketOut.AddInt2(2);
PacketOut.AddByte(255);
PacketOut.AddBreakString('MEOW');
for i := 0 to 6 do
PacketOut.AddBreakString('');
PacketOut.AddByte(255);
PacketOut.AddInt1(0); // Weight
PacketOut.AddInt1(50); // Max weight
PacketOut.AddByte(255); // Inventory
PacketOut.AddByte(255); // Spells
ThreadSafe;
try
PacketOut.AddInt1(length(Sessions));
PacketOut.AddByte(255);
for Session in Sessions do
begin
Session.BuildCharacterPacket(PacketOut);
PacketOut.AddByte(255);
end;{for Session in Sessions}
finally
ThreadSafeDone;
end;{try...finally}
PacketOut.AddByte(255); // NPCs
//PacketOut.AddByte(255); // Items
Send(PacketOut);
PacketOut.Reset;
PacketOut.SetID(PacketFamilyPlayers, PacketActionAgree);
PacketOut.AddByte(255);
BuildCharacterPacket(PacketOut);
PacketOut.AddInt1(1);
PacketOut.AddByte(255);
PacketOut.AddInt1(1);
SendAll(PacketOut, Self);
end;{HandleGameDataMessage}
begin
case PacketIn.Action of
PacketActionRequest: SendGameState;
PacketActionAgree: HandleGameDataAgree;
PacketActionMessage: HandleGameDataMessage;
else
{$IFNDEF SILENTUNHANDLED}
Log('Unhandled game data action ' + Str(PacketIn.Action));
{$ENDIF SILENTUNHANDLED}
end;{case PacketIn.Action}
end;{TSession.HandleGameData}
procedure TSession.HandleWalk;
begin
case PacketIn.Action of
PacketActionPlayer: Walk(PacketIn.GetInt1, False);
PacketActionSpecial: Walk(PacketIn.GetInt1, False, True);
PacketActionAdmin: Walk(PacketIn.GetInt1, True);
else
Log('Unhandled walk action ' + Str(PacketIn.Action));
end;{case Receive.Action}
end;{TSession.HandleWalk}
procedure TSession.HandleFace;
begin
if PacketIn.Action = PacketActionPlayer then
Face(PacketIn.GetInt1)
else
Log('Unhandled face action ' + Str(PacketIn.Action));
end;{TSession.HandleFace}
procedure TSession.HandleRequest;
var
RequestID: Integer;
Session: TSession;
begin
if PacketIn.Action <> PacketActionRequest then
begin
Log('Unhandled request action ' + Str(PacketIn.Action));
exit;
end;{if PacketIn.Action}
RequestID := PacketIn.GetInt2;
PacketOut.SetID(PacketFamilyPlayers, PacketActionRemove);
PacketOut.AddInt2(RequestID);
Send(PacketOut);
PacketOut.Reset;
PacketOut.SetID(PacketFamilyPlayers, PacketActionAgree);
PacketOut.AddByte(255);
ThreadSafe;
try
Session := GetSession(RequestID);
if Session = nil then exit;
Session.BuildCharacterPacket(PacketOut);
finally
ThreadSafeDone;
end;{try..finally}
PacketOut.AddInt1(1);
PacketOut.AddByte(255);
PacketOut.AddInt1(1);
Send(PacketOut);
end;{TSession.HandleRequest}
procedure TSession.HandleTalk;
procedure HandleTalkReport;
var
Text: AnsiString;
begin
Text := copy(PacketIn.GetBreakString, 1, 200);
if length(Text) = 0 then exit;
Say(Text);
end;{HandleTalkReport}
begin
case PacketIn.Action of
PacketActionReport: HandleTalkReport;
else
Log('Unhandled talk action ' + Str(PacketIn.Action));
end;{case PacketIn.Action}
end;{TSession.HandleTalk}
var
WSAData: TWSAData;
Socket: TSocket;
AddrIn: TSockAddrIn;
FDSet: TFDSet;
SockAddr: TSockAddr;
SockSize: Integer;
begin
try
Writeln('MEOW - "Mini EO? WOW!"');
Writeln('Created by Sordie out of pure bordem.');
Writeln;
Writeln('Loading item data');
ItemData := TItemData.Create('dat001.eif');
if length(ItemData.Data) = 0 then exit;
Writeln('Loading npc data');
NPCData := TNPCData.Create('dtn001.enf');
if length(NPCData.Data) = 0 then exit;
Writeln('Loading spell data');
SpellData := TSpellData.Create('dsl001.esf');
if length(SpellData.Data) = 0 then exit;
Writeln('Loading class data');
ClassData := TClassData.Create('dat001.ecf');
if length(ClassData.Data) = 0 then exit;
Writeln('Loading map data');
MapData := TMapData.Create('00001.emf');
if length(MapData.Data) = 0 then exit;
Writeln('Initializing WinSock');
WSAStartup(MakeLong(2, 2), WSAData);
Writeln('Creating server socket');
Socket := WinSock.socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if Socket = 0 then exit;
Writeln('Binding socket');
FillChar(AddrIn, sizeof(AddrIn), 0);
AddrIn.sin_family := AF_INET;
AddrIn.sin_addr.S_addr := inet_addr('0.0.0.0');
AddrIn.sin_port := htons(8078);
if bind(Socket, AddrIn, sizeof(AddrIn)) <> 0 then exit;
Writeln('Listening');
if listen(Socket, 0) <> 0 then exit;
repeat
sleep(1);
FDSet.fd_count := 1;
FDSet.fd_array[0] := Socket;
if select(0, @FDSet, nil, nil, nil) = 1 then
begin
SockSize := sizeof(SockAddr);
TSession.Create(accept(Socket, @SockAddr, @SockSize));
end;{if select}
until False;
closesocket(Socket);
Writeln('Success');
finally
Write('Done');
Readln;
end;{try...finally}
end.