diff --git a/Server/ULandscape.pas b/Server/ULandscape.pas index 5b7062d..7541fcb 100644 --- a/Server/ULandscape.pas +++ b/Server/ULandscape.pas @@ -1,1101 +1,1111 @@ -(* - * 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 ULandscape; - -{$mode objfpc}{$H+} - -interface - -uses - SysUtils, Classes, contnrs, UGenericIndex, UMap, UStatics, UTiledata, - UWorldItem, UMulBlock, math, - UTileDataProvider, URadarMap, - UListSort, UCacheManager, ULinkedList, UBufferedStreams, - UEnhancedMemoryStream, UPacketHandlers, UPackets, UNetState, UEnums; - -type - PRadarBlock = ^TRadarBlock; - TRadarBlock = array[0..7, 0..7] of Word; - TBlockSubscriptions = array of TLinkedList; - - { TBlock } - - TBlock = class(TObject) - constructor Create(AMap: TMapBlock; AStatics: TStaticBlock); - destructor Destroy; override; - protected - FMapBlock: TMapBlock; - FStaticBlock: TStaticBlock; - public - property Map: TMapBlock read FMapBlock; - property Static: TStaticBlock read FStaticBlock; - end; - - { TLandscape } - - TLandscape = class(TObject) - constructor Create(AMap, AStatics, AStaIdx, ATiledata, ARadarCol: string; - AWidth, AHeight: Word; var AValid: Boolean); - constructor Create(AMap, AStatics, AStaIdx, ATiledata: TStream; - ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); - destructor Destroy; override; - protected - FWidth: Word; - FHeight: Word; - FCellWidth: Word; - FCellHeight: Word; - FMap: TStream; - FStatics: TStream; - FStaIdx: TStream; - FTiledata: TStream; - FTiledataProvider: TTiledataProvider; - FOwnsStreams: Boolean; - FRadarMap: TRadarMap; - FBlockCache: TCacheManager; - FBlockSubscriptions: TBlockSubscriptions; - function Compare(left, right: TObject): Integer; - procedure OnBlockChanged(ABlock: TMulBlock); - procedure OnRemoveCachedObject(AObject: TObject); - function GetMapCell(AX, AY: Word): TMapCell; - function GetStaticList(AX, AY: Word): TList; - function GetBlockSubscriptions(AX, AY: Word): TLinkedList; - procedure UpdateStaticsPriority(AStaticItem: TStaticItem; - APrioritySolver: Integer); - - procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - procedure OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); - public - property Width: Word read FWidth; - property Height: Word read FHeight; - property CellWidth: Word read FCellWidth; - property CellHeight: Word read FCellHeight; - property MapCell[X, Y: Word]: TMapCell read GetMapCell; - property StaticList[X, Y: Word]: TList read GetStaticList; - property BlockSubscriptions[X, Y: Word]: TLinkedList read GetBlockSubscriptions; - property TiledataProvider: TTiledataProvider read FTiledataProvider; - - function GetMapBlock(AX, AY: Word): TMapBlock; - function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock; - function LoadBlock(AX, AY: Word): TBlock; - - procedure UpdateRadar(AX, AY: Word); - function GetEffectiveAltitude(ATile: TMapCell): ShortInt; - function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt; - procedure SortStaticsList(AStatics: TList); - - procedure Flush; - procedure SaveBlock(AWorldBlock: TWorldBlock); - function Validate: Boolean; - end; - - TStaticInfo = packed record - X: Word; - Y: Word; - Z: ShortInt; - TileID: Word; - Hue: Word; - end; - TAreaInfo = packed record - Left: Word; - Top: Word; - Right: Word; - Bottom: Word; - end; - TWorldPoint = packed record - X: Word; - Y: Word; - end; - -function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean; inline; - -implementation - -uses - UCEDServer, UConnectionHandling, UConfig, ULargeScaleOperations; - -const - mMap = 0; - mStatics = 1; - -function GetID(AX, AY: Word): Integer; -begin - Result := ((AX and $7FFF) shl 15) or (AY and $7FFF); -end; - -function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean; -begin - Result := InRange(AX, AArea.Left, AArea.Right) and - InRange(AY, AArea.Top, AArea.Bottom); -end; - -{ TBlock } - -constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock); -begin - inherited Create; - FMapBlock := AMap; - FStaticBlock := AStatics; -end; - -destructor TBlock.Destroy; -begin - if FMapBlock <> nil then FreeAndNil(FMapBlock); - if FStaticBlock <> nil then FreeAndNil(FStaticBlock); - inherited Destroy; -end; - -{ TLandscape } - -constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata, - ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); -begin - Create(TFileStream.Create(AMap, fmOpenReadWrite), - TFileStream.Create(AStatics, fmOpenReadWrite), - TBufferedReader.Create(TFileStream.Create(AStaIdx, fmOpenReadWrite), True), - TFileStream.Create(ATiledata, fmOpenRead or fmShareDenyWrite), - ARadarCol, AWidth, AHeight, AValid); - FOwnsStreams := True; -end; - -constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata: TStream; - ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); -var - blockID, blockType: Integer; -begin - inherited Create; - FWidth := AWidth; - FHeight := AHeight; - FCellWidth := FWidth * 8; - FCellHeight := FHeight * 8; - FMap := AMap; - FStatics := AStatics; - FStaIdx := AStaIdx; - FTiledata := ATiledata; - FOwnsStreams := False; - AValid := Validate; - if AValid then - begin - FBlockCache := TCacheManager.Create(256); - FBlockCache.OnRemoveObject := @OnRemoveCachedObject; - FTiledataProvider := TTiledataProvider.Create(ATiledata); - SetLength(FBlockSubscriptions, AWidth * AHeight); - for blockID := 0 to AWidth * AHeight - 1 do - FBlockSubscriptions[blockID] := TLinkedList.Create; - - FRadarMap := TRadarMap.Create(FMap, FStatics, FStaIdx, FWidth, FHeight, - ARadarCol); - - RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket)); - RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket)); - RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket)); - RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket)); - RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket)); - RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket)); - RegisterPacketHandler($0E, TPacketHandler.Create(0, @OnLargeScaleCommandPacket)); - end; -end; - -destructor TLandscape.Destroy; -var - i: Integer; -begin - for i := 0 to Length(FBlockSubscriptions) - 1 do - if FBlockSubscriptions[i] <> nil then FreeAndNil(FBlockSubscriptions[i]); - if FBlockCache <> nil then FreeAndNil(FBlockCache); - if FTiledataProvider <> nil then FreeAndNil(FTiledataProvider); - if FRadarMap <> nil then FreeAndNil(FRadarMap); - if FOwnsStreams then - begin - if FMap <> nil then FreeAndNil(FMap); - if FStatics <> nil then FreeAndNil(FStatics); - if FStaIdx <> nil then FreeAndNil(FStaIdx); - if FTiledata <> nil then FreeAndNil(FTiledata); - end; - - RegisterPacketHandler($06, nil); - RegisterPacketHandler($07, nil); - RegisterPacketHandler($08, nil); - RegisterPacketHandler($09, nil); - RegisterPacketHandler($0A, nil); - RegisterPacketHandler($0B, nil); - RegisterPacketHandler($0E, nil); - - inherited Destroy; -end; - -function TLandscape.GetBlockSubscriptions(AX, AY: Word): TLinkedList; -begin - if (AX >= 0) and (AX <= FWidth) and (AY >= 0) and (AY <= FHeight) then - Result := FBlockSubscriptions[(AY * FWidth) + AX] - else - Result := nil; -end; - -function TLandscape.GetMapCell(AX, AY: Word): TMapCell; -var - block: TMapBlock; -begin - Result := nil; - if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then - begin - block := GetMapBlock(AX div 8, AY div 8); - if block <> nil then - Result := block.Cells[(AY mod 8) * 8 + AX mod 8]; - end; -end; - -function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt; -begin - if (AX >= 0) and (AX < FCellWidth) and (AY >= 0) and (AY < FCellHeight) then - Result := MapCell[AX, AY].Altitude - else - Result := ADefault; -end; - -function TLandscape.GetStaticList(AX, AY: Word): TList; -var - block: TSeperatedStaticBlock; -begin - Result := nil; - if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then - begin - block := GetStaticBlock(AX div 8, AY div 8); - if block <> nil then - Result := block.Cells[(AY mod 8) * 8 + AX mod 8]; - end; -end; - -function TLandscape.Compare(left, right: TObject): Integer; -begin - Result := TWorldItem(right).Priority - TWorldItem(left).Priority; - if Result = 0 then - begin - if (left is TMapCell) and (right is TStaticItem) then - Result := 1 - else if (left is TStaticItem) and (right is TMapCell) then - Result := -1; - end; - - if Result = 0 then - Result := TWorldItem(right).PriorityBonus - TWorldItem(left).PriorityBonus; - - if Result = 0 then - Result := TWorldItem(right).PrioritySolver - TWorldItem(left).PrioritySolver; -end; - -procedure TLandscape.UpdateRadar(AX, AY: Word); -var - mapTile: TMapCell; - tile: TWorldItem; - staticItems, tiles: TList; - i: Integer; -begin - if (AX mod 8 = 0) and (AY mod 8 = 0) then - begin - staticItems := GetStaticList(AX, AY); - if staticItems <> nil then - begin - tiles := TList.Create; - mapTile := GetMapCell(AX, AY); - if mapTile <> nil then - begin - mapTile.Priority := GetEffectiveAltitude(mapTile); - mapTile.PriorityBonus := 0; - mapTile.PrioritySolver := 0; - tiles.Add(mapTile); - end; - for i := 0 to staticItems.Count - 1 do - begin - UpdateStaticsPriority(TStaticItem(staticItems.Items[i]), i + 1); - tiles.Add(staticItems.Items[i]); - end; - ListSort(tiles, @Compare); - - if tiles.Count > 0 then - begin - tile := TWorldItem(tiles.Items[tiles.Count - 1]); - if tile is TStaticItem then - FRadarMap.Update(AX div 8, AY div 8, tile.TileID + $4000) - else - FRadarMap.Update(AX div 8, AY div 8, tile.TileID) - end; - - tiles.Free; - end; - end; -end; - -procedure TLandscape.SortStaticsList(AStatics: TList); -var - i: Integer; -begin - for i := 0 to AStatics.Count - 1 do - UpdateStaticsPriority(TStaticItem(AStatics.Items[i]), i + 1); - ListSort(AStatics, @Compare); -end; - -function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt; -var - north, west, south, east: ShortInt; -begin - north := ATile.Altitude; - west := GetLandAlt(ATile.X, ATile.Y + 1, north); - south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north); - east := GetLandAlt(ATile.X + 1, ATile.Y, north); - - if Abs(north - south) > Abs(west - east) then - Result := (north + south) div 2 - else - Result := (west + east) div 2; -end; - -procedure TLandscape.OnBlockChanged(ABlock: TMulBlock); -begin - // Do nothing for now -end; - -procedure TLandscape.OnRemoveCachedObject(AObject: TObject); -var - block: TBlock; -begin - block := AObject as TBlock; - if block <> nil then - begin - if block.Map.Changed then SaveBlock(block.Map); - if block.Static.Changed then SaveBlock(block.Static); - end; -end; - -function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock; -var - block: TBlock; -begin - Result := nil; - if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then - begin - if FBlockCache.QueryID(GetID(AX, AY), TObject(block)) then - Result := block.Map - else - Result := LoadBlock(AX, AY).Map; - end; -end; - -function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock; -var - block: TBlock; -begin - Result := nil; - if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then - begin - if FBlockCache.QueryID(GetID(AX, AY), TObject(block)) then - Result := TSeperatedStaticBlock(block.Static) - else - Result := TSeperatedStaticBlock(LoadBlock(AX, AY).Static); - end; -end; - -function TLandscape.LoadBlock(AX, AY: Word): TBlock; -var - map: TMapBlock; - statics: TStaticBlock; - index: TGenericIndex; -begin - FMap.Position := ((AX * FHeight) + AY) * 196; - map := TMapBlock.Create(FMap, AX, AY); - map.OnChanged := @OnBlockChanged; - - FStaIdx.Position := ((AX * FHeight) + AY) * 12; - index := TGenericIndex.Create(FStaIdx); - statics := TSeperatedStaticBlock.Create(FStatics, index, AX, AY); - statics.OnChanged := @OnBlockChanged; - index.Free; - - Result := TBlock.Create(map, statics); - FBlockCache.StoreID(GetID(AX, AY), Result); -end; - -//Intelligent write: replace if possible, otherwise extend - -procedure TLandscape.Flush; -{var - blockID, blockType: Integer; - worldBlock: TWorldBlock; - index: TGenericIndex; - i, j, size: Integer;} -begin - {for blockID := 0 to FWidth * FHeight - 1 do - begin - for blockType := 0 to 1 do - begin - worldBlock := FPersistentBlocks[blockID][blockType]; - if Assigned(worldBlock) and worldBlock.Changed then - begin - if worldBlock is TMapBlock then - begin - FMap.Position := ((worldBlock.X * FHeight) + worldBlock.Y) * 196; - worldBlock.Write(FMap); - for i := 0 to 63 do - TMapBlock(worldBlock).Cells[i].InitOriginalState; - worldBlock.CleanUp; - end else if worldBlock is TStaticBlock then - begin - FStaIdx.Position := ((worldBlock.X * FHeight) + worldBlock.Y) * 12; - index := TGenericIndex.Create(FStaIdx); - size := worldBlock.GetSize; - if (size > index.Size) or (index.Lookup = LongInt($FFFFFFFF)) then - begin - FStatics.Position := FStatics.Size; - index.Lookup := FStatics.Position; - end; - if size = 0 then - index.Lookup := LongInt($FFFFFFFF) - else - begin - index.Size := size; - FStatics.Position := index.Lookup; - worldBlock.Write(FStatics); - end; - FStaIdx.Seek(-12, soFromCurrent); - index.Write(FStaIdx); - index.Free; - for i := 0 to 63 do - for j := 0 to TSeperatedStaticBlock(worldBlock).Cells[i].Count - 1 do - TStaticItem(TSeperatedStaticBlock(worldBlock).Cells[i].Items[j]).InitOriginalState; - worldBlock.CleanUp; - end; - end; - end; - end;} - FBlockCache.Clear; -end; - -procedure TLandscape.SaveBlock(AWorldBlock: TWorldBlock); -var - i, j, size: Integer; - index: TGenericIndex; -begin - if AWorldBlock is TMapBlock then - begin - FMap.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 196; - AWorldBlock.Write(FMap); - for i := 0 to 63 do - TMapBlock(AWorldBlock).Cells[i].InitOriginalState; - AWorldBlock.CleanUp; - end else if AWorldBlock is TStaticBlock then - begin - FStaIdx.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 12; - index := TGenericIndex.Create(FStaIdx); - size := AWorldBlock.GetSize; - if (size > index.Size) or (index.Lookup = LongInt($FFFFFFFF)) then - begin - FStatics.Position := FStatics.Size; - index.Lookup := FStatics.Position; - end; - if size = 0 then - index.Lookup := LongInt($FFFFFFFF) - else - begin - index.Size := size; - FStatics.Position := index.Lookup; - AWorldBlock.Write(FStatics); - end; - FStaIdx.Seek(-12, soFromCurrent); - index.Write(FStaIdx); - index.Free; - for i := 0 to 63 do - for j := 0 to TSeperatedStaticBlock(AWorldBlock).Cells[i].Count - 1 do - TStaticItem(TSeperatedStaticBlock(AWorldBlock).Cells[i].Items[j]).InitOriginalState; - AWorldBlock.CleanUp; - end; -end; - -function TLandscape.Validate: Boolean; -var - blocks: Integer; -begin - blocks := FWidth * FHeight; - FStaIdx.Seek(0, soFromEnd); //workaround for TBufferedStream - Result := (FMap.Size = (blocks * 196)) and (FStaIdx.Position = (blocks * 12)); -end; - -procedure TLandscape.UpdateStaticsPriority(AStaticItem: TStaticItem; - APrioritySolver: Integer); -var - staticTileData: TStaticTileData; -begin - staticTileData := FTiledataProvider.StaticTiles[AStaticItem.TileID]; - AStaticItem.PriorityBonus := 0; - if not ((staticTileData.Flags and tdfBackground) = tdfBackground) then - AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1; - if staticTileData.Height > 0 then - AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1; - AStaticItem.Priority := AStaticItem.Z + AStaticItem.PriorityBonus; - AStaticItem.PrioritySolver := APrioritySolver; -end; - -procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - x, y: Word; - cell: TMapCell; - subscriptions: TLinkedList; - item: PLinkedItem; - packet: TDrawMapPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - x := ABuffer.ReadWord; - y := ABuffer.ReadWord; - cell := GetMapCell(x, y); - if cell <> nil then - begin - cell.Altitude := ABuffer.ReadShortInt; - cell.TileID := ABuffer.ReadWord; - - packet := TDrawMapPacket.Create(cell); - subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)]; - item := nil; - while subscriptions.Iterate(item) do - CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); - packet.Free; - - UpdateRadar(x, y); - end; -end; - -procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - x, y: Word; - block: TSeperatedStaticBlock; - staticItem: TStaticItem; - targetStaticList: TList; - i: Integer; - subscriptions: TLinkedList; - item: PLinkedItem; - packet: TInsertStaticPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - x := ABuffer.ReadWord; - y := ABuffer.ReadWord; - block := GetStaticBlock(x div 8, y div 8); - if block <> nil then - begin - staticItem := TStaticItem.Create(nil, nil, 0, 0); - staticItem.X := x; - staticItem.Y := y; - staticItem.Z := ABuffer.ReadShortInt; - staticItem.TileID := ABuffer.ReadWord; - staticItem.Hue := ABuffer.ReadWord; - targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8]; - targetStaticList.Add(staticItem); - SortStaticsList(targetStaticList); - staticItem.Owner := block; - - packet := TInsertStaticPacket.Create(staticItem); - subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)]; - item := nil; - while subscriptions.Iterate(item) do - CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); - packet.Free; - - UpdateRadar(x, y); - end; -end; - -procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - block: TSeperatedStaticBlock; - i: Integer; - statics: TList; - staticInfo: TStaticInfo; - staticItem: TStaticItem; - subscriptions: TLinkedList; - item: PLinkedItem; - packet: TDeleteStaticPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); - block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); - if block <> nil then - begin - statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; - for i := 0 to statics.Count - 1 do - begin - staticItem := TStaticItem(statics.Items[i]); - if (staticItem.Z = staticInfo.Z) and - (staticItem.TileID = staticInfo.TileID) and - (staticItem.Hue = staticInfo.Hue) then - begin - packet := TDeleteStaticPacket.Create(staticItem); - - statics.Delete(i); - staticItem.Delete; - - subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; - item := nil; - while subscriptions.Iterate(item) do - CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); - packet.Free; - - UpdateRadar(staticInfo.X, staticInfo.Y); - - Break; - end; - end; - end; -end; - -procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - block: TSeperatedStaticBlock; - i, j: Integer; - statics: TList; - staticInfo: TStaticInfo; - staticItem: TStaticItem; - newZ: ShortInt; - subscriptions: TLinkedList; - item: PLinkedItem; - packet: TElevateStaticPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); - block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); - if block <> nil then - begin - statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; - for i := 0 to statics.Count - 1 do - begin - staticItem := TStaticItem(statics.Items[i]); - if (staticItem.Z = staticInfo.Z) and - (staticItem.TileID = staticInfo.TileID) and - (staticItem.Hue = staticInfo.Hue) then - begin - newZ := ABuffer.ReadShortInt; - packet := TElevateStaticPacket.Create(staticItem, newZ); - - staticItem.Z := newZ; - SortStaticsList(statics); - - subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; - item := nil; - while subscriptions.Iterate(item) do - CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); - packet.Free; - - UpdateRadar(staticInfo.X, staticInfo.Y); - - Break; - end; - end; - end; -end; - -procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - sourceBlock, targetBlock: TSeperatedStaticBlock; - sourceSubscriptions, targetSubscriptions: TList; - i: Integer; - statics: TList; - staticInfo: TStaticInfo; - staticItem: TStaticItem; - newX, newY: Word; - subscriptions: TLinkedList; - item: PLinkedItem; - insertPacket: TInsertStaticPacket; - deletePacket: TDeleteStaticPacket; - movePacket: TMoveStaticPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - staticItem := nil; - ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); - newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1); - newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1); - - if (staticInfo.X = newX) and (staticInfo.Y = newY) then Exit; - - if ((abs(staticInfo.X - newX) > 8) or (abs(staticInfo.Y - newY) > 8)) and - (not ValidateAccess(ANetState, alAdministrator)) then Exit; - - sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); - targetBlock := GetStaticBlock(newX div 8, newY div 8); - if (sourceBlock <> nil) and (targetBlock <> nil) then - begin - statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; - i := 0; - while (i < statics.Count) and (staticItem = nil) do - begin - staticItem := TStaticItem(statics.Items[i]); - if (staticItem.Z <> staticInfo.Z) or - (staticItem.TileID <> staticInfo.TileID) or - (staticItem.Hue <> staticInfo.Hue) then - begin - staticItem := nil; - end; - Inc(i); - end; - - if staticItem <> nil then - begin - deletePacket := TDeleteStaticPacket.Create(staticItem); - movePacket := TMoveStaticPacket.Create(staticItem, newX, newY); - - statics.Remove(staticItem); - statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8]; - statics.Add(staticItem); - staticItem.UpdatePos(newX, newY, staticItem.Z); - staticItem.Owner := targetBlock; - - insertPacket := TInsertStaticPacket.Create(staticItem); - - SortStaticsList(statics); - - sourceSubscriptions := TList.Create; - subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; - item := nil; - while subscriptions.Iterate(item) do - sourceSubscriptions.Add(item^.Data); - - targetSubscriptions := TList.Create; - subscriptions := FBlockSubscriptions[(newY div 8) * FWidth + (newX div 8)]; - item := nil; - while subscriptions.Iterate(item) do - targetSubscriptions.Add(item^.Data); - - for i := 0 to sourceSubscriptions.Count - 1 do - begin - if targetSubscriptions.IndexOf(sourceSubscriptions.Items[i]) > -1 then - CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), movePacket, False) - else - CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), deletePacket, False); - end; - - for i := 0 to targetSubscriptions.Count - 1 do - begin - if sourceSubscriptions.IndexOf(targetSubscriptions.Items[i]) = -1 then - CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), insertPacket, False); - end; - - UpdateRadar(staticInfo.X, staticInfo.Y); - UpdateRadar(newX, newY); - - insertPacket.Free; - deletePacket.Free; - movePacket.Free; - end; - end; -end; - -procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - block: TSeperatedStaticBlock; - i, j: Integer; - statics: TList; - staticInfo: TStaticInfo; - staticItem: TStaticItem; - newHue: Word; - subscriptions: TLinkedList; - item: PLinkedItem; - packet: THueStaticPacket; -begin - if not ValidateAccess(ANetState, alNormal) then Exit; - ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); - block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); - if block <> nil then - begin - statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; - for i := 0 to statics.Count - 1 do - begin - staticItem := TStaticItem(statics.Items[i]); - if (staticItem.Z = staticInfo.Z) and - (staticItem.TileID = staticInfo.TileID) and - (staticItem.Hue = staticInfo.Hue) then - begin - newHue := ABuffer.ReadWord; - packet := THueStaticPacket.Create(staticItem, newHue); - - staticItem.Hue := newHue; - - subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; - item := nil; - while subscriptions.Iterate(item) do - CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); - packet.Free; - - Break; - end; - end; - end; -end; - -procedure TLandscape.OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream; - ANetState: TNetState); -var - areaInfo: array of TAreaInfo; - areaCount: Byte; - i: Integer; - blockX, blockY, cellX, cellY, x, y: Word; - realBlockX, realBlockY, realCellX, realCellY: Word; - blockOffX, cellOffX, modX, blockOffY, cellOffY, modY: Integer; - blockID, cellID: Cardinal; - emptyBits: TBits; - bitMask: array of TBits; - mapTile: TMapCell; - statics: TList; - operations: TList; - clients: array of record - NetState: TNetState; - Blocks: TBlockCoordsArray; - end; - netState: TNetState; - subscriptions: TLinkedList; - item: PLinkedItem; - cmOperation: TLSCopyMove; - additionalAffectedBlocks: TBits; -begin - if not ValidateAccess(ANetState, alAdministrator) then Exit; - Writeln(TimeStamp, ANetState.Account.Name, ' begins large scale operation'); - CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssOther, - Format('%s is performing large scale operations ...', [ANetState.Account.Name]))); - - //Bitmask - emptyBits := TBits.Create(64); - SetLength(bitMask, FWidth * FHeight); - for i := Low(bitMask) to High(bitMask) do - bitMask[i] := TBits.Create(64); - additionalAffectedBlocks := TBits.Create(FWidth * FHeight); - - areaCount := ABuffer.ReadByte; - SetLength(areaInfo, areaCount); - for i := 0 to areaCount - 1 do - begin - areaInfo[i].Left := Max(ABuffer.ReadWord, 0); - areaInfo[i].Top := Max(ABuffer.ReadWord, 0); - areaInfo[i].Right := Min(ABuffer.ReadWord, FCellWidth - 1); - areaInfo[i].Bottom := Min(ABuffer.ReadWord, FCellHeight - 1); - for x := areaInfo[i].Left to areaInfo[i].Right do - for y := areaInfo[i].Top to areaInfo[i].Bottom do - begin - blockID := (x div 8) * FHeight + (y div 8); - cellID := (y mod 8) * 8 + (x mod 8); - bitMask[blockID].Bits[cellID] := True; - end; - end; - - //client blocks - SetLength(clients, 0); - CEDServerInstance.TCPServer.IterReset; - while CEDServerInstance.TCPServer.IterNext do - begin - netState := TNetState(CEDServerInstance.TCPServer.Iterator.UserData); - if netState <> nil then - begin - SetLength(clients, Length(clients) + 1); - clients[High(clients)].NetState := netState; - SetLength(clients[High(clients)].Blocks, 0); - end; - end; - - operations := TList.Create; - - cmOperation := nil; - if ABuffer.ReadBoolean then - begin - cmOperation := TLSCopyMove.Init(ABuffer, Self); - if (cmOperation.OffsetX <> 0) or (cmOperation.OffsetY <> 0) then - begin - operations.Add(cmOperation); - - if cmOperation.OffsetX > 0 then - begin - blockOffX := FWidth - 1; - cellOffX := 7; - modX := -1; - end else - begin - blockOffX := 0; - cellOffX := 0; - modX := 1; - end; - - if cmOperation.OffsetY > 0 then - begin - blockOffY := FHeight - 1; - cellOffY := 7; - modY := -1; - end else - begin - blockOffY := 0; - cellOffY := 0; - modY := 1; - end; - end else - FreeAndNil(cmOperation); - end; - if cmOperation = nil then - begin - blockOffX := 0; - cellOffX := 0; - modX := 1; - blockOffY := 0; - cellOffY := 0; - modY := 1; - end; - if ABuffer.ReadBoolean then operations.Add(TLSSetAltitude.Init(ABuffer, Self)); - if ABuffer.ReadBoolean then operations.Add(TLSDrawTerrain.Init(ABuffer, Self)); - if ABuffer.ReadBoolean then operations.Add(TLSDeleteStatics.Init(ABuffer, Self)); - if ABuffer.ReadBoolean then operations.Add(TLSInsertStatics.Init(ABuffer, Self)); - - FRadarMap.BeginUpdate; - for blockX := 0 to FWidth - 1 do - begin - realBlockX := blockOffX + modX * blockX; - for blockY := 0 to FHeight - 1 do - begin - realBlockY := blockOffY + modY * blockY; - blockID := (realBlockX * FHeight) + realBlockY; - if bitMask[blockID].Equals(emptyBits) then Continue; - - for cellY := 0 to 7 do - begin - realCellY := cellOffY + modY * cellY; - for cellX := 0 to 7 do - begin - realCellX := cellOffX + modX * cellX; - if bitMask[blockID].Bits[(realCellY * 8) + realCellX] then - begin - x := realBlockX * 8 + realCellX; - y := realBlockY * 8 + realCellY; - mapTile := GetMapCell(x, y); - statics := GetStaticList(x, y); - for i := 0 to operations.Count - 1 do - TLargeScaleOperation(operations.Items[i]).Apply(mapTile, statics, - additionalAffectedBlocks); - SortStaticsList(statics); - - UpdateRadar(x, y); - end; - end; - end; - - subscriptions := FBlockSubscriptions[realBlockY * FWidth + realBlockX]; - for i := Low(clients) to High(clients) do - begin - item := nil; - while subscriptions.Iterate(item) do - begin - if TNetState(item^.Data) = clients[i].NetState then - begin - SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1); - with clients[i].Blocks[High(clients[i].Blocks)] do - begin - X := realBlockX; - Y := realBlockY; - end; - Break; - end; - end; - end; - - end; - end; - - //additional blocks - for blockX := 0 to FWidth - 1 do - begin - for blockY := 0 to FHeight - 1 do - begin - blockID := (blockX * FHeight) + blockY; - if bitMask[blockID].Equals(emptyBits) and additionalAffectedBlocks[blockID] then - begin - subscriptions := FBlockSubscriptions[blockY * FWidth + blockX]; - for i := Low(clients) to High(clients) do - begin - item := nil; - while subscriptions.Iterate(item) do - begin - if TNetState(item^.Data) = clients[i].NetState then - begin - SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1); - with clients[i].Blocks[High(clients[i].Blocks)] do - begin - X := blockX; - Y := blockY; - end; - Break; - end; - end; - end; - - UpdateRadar(blockX * 8, blockY * 8); - - end; - end; - end; - - //clean up - for i := Low(bitMask) to High(bitMask) do - bitMask[i].Free; - emptyBits.Free; - additionalAffectedBlocks.Free; - - for i := 0 to operations.Count - 1 do - TLargeScaleOperation(operations.Items[i]).Free; - operations.Free; - - //Update clients - FRadarMap.EndUpdate; - for i := Low(clients) to High(clients) do - begin - if Length(clients[i].Blocks) > 0 then - begin - CEDServerInstance.SendPacket(clients[i].NetState, TCompressedPacket.Create( - TBlockPacket.Create(clients[i].Blocks, nil))); - clients[i].NetState.LastAction := Now; - end; - end; - - CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssRunning)); - Writeln(TimeStamp, 'Large scale operation ended.'); -end; - -end. - +(* + * 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 2008 Andreas Schneider + *) +unit ULandscape; + +{$mode objfpc}{$H+} + +interface + +uses + SysUtils, Classes, contnrs, UGenericIndex, UMap, UStatics, UTiledata, + UWorldItem, UMulBlock, math, + UTileDataProvider, URadarMap, + UListSort, UCacheManager, ULinkedList, UBufferedStreams, + UEnhancedMemoryStream, UPacketHandlers, UPackets, UNetState, UEnums; + +type + PRadarBlock = ^TRadarBlock; + TRadarBlock = array[0..7, 0..7] of Word; + TBlockSubscriptions = array of TLinkedList; + + { TBlock } + + TBlock = class(TObject) + constructor Create(AMap: TMapBlock; AStatics: TStaticBlock); + destructor Destroy; override; + protected + FMapBlock: TMapBlock; + FStaticBlock: TStaticBlock; + public + property Map: TMapBlock read FMapBlock; + property Static: TStaticBlock read FStaticBlock; + end; + + { TLandscape } + + TLandscape = class(TObject) + constructor Create(AMap, AStatics, AStaIdx, ATiledata, ARadarCol: string; + AWidth, AHeight: Word; var AValid: Boolean); + constructor Create(AMap, AStatics, AStaIdx, ATiledata: TStream; + ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); + destructor Destroy; override; + protected + FWidth: Word; + FHeight: Word; + FCellWidth: Word; + FCellHeight: Word; + FMap: TStream; + FStatics: TStream; + FStaIdx: TStream; + FTiledata: TStream; + FTiledataProvider: TTiledataProvider; + FOwnsStreams: Boolean; + FRadarMap: TRadarMap; + FBlockCache: TCacheManager; + FBlockSubscriptions: TBlockSubscriptions; + function Compare(left, right: TObject): Integer; + procedure OnBlockChanged(ABlock: TMulBlock); + procedure OnRemoveCachedObject(AObject: TObject); + function GetMapCell(AX, AY: Word): TMapCell; + function GetStaticList(AX, AY: Word): TList; + function GetBlockSubscriptions(AX, AY: Word): TLinkedList; + procedure UpdateStaticsPriority(AStaticItem: TStaticItem; + APrioritySolver: Integer); + + procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + procedure OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); + public + property Width: Word read FWidth; + property Height: Word read FHeight; + property CellWidth: Word read FCellWidth; + property CellHeight: Word read FCellHeight; + property MapCell[X, Y: Word]: TMapCell read GetMapCell; + property StaticList[X, Y: Word]: TList read GetStaticList; + property BlockSubscriptions[X, Y: Word]: TLinkedList read GetBlockSubscriptions; + property TiledataProvider: TTiledataProvider read FTiledataProvider; + + function GetMapBlock(AX, AY: Word): TMapBlock; + function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock; + function LoadBlock(AX, AY: Word): TBlock; + + procedure UpdateRadar(AX, AY: Word); + function GetEffectiveAltitude(ATile: TMapCell): ShortInt; + function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt; + procedure SortStaticsList(AStatics: TList); + + procedure Flush; + procedure SaveBlock(AWorldBlock: TWorldBlock); + function Validate: Boolean; + end; + + TStaticInfo = packed record + X: Word; + Y: Word; + Z: ShortInt; + TileID: Word; + Hue: Word; + end; + TAreaInfo = packed record + Left: Word; + Top: Word; + Right: Word; + Bottom: Word; + end; + TWorldPoint = packed record + X: Word; + Y: Word; + end; + +function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean; inline; + +implementation + +uses + UCEDServer, UConnectionHandling, UConfig, ULargeScaleOperations; + +const + mMap = 0; + mStatics = 1; + +function GetID(AX, AY: Word): Integer; +begin + Result := ((AX and $7FFF) shl 15) or (AY and $7FFF); +end; + +function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean; +begin + Result := InRange(AX, AArea.Left, AArea.Right) and + InRange(AY, AArea.Top, AArea.Bottom); +end; + +{ TBlock } + +constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock); +begin + inherited Create; + FMapBlock := AMap; + FStaticBlock := AStatics; +end; + +destructor TBlock.Destroy; +begin + if FMapBlock <> nil then FreeAndNil(FMapBlock); + if FStaticBlock <> nil then FreeAndNil(FStaticBlock); + inherited Destroy; +end; + +{ TLandscape } + +constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata, + ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); +var + map, statics, staidx, tiledata: TStream; +begin + Write(TimeStamp, 'Loading Map'); + map := TFileStream.Create(AMap, fmOpenReadWrite); + Write(', Statics'); + statics := TFileStream.Create(AStatics, fmOpenReadWrite); + Write(', StaIdx'); + staidx := TBufferedReader.Create(TFileStream.Create(AStaIdx, fmOpenReadWrite), True); + Writeln(', Tiledata'); + tiledata := TFileStream.Create(ATiledata, fmOpenRead or fmShareDenyWrite); + Create(map, statics, staidx, tiledata, ARadarCol, AWidth, AHeight, AValid); + FOwnsStreams := True; +end; + +constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata: TStream; + ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean); +var + blockID, blockType: Integer; +begin + inherited Create; + FWidth := AWidth; + FHeight := AHeight; + FCellWidth := FWidth * 8; + FCellHeight := FHeight * 8; + FMap := AMap; + FStatics := AStatics; + FStaIdx := AStaIdx; + FTiledata := ATiledata; + FOwnsStreams := False; + AValid := Validate; + if AValid then + begin + Write(TimeStamp, 'Creating Cache'); + FBlockCache := TCacheManager.Create(256); + FBlockCache.OnRemoveObject := @OnRemoveCachedObject; + Write(', Tiledata'); + FTiledataProvider := TTiledataProvider.Create(ATiledata); + Write(', Subscriptions'); + SetLength(FBlockSubscriptions, AWidth * AHeight); + for blockID := 0 to AWidth * AHeight - 1 do + FBlockSubscriptions[blockID] := TLinkedList.Create; + + Writeln(', RadarMap'); + FRadarMap := TRadarMap.Create(FMap, FStatics, FStaIdx, FWidth, FHeight, + ARadarCol); + + RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket)); + RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket)); + RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket)); + RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket)); + RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket)); + RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket)); + RegisterPacketHandler($0E, TPacketHandler.Create(0, @OnLargeScaleCommandPacket)); + end; +end; + +destructor TLandscape.Destroy; +var + i: Integer; +begin + for i := 0 to Length(FBlockSubscriptions) - 1 do + if FBlockSubscriptions[i] <> nil then FreeAndNil(FBlockSubscriptions[i]); + if FBlockCache <> nil then FreeAndNil(FBlockCache); + if FTiledataProvider <> nil then FreeAndNil(FTiledataProvider); + if FRadarMap <> nil then FreeAndNil(FRadarMap); + if FOwnsStreams then + begin + if FMap <> nil then FreeAndNil(FMap); + if FStatics <> nil then FreeAndNil(FStatics); + if FStaIdx <> nil then FreeAndNil(FStaIdx); + if FTiledata <> nil then FreeAndNil(FTiledata); + end; + + RegisterPacketHandler($06, nil); + RegisterPacketHandler($07, nil); + RegisterPacketHandler($08, nil); + RegisterPacketHandler($09, nil); + RegisterPacketHandler($0A, nil); + RegisterPacketHandler($0B, nil); + RegisterPacketHandler($0E, nil); + + inherited Destroy; +end; + +function TLandscape.GetBlockSubscriptions(AX, AY: Word): TLinkedList; +begin + if (AX >= 0) and (AX <= FWidth) and (AY >= 0) and (AY <= FHeight) then + Result := FBlockSubscriptions[(AY * FWidth) + AX] + else + Result := nil; +end; + +function TLandscape.GetMapCell(AX, AY: Word): TMapCell; +var + block: TMapBlock; +begin + Result := nil; + if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then + begin + block := GetMapBlock(AX div 8, AY div 8); + if block <> nil then + Result := block.Cells[(AY mod 8) * 8 + AX mod 8]; + end; +end; + +function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt; +begin + if (AX >= 0) and (AX < FCellWidth) and (AY >= 0) and (AY < FCellHeight) then + Result := MapCell[AX, AY].Altitude + else + Result := ADefault; +end; + +function TLandscape.GetStaticList(AX, AY: Word): TList; +var + block: TSeperatedStaticBlock; +begin + Result := nil; + if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then + begin + block := GetStaticBlock(AX div 8, AY div 8); + if block <> nil then + Result := block.Cells[(AY mod 8) * 8 + AX mod 8]; + end; +end; + +function TLandscape.Compare(left, right: TObject): Integer; +begin + Result := TWorldItem(right).Priority - TWorldItem(left).Priority; + if Result = 0 then + begin + if (left is TMapCell) and (right is TStaticItem) then + Result := 1 + else if (left is TStaticItem) and (right is TMapCell) then + Result := -1; + end; + + if Result = 0 then + Result := TWorldItem(right).PriorityBonus - TWorldItem(left).PriorityBonus; + + if Result = 0 then + Result := TWorldItem(right).PrioritySolver - TWorldItem(left).PrioritySolver; +end; + +procedure TLandscape.UpdateRadar(AX, AY: Word); +var + mapTile: TMapCell; + tile: TWorldItem; + staticItems, tiles: TList; + i: Integer; +begin + if (AX mod 8 = 0) and (AY mod 8 = 0) then + begin + staticItems := GetStaticList(AX, AY); + if staticItems <> nil then + begin + tiles := TList.Create; + mapTile := GetMapCell(AX, AY); + if mapTile <> nil then + begin + mapTile.Priority := GetEffectiveAltitude(mapTile); + mapTile.PriorityBonus := 0; + mapTile.PrioritySolver := 0; + tiles.Add(mapTile); + end; + for i := 0 to staticItems.Count - 1 do + begin + UpdateStaticsPriority(TStaticItem(staticItems.Items[i]), i + 1); + tiles.Add(staticItems.Items[i]); + end; + ListSort(tiles, @Compare); + + if tiles.Count > 0 then + begin + tile := TWorldItem(tiles.Items[tiles.Count - 1]); + if tile is TStaticItem then + FRadarMap.Update(AX div 8, AY div 8, tile.TileID + $4000) + else + FRadarMap.Update(AX div 8, AY div 8, tile.TileID) + end; + + tiles.Free; + end; + end; +end; + +procedure TLandscape.SortStaticsList(AStatics: TList); +var + i: Integer; +begin + for i := 0 to AStatics.Count - 1 do + UpdateStaticsPriority(TStaticItem(AStatics.Items[i]), i + 1); + ListSort(AStatics, @Compare); +end; + +function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt; +var + north, west, south, east: ShortInt; +begin + north := ATile.Altitude; + west := GetLandAlt(ATile.X, ATile.Y + 1, north); + south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north); + east := GetLandAlt(ATile.X + 1, ATile.Y, north); + + if Abs(north - south) > Abs(west - east) then + Result := (north + south) div 2 + else + Result := (west + east) div 2; +end; + +procedure TLandscape.OnBlockChanged(ABlock: TMulBlock); +begin + // Do nothing for now +end; + +procedure TLandscape.OnRemoveCachedObject(AObject: TObject); +var + block: TBlock; +begin + block := AObject as TBlock; + if block <> nil then + begin + if block.Map.Changed then SaveBlock(block.Map); + if block.Static.Changed then SaveBlock(block.Static); + end; +end; + +function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock; +var + block: TBlock; +begin + Result := nil; + if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then + begin + if FBlockCache.QueryID(GetID(AX, AY), TObject(block)) then + Result := block.Map + else + Result := LoadBlock(AX, AY).Map; + end; +end; + +function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock; +var + block: TBlock; +begin + Result := nil; + if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then + begin + if FBlockCache.QueryID(GetID(AX, AY), TObject(block)) then + Result := TSeperatedStaticBlock(block.Static) + else + Result := TSeperatedStaticBlock(LoadBlock(AX, AY).Static); + end; +end; + +function TLandscape.LoadBlock(AX, AY: Word): TBlock; +var + map: TMapBlock; + statics: TStaticBlock; + index: TGenericIndex; +begin + FMap.Position := ((AX * FHeight) + AY) * 196; + map := TMapBlock.Create(FMap, AX, AY); + map.OnChanged := @OnBlockChanged; + + FStaIdx.Position := ((AX * FHeight) + AY) * 12; + index := TGenericIndex.Create(FStaIdx); + statics := TSeperatedStaticBlock.Create(FStatics, index, AX, AY); + statics.OnChanged := @OnBlockChanged; + index.Free; + + Result := TBlock.Create(map, statics); + FBlockCache.StoreID(GetID(AX, AY), Result); +end; + +//Intelligent write: replace if possible, otherwise extend + +procedure TLandscape.Flush; +{var + blockID, blockType: Integer; + worldBlock: TWorldBlock; + index: TGenericIndex; + i, j, size: Integer;} +begin + {for blockID := 0 to FWidth * FHeight - 1 do + begin + for blockType := 0 to 1 do + begin + worldBlock := FPersistentBlocks[blockID][blockType]; + if Assigned(worldBlock) and worldBlock.Changed then + begin + if worldBlock is TMapBlock then + begin + FMap.Position := ((worldBlock.X * FHeight) + worldBlock.Y) * 196; + worldBlock.Write(FMap); + for i := 0 to 63 do + TMapBlock(worldBlock).Cells[i].InitOriginalState; + worldBlock.CleanUp; + end else if worldBlock is TStaticBlock then + begin + FStaIdx.Position := ((worldBlock.X * FHeight) + worldBlock.Y) * 12; + index := TGenericIndex.Create(FStaIdx); + size := worldBlock.GetSize; + if (size > index.Size) or (index.Lookup = LongInt($FFFFFFFF)) then + begin + FStatics.Position := FStatics.Size; + index.Lookup := FStatics.Position; + end; + if size = 0 then + index.Lookup := LongInt($FFFFFFFF) + else + begin + index.Size := size; + FStatics.Position := index.Lookup; + worldBlock.Write(FStatics); + end; + FStaIdx.Seek(-12, soFromCurrent); + index.Write(FStaIdx); + index.Free; + for i := 0 to 63 do + for j := 0 to TSeperatedStaticBlock(worldBlock).Cells[i].Count - 1 do + TStaticItem(TSeperatedStaticBlock(worldBlock).Cells[i].Items[j]).InitOriginalState; + worldBlock.CleanUp; + end; + end; + end; + end;} + FBlockCache.Clear; +end; + +procedure TLandscape.SaveBlock(AWorldBlock: TWorldBlock); +var + i, j, size: Integer; + index: TGenericIndex; +begin + if AWorldBlock is TMapBlock then + begin + FMap.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 196; + AWorldBlock.Write(FMap); + for i := 0 to 63 do + TMapBlock(AWorldBlock).Cells[i].InitOriginalState; + AWorldBlock.CleanUp; + end else if AWorldBlock is TStaticBlock then + begin + FStaIdx.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 12; + index := TGenericIndex.Create(FStaIdx); + size := AWorldBlock.GetSize; + if (size > index.Size) or (index.Lookup = LongInt($FFFFFFFF)) then + begin + FStatics.Position := FStatics.Size; + index.Lookup := FStatics.Position; + end; + if size = 0 then + index.Lookup := LongInt($FFFFFFFF) + else + begin + index.Size := size; + FStatics.Position := index.Lookup; + AWorldBlock.Write(FStatics); + end; + FStaIdx.Seek(-12, soFromCurrent); + index.Write(FStaIdx); + index.Free; + for i := 0 to 63 do + for j := 0 to TSeperatedStaticBlock(AWorldBlock).Cells[i].Count - 1 do + TStaticItem(TSeperatedStaticBlock(AWorldBlock).Cells[i].Items[j]).InitOriginalState; + AWorldBlock.CleanUp; + end; +end; + +function TLandscape.Validate: Boolean; +var + blocks: Integer; +begin + blocks := FWidth * FHeight; + FStaIdx.Seek(0, soFromEnd); //workaround for TBufferedStream + Result := (FMap.Size = (blocks * 196)) and (FStaIdx.Position = (blocks * 12)); +end; + +procedure TLandscape.UpdateStaticsPriority(AStaticItem: TStaticItem; + APrioritySolver: Integer); +var + staticTileData: TStaticTileData; +begin + staticTileData := FTiledataProvider.StaticTiles[AStaticItem.TileID]; + AStaticItem.PriorityBonus := 0; + if not ((staticTileData.Flags and tdfBackground) = tdfBackground) then + AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1; + if staticTileData.Height > 0 then + AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1; + AStaticItem.Priority := AStaticItem.Z + AStaticItem.PriorityBonus; + AStaticItem.PrioritySolver := APrioritySolver; +end; + +procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + x, y: Word; + cell: TMapCell; + subscriptions: TLinkedList; + item: PLinkedItem; + packet: TDrawMapPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + x := ABuffer.ReadWord; + y := ABuffer.ReadWord; + cell := GetMapCell(x, y); + if cell <> nil then + begin + cell.Altitude := ABuffer.ReadShortInt; + cell.TileID := ABuffer.ReadWord; + + packet := TDrawMapPacket.Create(cell); + subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)]; + item := nil; + while subscriptions.Iterate(item) do + CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); + packet.Free; + + UpdateRadar(x, y); + end; +end; + +procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + x, y: Word; + block: TSeperatedStaticBlock; + staticItem: TStaticItem; + targetStaticList: TList; + i: Integer; + subscriptions: TLinkedList; + item: PLinkedItem; + packet: TInsertStaticPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + x := ABuffer.ReadWord; + y := ABuffer.ReadWord; + block := GetStaticBlock(x div 8, y div 8); + if block <> nil then + begin + staticItem := TStaticItem.Create(nil, nil, 0, 0); + staticItem.X := x; + staticItem.Y := y; + staticItem.Z := ABuffer.ReadShortInt; + staticItem.TileID := ABuffer.ReadWord; + staticItem.Hue := ABuffer.ReadWord; + targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8]; + targetStaticList.Add(staticItem); + SortStaticsList(targetStaticList); + staticItem.Owner := block; + + packet := TInsertStaticPacket.Create(staticItem); + subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)]; + item := nil; + while subscriptions.Iterate(item) do + CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); + packet.Free; + + UpdateRadar(x, y); + end; +end; + +procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + block: TSeperatedStaticBlock; + i: Integer; + statics: TList; + staticInfo: TStaticInfo; + staticItem: TStaticItem; + subscriptions: TLinkedList; + item: PLinkedItem; + packet: TDeleteStaticPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); + block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); + if block <> nil then + begin + statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; + for i := 0 to statics.Count - 1 do + begin + staticItem := TStaticItem(statics.Items[i]); + if (staticItem.Z = staticInfo.Z) and + (staticItem.TileID = staticInfo.TileID) and + (staticItem.Hue = staticInfo.Hue) then + begin + packet := TDeleteStaticPacket.Create(staticItem); + + statics.Delete(i); + staticItem.Delete; + + subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; + item := nil; + while subscriptions.Iterate(item) do + CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); + packet.Free; + + UpdateRadar(staticInfo.X, staticInfo.Y); + + Break; + end; + end; + end; +end; + +procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + block: TSeperatedStaticBlock; + i, j: Integer; + statics: TList; + staticInfo: TStaticInfo; + staticItem: TStaticItem; + newZ: ShortInt; + subscriptions: TLinkedList; + item: PLinkedItem; + packet: TElevateStaticPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); + block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); + if block <> nil then + begin + statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; + for i := 0 to statics.Count - 1 do + begin + staticItem := TStaticItem(statics.Items[i]); + if (staticItem.Z = staticInfo.Z) and + (staticItem.TileID = staticInfo.TileID) and + (staticItem.Hue = staticInfo.Hue) then + begin + newZ := ABuffer.ReadShortInt; + packet := TElevateStaticPacket.Create(staticItem, newZ); + + staticItem.Z := newZ; + SortStaticsList(statics); + + subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; + item := nil; + while subscriptions.Iterate(item) do + CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); + packet.Free; + + UpdateRadar(staticInfo.X, staticInfo.Y); + + Break; + end; + end; + end; +end; + +procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + sourceBlock, targetBlock: TSeperatedStaticBlock; + sourceSubscriptions, targetSubscriptions: TList; + i: Integer; + statics: TList; + staticInfo: TStaticInfo; + staticItem: TStaticItem; + newX, newY: Word; + subscriptions: TLinkedList; + item: PLinkedItem; + insertPacket: TInsertStaticPacket; + deletePacket: TDeleteStaticPacket; + movePacket: TMoveStaticPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + staticItem := nil; + ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); + newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1); + newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1); + + if (staticInfo.X = newX) and (staticInfo.Y = newY) then Exit; + + if ((abs(staticInfo.X - newX) > 8) or (abs(staticInfo.Y - newY) > 8)) and + (not ValidateAccess(ANetState, alAdministrator)) then Exit; + + sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); + targetBlock := GetStaticBlock(newX div 8, newY div 8); + if (sourceBlock <> nil) and (targetBlock <> nil) then + begin + statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; + i := 0; + while (i < statics.Count) and (staticItem = nil) do + begin + staticItem := TStaticItem(statics.Items[i]); + if (staticItem.Z <> staticInfo.Z) or + (staticItem.TileID <> staticInfo.TileID) or + (staticItem.Hue <> staticInfo.Hue) then + begin + staticItem := nil; + end; + Inc(i); + end; + + if staticItem <> nil then + begin + deletePacket := TDeleteStaticPacket.Create(staticItem); + movePacket := TMoveStaticPacket.Create(staticItem, newX, newY); + + statics.Remove(staticItem); + statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8]; + statics.Add(staticItem); + staticItem.UpdatePos(newX, newY, staticItem.Z); + staticItem.Owner := targetBlock; + + insertPacket := TInsertStaticPacket.Create(staticItem); + + SortStaticsList(statics); + + sourceSubscriptions := TList.Create; + subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; + item := nil; + while subscriptions.Iterate(item) do + sourceSubscriptions.Add(item^.Data); + + targetSubscriptions := TList.Create; + subscriptions := FBlockSubscriptions[(newY div 8) * FWidth + (newX div 8)]; + item := nil; + while subscriptions.Iterate(item) do + targetSubscriptions.Add(item^.Data); + + for i := 0 to sourceSubscriptions.Count - 1 do + begin + if targetSubscriptions.IndexOf(sourceSubscriptions.Items[i]) > -1 then + CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), movePacket, False) + else + CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), deletePacket, False); + end; + + for i := 0 to targetSubscriptions.Count - 1 do + begin + if sourceSubscriptions.IndexOf(targetSubscriptions.Items[i]) = -1 then + CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), insertPacket, False); + end; + + UpdateRadar(staticInfo.X, staticInfo.Y); + UpdateRadar(newX, newY); + + insertPacket.Free; + deletePacket.Free; + movePacket.Free; + end; + end; +end; + +procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + block: TSeperatedStaticBlock; + i, j: Integer; + statics: TList; + staticInfo: TStaticInfo; + staticItem: TStaticItem; + newHue: Word; + subscriptions: TLinkedList; + item: PLinkedItem; + packet: THueStaticPacket; +begin + if not ValidateAccess(ANetState, alNormal) then Exit; + ABuffer.Read(staticInfo, SizeOf(TStaticInfo)); + block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8); + if block <> nil then + begin + statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8]; + for i := 0 to statics.Count - 1 do + begin + staticItem := TStaticItem(statics.Items[i]); + if (staticItem.Z = staticInfo.Z) and + (staticItem.TileID = staticInfo.TileID) and + (staticItem.Hue = staticInfo.Hue) then + begin + newHue := ABuffer.ReadWord; + packet := THueStaticPacket.Create(staticItem, newHue); + + staticItem.Hue := newHue; + + subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)]; + item := nil; + while subscriptions.Iterate(item) do + CEDServerInstance.SendPacket(TNetState(item^.Data), packet, False); + packet.Free; + + Break; + end; + end; + end; +end; + +procedure TLandscape.OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream; + ANetState: TNetState); +var + areaInfo: array of TAreaInfo; + areaCount: Byte; + i: Integer; + blockX, blockY, cellX, cellY, x, y: Word; + realBlockX, realBlockY, realCellX, realCellY: Word; + blockOffX, cellOffX, modX, blockOffY, cellOffY, modY: Integer; + blockID, cellID: Cardinal; + emptyBits: TBits; + bitMask: array of TBits; + mapTile: TMapCell; + statics: TList; + operations: TList; + clients: array of record + NetState: TNetState; + Blocks: TBlockCoordsArray; + end; + netState: TNetState; + subscriptions: TLinkedList; + item: PLinkedItem; + cmOperation: TLSCopyMove; + additionalAffectedBlocks: TBits; +begin + if not ValidateAccess(ANetState, alAdministrator) then Exit; + Writeln(TimeStamp, ANetState.Account.Name, ' begins large scale operation'); + CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssOther, + Format('%s is performing large scale operations ...', [ANetState.Account.Name]))); + + //Bitmask + emptyBits := TBits.Create(64); + SetLength(bitMask, FWidth * FHeight); + for i := Low(bitMask) to High(bitMask) do + bitMask[i] := TBits.Create(64); + additionalAffectedBlocks := TBits.Create(FWidth * FHeight); + + areaCount := ABuffer.ReadByte; + SetLength(areaInfo, areaCount); + for i := 0 to areaCount - 1 do + begin + areaInfo[i].Left := Max(ABuffer.ReadWord, 0); + areaInfo[i].Top := Max(ABuffer.ReadWord, 0); + areaInfo[i].Right := Min(ABuffer.ReadWord, FCellWidth - 1); + areaInfo[i].Bottom := Min(ABuffer.ReadWord, FCellHeight - 1); + for x := areaInfo[i].Left to areaInfo[i].Right do + for y := areaInfo[i].Top to areaInfo[i].Bottom do + begin + blockID := (x div 8) * FHeight + (y div 8); + cellID := (y mod 8) * 8 + (x mod 8); + bitMask[blockID].Bits[cellID] := True; + end; + end; + + //client blocks + SetLength(clients, 0); + CEDServerInstance.TCPServer.IterReset; + while CEDServerInstance.TCPServer.IterNext do + begin + netState := TNetState(CEDServerInstance.TCPServer.Iterator.UserData); + if netState <> nil then + begin + SetLength(clients, Length(clients) + 1); + clients[High(clients)].NetState := netState; + SetLength(clients[High(clients)].Blocks, 0); + end; + end; + + operations := TList.Create; + + cmOperation := nil; + if ABuffer.ReadBoolean then + begin + cmOperation := TLSCopyMove.Init(ABuffer, Self); + if (cmOperation.OffsetX <> 0) or (cmOperation.OffsetY <> 0) then + begin + operations.Add(cmOperation); + + if cmOperation.OffsetX > 0 then + begin + blockOffX := FWidth - 1; + cellOffX := 7; + modX := -1; + end else + begin + blockOffX := 0; + cellOffX := 0; + modX := 1; + end; + + if cmOperation.OffsetY > 0 then + begin + blockOffY := FHeight - 1; + cellOffY := 7; + modY := -1; + end else + begin + blockOffY := 0; + cellOffY := 0; + modY := 1; + end; + end else + FreeAndNil(cmOperation); + end; + if cmOperation = nil then + begin + blockOffX := 0; + cellOffX := 0; + modX := 1; + blockOffY := 0; + cellOffY := 0; + modY := 1; + end; + if ABuffer.ReadBoolean then operations.Add(TLSSetAltitude.Init(ABuffer, Self)); + if ABuffer.ReadBoolean then operations.Add(TLSDrawTerrain.Init(ABuffer, Self)); + if ABuffer.ReadBoolean then operations.Add(TLSDeleteStatics.Init(ABuffer, Self)); + if ABuffer.ReadBoolean then operations.Add(TLSInsertStatics.Init(ABuffer, Self)); + + FRadarMap.BeginUpdate; + for blockX := 0 to FWidth - 1 do + begin + realBlockX := blockOffX + modX * blockX; + for blockY := 0 to FHeight - 1 do + begin + realBlockY := blockOffY + modY * blockY; + blockID := (realBlockX * FHeight) + realBlockY; + if bitMask[blockID].Equals(emptyBits) then Continue; + + for cellY := 0 to 7 do + begin + realCellY := cellOffY + modY * cellY; + for cellX := 0 to 7 do + begin + realCellX := cellOffX + modX * cellX; + if bitMask[blockID].Bits[(realCellY * 8) + realCellX] then + begin + x := realBlockX * 8 + realCellX; + y := realBlockY * 8 + realCellY; + mapTile := GetMapCell(x, y); + statics := GetStaticList(x, y); + for i := 0 to operations.Count - 1 do + TLargeScaleOperation(operations.Items[i]).Apply(mapTile, statics, + additionalAffectedBlocks); + SortStaticsList(statics); + + UpdateRadar(x, y); + end; + end; + end; + + subscriptions := FBlockSubscriptions[realBlockY * FWidth + realBlockX]; + for i := Low(clients) to High(clients) do + begin + item := nil; + while subscriptions.Iterate(item) do + begin + if TNetState(item^.Data) = clients[i].NetState then + begin + SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1); + with clients[i].Blocks[High(clients[i].Blocks)] do + begin + X := realBlockX; + Y := realBlockY; + end; + Break; + end; + end; + end; + + end; + end; + + //additional blocks + for blockX := 0 to FWidth - 1 do + begin + for blockY := 0 to FHeight - 1 do + begin + blockID := (blockX * FHeight) + blockY; + if bitMask[blockID].Equals(emptyBits) and additionalAffectedBlocks[blockID] then + begin + subscriptions := FBlockSubscriptions[blockY * FWidth + blockX]; + for i := Low(clients) to High(clients) do + begin + item := nil; + while subscriptions.Iterate(item) do + begin + if TNetState(item^.Data) = clients[i].NetState then + begin + SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1); + with clients[i].Blocks[High(clients[i].Blocks)] do + begin + X := blockX; + Y := blockY; + end; + Break; + end; + end; + end; + + UpdateRadar(blockX * 8, blockY * 8); + + end; + end; + end; + + //clean up + for i := Low(bitMask) to High(bitMask) do + bitMask[i].Free; + emptyBits.Free; + additionalAffectedBlocks.Free; + + for i := 0 to operations.Count - 1 do + TLargeScaleOperation(operations.Items[i]).Free; + operations.Free; + + //Update clients + FRadarMap.EndUpdate; + for i := Low(clients) to High(clients) do + begin + if Length(clients[i].Blocks) > 0 then + begin + CEDServerInstance.SendPacket(clients[i].NetState, TCompressedPacket.Create( + TBlockPacket.Create(clients[i].Blocks, nil))); + clients[i].NetState.LastAction := Now; + end; + end; + + CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssRunning)); + Writeln(TimeStamp, 'Large scale operation ended.'); +end; + +end. + diff --git a/Server/cedserver.lpi b/Server/cedserver.lpi index 73aecba..4bcba24 100644 --- a/Server/cedserver.lpi +++ b/Server/cedserver.lpi @@ -28,7 +28,7 @@ - + @@ -74,6 +74,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/cedserver.lpr b/Server/cedserver.lpr index 1562d89..a505857 100644 --- a/Server/cedserver.lpr +++ b/Server/cedserver.lpr @@ -33,11 +33,13 @@ uses {$ENDIF}{$ENDIF} SysUtils, Classes, lnetbase, - UConfig, UCEDServer, URadarMap, ULargeScaleOperations; + UConfig, UCEDServer, URadarMap, ULargeScaleOperations, UPackets, + UAdminHandling, UClientHandling, ULandscape, UPacketHandlers; {$I version.inc} begin + Writeln(''); Writeln('UO CentrED Server Version ', ProductVersion); Writeln('Copyright ', Copyright); //Writeln('================================'); @@ -59,15 +61,15 @@ begin end; {$ENDIF} - Write(TimeStamp, 'Initializing ... '); + Writeln(TimeStamp, 'Initialization started'); Randomize; CEDServerInstance := TCEDServer.Create; - Writeln('Done'); + Writeln(TimeStamp, 'Initialization done'); CEDServerInstance.Run; - Write(TimeStamp, 'Terminating ... '); + Write(TimeStamp, 'Shutting down ... '); FreeAndNil(CEDServerInstance); Config.Flush; FreeAndNil(Config); - Writeln('Done'); + Writeln('done'); end. diff --git a/version.inc b/version.inc index 02ffc38..f6ab7bd 100644 --- a/version.inc +++ b/version.inc @@ -1,5 +1,5 @@ -const - ProductVersion = '0.3.6'; - ProtocolVersion = 5; - Revision = '41'; - Copyright = '2007 Andreas Schneider'; +const + ProductVersion = '0.3.6'; + ProtocolVersion = 5; + Revision = '41'; + Copyright = '2008 Andreas Schneider';