{( )) 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.