Pastebin

New pastes are no longer accepted · Stats

Latest Pastes

MEOW Namespace

{(
 )) MEOW.dpr - Mini EO? WOW!
((
 )) Created by Sordie Solomon-Smith out of bordem
((  http://sordie.com/
 )}

program MEOW;

{$APPTYPE CONSOLE}

uses
  Windows, WinSock;

type
  procedureref = reference to procedure;

  CriticalSectionHelper = record helper for TRTLCriticalSection
    procedure Create; inline;
    procedure Free;   inline;

    procedure Enter; inline;
    procedure Leave; inline;

    procedure Section(Code: procedureref); inline;
  end;{CriticalSectionHelper}

  Server = class abstract
    class var CriticalSection: TRTLCriticalSection;

    type TSession = class
      var Socket: TSocket;
      var IPStr:  AnsiString;
      var IPInt:  Integer;

      var Thread: THandle;

      var ID: Cardinal;

      constructor Create(ASocket: TSocket);
      destructor  Destroy; override;

      function Execute: Boolean;
    end;{Session}

    type TArray<T: class> = class
      var Items: array of T;

      constructor Create;
      destructor  Destroy; override;

      function Find  (Item: T): Integer;
      function Add   (Item: T): Integer;
      function Remove(Item: T): Integer;

      procedure Clear;
    end;{TArray<T>}

    class var Sessions: TArray<TSession>;
    class var Socket:   TSocket;

    class constructor Create;
    class destructor  Destroy;

    class procedure Reference;
    class procedure Main;

    class function GetSessionByID(ID: Cardinal): TSession;

    class procedure Log  (Params: array of const);
    class procedure Error(Params: array of const);
  end;{Server}

procedure CriticalSectionHelper.Create;
begin
  InitializeCriticalSection(Self);
end;{CriticalSectionHelper.Create}

procedure CriticalSectionHelper.Free;
begin
  DeleteCriticalSection(Self);
end;{CriticalSectionHelper.Free}

procedure CriticalSectionHelper.Enter;
begin
  EnterCriticalSection(Self);
end;{CriticalSectionHelper.Enter}

procedure CriticalSectionHelper.Leave;
begin
  LeaveCriticalSection(Self);
end;{CriticalSectionHelper.Leave}

procedure CriticalSectionHelper.Section(Code: procedureref);
begin
  Enter;
  try
    Code;
  finally
    Leave;
  end;{try...finally}
end;{CriticalSectionHelper.Secion}

class constructor Server.Create;
var
  WSAData: TWSAData;
  AddrIn:  TSockAddrIn;
begin
  CriticalSection.Create;
  Log(['Server.Create']);

  Sessions := TArray<TSession>.Create;

  WSAStartup(MakeLong(2, 2), WSAData);

  Socket := WinSock.socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if Socket = 0 then
    Error(['Failed to create socket']);

  FillChar(AddrIn, sizeof(AddrIn), 0);
  with AddrIn do
  begin
    sin_family      := AF_INET;
    sin_addr.S_addr := inet_addr('0.0.0.0');
    sin_port        := htons(8078);
  end;{with AddrIn}
  if bind(Socket, AddrIn, sizeof(AddrIn)) <> 0 then
    Error(['Failed to bind socket']);

  if listen(Socket, 0) <> 0 then
    Error(['Cannot listen on socket']);

  try
    Main;
  except
    Log(['Server Exception']);
  end;{try...except}
end;{class)Server.Create}

class destructor Server.Destroy;
begin
  if Socket <> 0 then
  begin
    closesocket(Socket);
    Socket := 0;
  end;{if Socket <> 0}

  Sessions.Free;

  Log(['Server.Destroy']);

  CriticalSection.Free;
  Readln;
end;{class)Server.Destroy}

class procedure Server.Reference;
begin
  ClassName
end;{class)Server.Reference}

class procedure Server.Main;
var
  FDSet:    TFDSet;
  SockSize: Integer;
  SockAddr: TSockAddr;
begin
  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 Socket = 0;
end;{class)Server.Main}

class function Server.GetSessionByID(ID: Cardinal): TSession;
var
  Session: TSession;
begin
  CriticalSection.Enter;
  try
    for Session in Sessions.Items do
      if Session.ID = ID then exit(Session);

    Result := nil;
  finally
    CriticalSection.Leave;
  end;{try...finally}
end;{class)Server.GetSessionByID}

class procedure Server.Log(Params: array of const);
var
  i: Integer;
begin
  CriticalSection.Enter;
  try
    for i := 0 to high(Params) do
      with TVarRec(Params[i]) do
        case VType of
          vtInteger:       Write(VInteger);
          vtBoolean:       Write(VBoolean);
          vtChar:          Write(VChar);
          vtWideChar:      Write(VWideChar);
          vtExtended:      Write(VExtended^);
          vtString:        Write(AnsiString(VString));
          vtPointer:       Write(Cardinal(VPointer));
          vtPChar:         Write(AnsiString(VPChar));
          vtObject:        Write(VObject.ClassName);
          vtClass:         Write(VClass.ClassName);
          vtPWideChar:     Write(WideString(VPWideChar));
          vtWideString:    Write(WideString(VWideString));
          vtInt64:         Write(VInt64^);
          vtUnicodeString: Write(String(VUnicodeString));
        else
          Write('?(', VType, ')');
        end;{case VType}
  finally
    Writeln;
    CriticalSection.Leave;
  end;{try...finally}
end;{class)Server.Log}

class procedure Server.Error(Params: array of const);
begin
  Log(Params);
  halt(1);
end;{class)Server.Error}

constructor Server.TArray<T>.Create;
begin
  inherited Create;

  Clear;
end;{Server.TArray<T>.Create}

destructor Server.TArray<T>.Destroy;
begin
  Clear;

  inherited;
end;{Server.TArray<T>.Destroy}

function Server.TArray<T>.Find(Item: T): Integer;
var
  i: Integer;
begin
  for i := 0 to high(Items) do
    if Items[i] = Item then exit(i);

  Result := -1;
end;{Server.TArray<T>.Add}

function Server.TArray<T>.Add(Item: T): Integer;
begin
  Result := Find(Item);

  if Result = -1 then
  begin
    SetLength(Items, length(Items) + 1);
    Result := high(Items);
    Items[Result] := Item;
  end;{if Result = -1}
end;{Server.TArray<T>.Add}

function Server.TArray<T>.Remove(Item: T): Integer;
begin
  Result := Find(Item);
  if Result = -1 then exit;

  if Result < high(Items) then
    move(Items[Result + 1], Items[Result], sizeof(T) * (length(Items) - 1));

  SetLength(Items, length(Items) - 1);
end;{Server.Tarray<T>.Remove}

procedure Server.TArray<T>.Clear;
begin
  SetLength(Items, 0);
end;{Server.TArray<T>.Clear}

function SessionThread(Session: Server.TSession): Integer;
begin
  Result := 0;

  try
    try
      while Session.Execute do
        Sleep(1);
    except
      Server.Log(['Session Exception']);
    end;{try...except}
  finally
    try
      Session.Free;
    except
      {}
    end;{try...except}

    EndThread(Result);
  end;{try...finally}
end;{SessionThread}

constructor Server.TSession.Create(ASocket: TSocket);
var
  i:    Integer;
  Addr: TSockAddr;
begin
  inherited Create;

  Socket := ASocket;

  if Socket <> 0 then
  begin
    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;

    i := 1;
    ioctlsocket(Socket, FIONBIO, i);
  end;{if Socket}

  Server.CriticalSection.Section(procedure
  begin
    ID := 100;
    while Server.GetSessionByID(ID) <> nil do inc(ID);

    Server.Sessions.Add(Self);
  end);{Server.CriticalSection.Section}

  BeginThread(nil, 0, @SessionThread, Pointer(Self), 0, Thread);

  Server.Log(['Session[', ID, '] ', IPStr, ' created']);
end;{Server.TSession.Create}

destructor Server.TSession.Destroy;
begin
  if Socket <> 0 then
  begin
    closesocket(Socket);
    Socket := 0;
  end;{if Socket <> 0}

  Server.CriticalSection.Section(procedure
  begin
    Server.Sessions.Remove(Self);
  end);{Server.CriticalSection.Section}

  Server.Log(['Session[', ID, '] ', IPStr, ' destroyed']);

  inherited;
end;{Server.TSession.Destroy}

function Server.TSession.Execute: Boolean;
begin
  if (Socket = 0) or (recv(Socket, nil^, 0, MSG_OOB) = 0) then exit(False);

  Result := True;
end;{Server.TSession.Execute}

begin
  Server.Reference;
end.