(*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.php.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Andreas Schneider
 *)
unit UPacketHandlers;

interface

uses
  Classes, SysUtils, dzlib, UConfig, UNetState, UEnhancedMemoryStream, UEnums,
  ULinkedList, URegions, LConvEncoding, Language;

type
  TPacketProcessor = procedure(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
  TPacketProcessorMethod = procedure(ABuffer: TEnhancedMemoryStream; ANetState: TNetState) of object;

  { TPacketHandler }

  TPacketHandler = class(TObject)
    constructor Create(ALength: Cardinal; APacketProcessor: TPacketProcessor); overload;
    constructor Create(ALength: Cardinal; APacketProcessorMethod: TPacketProcessorMethod); overload;
    procedure Process(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
  protected
    FLength: Cardinal;
    FPacketProcessor: TPacketProcessor;
    FPacketProcessorMethod: TPacketProcessorMethod;
  published
    property PacketLength: Cardinal read FLength;
  end;

var
  PacketHandlers: array[0..$FF] of TPacketHandler;
  
function ValidateAccess(ANetState: TNetState; ALevel: TAccessLevel): Boolean; overload;
function ValidateAccess(ANetState: TNetState; ALevel: TAccessLevel; AX, AY: Cardinal): Boolean; overload;
procedure RegisterPacketHandler(AID: Byte; APacketHandler: TPacketHandler);

implementation

uses
  UCEDServer, UPackets, UConnectionHandling, UAdminHandling, UClientHandling;

function ValidateAccess(ANetState: TNetState; ALevel: TAccessLevel): Boolean;
begin
  Result := (ANetState.Account <> nil) and (ANetState.Account.AccessLevel >= ALevel);
end;

function ValidateAccess(ANetState: TNetState; ALevel: TAccessLevel; AX, AY: Cardinal): Boolean;
var
  i,j: Word;
  region: TRegion;
  rect: TRect;
begin
  if not ValidateAccess(ANetState, ALevel) then Exit(False);
  if (ANetState.Account.Regions.Count = 0) or
    (ANetState.Account.AccessLevel >= alAdministrator) then Exit(True); //no restrictions

  Result := False;
  for i := 0 to ANetState.Account.Regions.Count - 1 do
  begin
    region := Config.Regions.Find(ANetState.Account.Regions[i]);
    if region <> nil then
    begin
      for j := 0 to region.Areas.Count - 1 do
      begin
        rect := region.Areas.Rects[j];
        if (AX >= rect.Left) and
           (AX < rect.Right) and
           (AY >= rect.Top) and
           (AY < rect.Bottom) then
          Exit(True);
      end;
    end;
  end;
end;

procedure RegisterPacketHandler(AID: Byte; APacketHandler: TPacketHandler);
begin
  if Assigned(PacketHandlers[AID]) then FreeAndNil(PacketHandlers[AID]);
  PacketHandlers[AID] := APacketHandler;
end;

{ TPacketHandler }

constructor TPacketHandler.Create(ALength: Cardinal; APacketProcessor: TPacketProcessor);
begin
  inherited Create;
  FLength := ALength;
  FPacketProcessor := APacketProcessor;
  FPacketProcessorMethod := nil;
end;

constructor TPacketHandler.Create(ALength: Cardinal;
  APacketProcessorMethod: TPacketProcessorMethod);
begin
  inherited Create;
  FLength := ALength;
  FPacketProcessor := nil;
  FPacketProcessorMethod := APacketProcessorMethod;
end;

procedure TPacketHandler.Process(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
begin
  if Assigned(FPacketProcessor) then
    FPacketProcessor(ABuffer, ANetState)
  else if Assigned(FPacketProcessorMethod) then
    FPacketProcessorMethod(ABuffer, ANetState);
end;

procedure OnCompressedPacket(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
var
  uncompStream: TEnhancedMemoryStream;
  uncompBuffer: TDecompressionStream;
  targetSize: Cardinal;
  packetID: Byte;
begin
  targetSize := ABuffer.ReadCardinal;
  uncompBuffer := TDecompressionStream.Create(ABuffer);
  uncompStream := TEnhancedMemoryStream.Create;
  try
    uncompStream.CopyFrom(uncompBuffer, targetSize);
    uncompStream.Position := 0;
    packetID := uncompStream.ReadByte;
    if PacketHandlers[packetID] <> nil then
    begin
      if PacketHandlers[PacketID].PacketLength = 0 then
        uncompStream.Position := uncompStream.Position + 4;
      uncompStream.Lock(uncompStream.Position, uncompStream.Size - uncompStream.Position);
      PacketHandlers[PacketID].Process(uncompStream, ANetState);
      uncompStream.Unlock;
    end else
    begin
      Writeln(TimeStamp, GetText('UnkPack1'), packetID, GetText('UnkPack2') + ' ', ANetState.Socket.PeerAddress);
      ANetState.ReceiveQueue.Clear;
      CEDServerInstance.Disconnect(ANetState.Socket);
    end;
  finally
    if uncompBuffer <> nil then uncompBuffer.Free;
    if uncompStream <> nil then uncompStream.Free;
  end;
end;

procedure OnRequestBlocksPacket(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
var
  coords: TBlockCoordsArray;
begin
  if not ValidateAccess(ANetState, alView) then Exit;
  SetLength(coords, (ABuffer.Size - ABuffer.Position) div SizeOf(TBlockCoords));
  ABuffer.Read(coords[0], Length(coords) * SizeOf(TBlockCoords));
  CEDServerInstance.SendPacket(ANetState, TCompressedPacket.Create(TBlockPacket.Create(coords, ANetState)));
end;

procedure OnFreeBlockPacket(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
var
  x, y: Word;
  blockSubscriptions: TLinkedList;
begin
  if not ValidateAccess(ANetState, alView) then Exit;
  x := ABuffer.ReadWord;
  y := ABuffer.ReadWord;
  blockSubscriptions := CEDServerInstance.Landscape.BlockSubscriptions[X, Y];
  if blockSubscriptions <> nil then
  begin
    blockSubscriptions.Delete(ANetState);
    ANetState.Subscriptions.Remove(blockSubscriptions);
  end;
end;

procedure OnNoOpPacket(ABuffer: TEnhancedMemoryStream; ANetState: TNetState);
begin
  //no operation
end;

{$WARNINGS OFF}
var
  i: Integer;

initialization
  for i := 0 to $FF do
    PacketHandlers[i] := nil;
  PacketHandlers[$01] := TPacketHandler.Create(0, @OnCompressedPacket);
  PacketHandlers[$02] := TPacketHandler.Create(0, @OnConnectionHandlerPacket);
  PacketHandlers[$03] := TPacketHandler.Create(0, @OnAdminHandlerPacket);
  PacketHandlers[$04] := TPacketHandler.Create(0, @OnRequestBlocksPacket);
  PacketHandlers[$05] := TPacketHandler.Create(5, @OnFreeBlockPacket);
  //$06-$0B handled by landscape
  PacketHandlers[$0C] := TPacketHandler.Create(0, @OnClientHandlerPacket);
  //$0D handled by radarmap
  //$0E handled by landscape
  PacketHandlers[$FF] := TPacketHandler.Create(1, @OnNoOpPacket);
finalization
  for i := 0 to $FF do
    if PacketHandlers[i] <> nil then
      PacketHandlers[i].Free;
{$WARNINGS ON}
end.

