Statistics
| Branch: | Tag: | Revision:

root / Server / ULandscape.pas @ 152:2c10e1ad6647

History | View | Annotate | Download (34.1 kB)

1
(*
2
 * CDDL HEADER START
3
 *
4
 * The contents of this file are subject to the terms of the
5
 * Common Development and Distribution License, Version 1.0 only
6
 * (the "License").  You may not use this file except in compliance
7
 * with the License.
8
 *
9
 * You can obtain a copy of the license at
10
 * http://www.opensource.org/licenses/cddl1.php.
11
 * See the License for the specific language governing permissions
12
 * and limitations under the License.
13
 *
14
 * When distributing Covered Code, include this CDDL HEADER in each
15
 * file and include the License file at
16
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
17
 * add the following below this CDDL HEADER, with the fields enclosed
18
 * by brackets "[]" replaced with your own identifying * information:
19
 *      Portions Copyright [yyyy] [name of copyright owner]
20
 *
21
 * CDDL HEADER END
22
 *
23
 *
24
 *      Portions Copyright 2009 Andreas Schneider
25
 *)
26
unit ULandscape;
27
28
{$mode objfpc}{$H+}
29
30
interface
31
32
uses
33
  SysUtils, Classes, math, UGenericIndex, UMap, UStatics, UWorldItem, UMulBlock,
34
  UTileDataProvider, URadarMap,
35
  UCacheManager, ULinkedList, UBufferedStreams,
36
  UEnhancedMemoryStream, UPacketHandlers, UPackets, UNetState, UEnums;
37
38
type
39
  PRadarBlock = ^TRadarBlock;
40
  TRadarBlock = array[0..7, 0..7] of Word;
41
  TBlockSubscriptions = array of TLinkedList;
42
43
  { TSeperatedStaticBlock }
44
45
  TSeperatedStaticBlock = class(TStaticBlock)
46
    constructor Create(AData: TStream; AIndex: TGenericIndex; AX, AY: Word); overload;
47
    constructor Create(AData: TStream; AIndex: TGenericIndex); overload;
48
    destructor Destroy; override;
49
  protected
50
    FTiledataProvider: TTiledataProvider;
51
  public
52
    { Fields }
53
    Cells: array[0..63] of TStaticItemList;
54
    property TiledataProvider: TTiledataProvider read FTiledataProvider
55
      write FTiledataProvider;
56
57
    { Methods }
58
    function Clone: TSeperatedStaticBlock; override;
59
    function GetSize: Integer; override;
60
    procedure RebuildList;
61
  end;
62
  
63
  { TBlock }
64
65
  TBlock = class
66
    constructor Create(AMap: TMapBlock; AStatics: TSeperatedStaticBlock);
67
    destructor Destroy; override;
68
  protected
69
    FMapBlock: TMapBlock;
70
    FStaticBlock: TSeperatedStaticBlock;
71
  public
72
    property Map: TMapBlock read FMapBlock;
73
    property Static: TSeperatedStaticBlock read FStaticBlock;
74
  end;
75
76
  TBlockCache = specialize TCacheManager<TBlock>;
77
78
  { TLandscape }
79
80
  TLandscape = class
81
    constructor Create(AMap, AStatics, AStaIdx, ATiledata, ARadarCol: string;
82
      AWidth, AHeight: Word; var AValid: Boolean);
83
    constructor Create(AMap, AStatics, AStaIdx, ATiledata: TStream;
84
      ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean);
85
    destructor Destroy; override;
86
  protected
87
    FWidth: Word;
88
    FHeight: Word;
89
    FCellWidth: Word;
90
    FCellHeight: Word;
91
    FMap: TStream;
92
    FStatics: TStream;
93
    FStaIdx: TStream;
94
    FTiledata: TStream;
95
    FTiledataProvider: TTiledataProvider;
96
    FOwnsStreams: Boolean;
97
    FRadarMap: TRadarMap;
98
    FBlockCache: TBlockCache;
99
    FBlockSubscriptions: TBlockSubscriptions;
100
    procedure OnRemoveCachedObject(ABlock: TBlock);
101
    function GetMapCell(AX, AY: Word): TMapCell;
102
    function GetStaticList(AX, AY: Word): TStaticItemList;
103
    function GetBlockSubscriptions(AX, AY: Word): TLinkedList;
104
105
    procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream;
106
      ANetState: TNetState);
107
    procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream;
108
      ANetState: TNetState);
109
    procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream;
110
      ANetState: TNetState);
111
    procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream;
112
      ANetState: TNetState);
113
    procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream;
114
      ANetState: TNetState);
115
    procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream;
116
      ANetState: TNetState);
117
    procedure OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream;
118
      ANetState: TNetState);
119
  public
120
    property Width: Word read FWidth;
121
    property Height: Word read FHeight;
122
    property CellWidth: Word read FCellWidth;
123
    property CellHeight: Word read FCellHeight;
124
    property MapCell[X, Y: Word]: TMapCell read GetMapCell;
125
    property StaticList[X, Y: Word]: TStaticItemList read GetStaticList;
126
    property BlockSubscriptions[X, Y: Word]: TLinkedList read GetBlockSubscriptions;
127
    property TiledataProvider: TTiledataProvider read FTiledataProvider;
128
129
    function GetMapBlock(AX, AY: Word): TMapBlock;
130
    function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
131
    function LoadBlock(AX, AY: Word): TBlock;
132
133
    procedure UpdateRadar(AX, AY: Word);
134
    function GetEffectiveAltitude(ATile: TMapCell): ShortInt;
135
    function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
136
    procedure SortStaticsList(AStatics: TStaticItemList);
137
138
    procedure Flush;
139
    procedure SaveBlock(AWorldBlock: TWorldBlock);
140
    function Validate: Boolean;
141
  end;
142
  
143
  TStaticInfo = packed record
144
    X: Word;
145
    Y: Word;
146
    Z: ShortInt;
147
    TileID: Word;
148
    Hue: Word;
149
  end;
150
  TAreaInfo = packed record
151
    Left: Word;
152
    Top: Word;
153
    Right: Word;
154
    Bottom: Word;
155
  end;
156
  TWorldPoint = packed record
157
    X: Word;
158
    Y: Word;
159
  end;
160
  
161
function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean; inline;
162
163
implementation
164
165
uses
166
  UCEDServer, UConnectionHandling, UConfig, ULargeScaleOperations, Logging;
167
168
function GetID(AX, AY: Word): Integer;
169
begin
170
  Result := ((AX and $7FFF) shl 15) or (AY and $7FFF);
171
end;
172
173
function PointInArea(AArea: TAreaInfo; AX, AY: Word): Boolean;
174
begin
175
  Result := InRange(AX, AArea.Left, AArea.Right) and
176
            InRange(AY, AArea.Top, AArea.Bottom);
177
end;
178
179
{ TSeperatedStaticBlock }
180
181
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex;
182
  AX, AY: Word);
183
var
184
  i: Integer;
185
  item: TStaticItem;
186
  block: TMemoryStream;
187
begin
188
  inherited Create;
189
  FItems := TStaticItemList.Create(False);
190
191
  FX := AX;
192
  FY := AY;
193
194
  for i := 0 to 63 do
195
    Cells[i] := TStaticItemList.Create(True);
196
197
  if (AData <> nil) and (AIndex.Lookup > 0) and (AIndex.Size > 0) then
198
  begin
199
    AData.Position := AIndex.Lookup;
200
    block := TMemoryStream.Create;
201
    block.CopyFrom(AData, AIndex.Size);
202
    block.Position := 0;
203
    for i := 1 to (AIndex.Size div 7) do
204
    begin
205
      item := TStaticItem.Create(Self, block, AX, AY);
206
      Cells[(item.Y mod 8) * 8 + (item.X mod 8)].Add(item);
207
    end;
208
    block.Free;
209
  end;
210
end;
211
212
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex);
213
begin
214
  Create(AData, AIndex, 0, 0);
215
end;
216
217
destructor TSeperatedStaticBlock.Destroy;
218
var
219
  i: Integer;
220
begin
221
  FreeAndNil(FItems);
222
223
  for i := 0 to 63 do
224
    FreeAndNil(Cells[i]);
225
226
  inherited Destroy;
227
end;
228
229
function TSeperatedStaticBlock.Clone: TSeperatedStaticBlock;
230
begin
231
  raise Exception.Create('TSeperatedStaticBlock.Clone is not implemented (yet).');
232
  Result := nil;
233
end;
234
235
function TSeperatedStaticBlock.GetSize: Integer;
236
begin
237
  RebuildList;
238
  Result := inherited GetSize;
239
end;
240
241
procedure TSeperatedStaticBlock.RebuildList;
242
var
243
  i, j, solver: Integer;
244
begin
245
  FItems.Clear;
246
  solver := 0;
247
  for i := 0 to 63 do
248
  begin
249
    if Cells[i] <> nil then
250
    begin
251
      for j := 0 to Cells[i].Count - 1 do
252
      begin
253
        FItems.Add(Cells[i].Items[j]);
254
        if Cells[i].Items[j].TileID < FTiledataProvider.StaticCount then
255
        begin
256
          Cells[i].Items[j].UpdatePriorities(
257
            FTiledataProvider.StaticTiles[Cells[i].Items[j].TileID], solver);
258
        end else
259
          Logger.Send([lcLandscape, lcServer, lcError], 'Cannot find Tiledata ' +
260
            'for the Static Item with ID $%x.', [Cells[i].Items[j].TileID]);
261
        Inc(solver);
262
      end;
263
    end;
264
  end;
265
  Sort;
266
end;
267
268
{ TBlock }
269
270
constructor TBlock.Create(AMap: TMapBlock; AStatics: TSeperatedStaticBlock);
271
begin
272
  inherited Create;
273
  FMapBlock := AMap;
274
  FStaticBlock := AStatics;
275
end;
276
277
destructor TBlock.Destroy;
278
begin
279
  FreeAndNil(FMapBlock);
280
  FreeAndNil(FStaticBlock);
281
  inherited Destroy;
282
end;
283
284
{ TLandscape }
285
286
constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata,
287
  ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean);
288
var
289
  map, statics, staidx, tiledata: TStream;
290
begin
291
  Write(TimeStamp, 'Loading Map');
292
  map := TFileStream.Create(AMap, fmOpenReadWrite);
293
  Write(', Statics');
294
  statics := TFileStream.Create(AStatics, fmOpenReadWrite);
295
  Write(', StaIdx');
296
  staidx := TBufferedReader.Create(TFileStream.Create(AStaIdx, fmOpenReadWrite), True);
297
  Writeln(', Tiledata');
298
  tiledata := TFileStream.Create(ATiledata, fmOpenRead or fmShareDenyWrite);
299
  Create(map, statics, staidx, tiledata, ARadarCol, AWidth, AHeight, AValid);
300
  FOwnsStreams := True;
301
end;
302
303
constructor TLandscape.Create(AMap, AStatics, AStaIdx, ATiledata: TStream;
304
  ARadarCol: string; AWidth, AHeight: Word; var AValid: Boolean);
305
var
306
  blockID: Integer;
307
begin
308
  inherited Create;
309
  FWidth := AWidth;
310
  FHeight := AHeight;
311
  FCellWidth := FWidth * 8;
312
  FCellHeight := FHeight * 8;
313
  FMap := AMap;
314
  FStatics := AStatics;
315
  FStaIdx := AStaIdx;
316
  FTiledata := ATiledata;
317
  FOwnsStreams := False;
318
  AValid := Validate;
319
  if AValid then
320
  begin
321
    Write(TimeStamp, 'Creating Cache');
322
    FBlockCache := TBlockCache.Create(256);
323
    FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
324
    Write(', Tiledata');
325
    FTiledataProvider := TTiledataProvider.Create(ATiledata);
326
    Write(', Subscriptions');
327
    SetLength(FBlockSubscriptions, AWidth * AHeight);
328
    for blockID := 0 to AWidth * AHeight - 1 do
329
      FBlockSubscriptions[blockID] := TLinkedList.Create;
330
331
    Writeln(', RadarMap');
332
    FRadarMap := TRadarMap.Create(FMap, FStatics, FStaIdx, FWidth, FHeight,
333
      ARadarCol);
334
335
    RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
336
    RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
337
    RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
338
    RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
339
    RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
340
    RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
341
    RegisterPacketHandler($0E, TPacketHandler.Create(0, @OnLargeScaleCommandPacket));
342
  end;
343
end;
344
345
destructor TLandscape.Destroy;
346
var
347
  i: Integer;
348
begin
349
  for i := 0 to Length(FBlockSubscriptions) - 1 do
350
    FreeAndNil(FBlockSubscriptions[i]);
351
  FreeAndNil(FBlockCache);
352
  FreeAndNil(FTiledataProvider);
353
  FreeAndNil(FRadarMap);
354
  if FOwnsStreams then
355
  begin
356
    FreeAndNil(FMap);
357
    FreeAndNil(FStatics);
358
    FreeAndNil(FStaIdx);
359
    FreeAndNil(FTiledata);
360
  end;
361
  
362
  RegisterPacketHandler($06, nil);
363
  RegisterPacketHandler($07, nil);
364
  RegisterPacketHandler($08, nil);
365
  RegisterPacketHandler($09, nil);
366
  RegisterPacketHandler($0A, nil);
367
  RegisterPacketHandler($0B, nil);
368
  RegisterPacketHandler($0E, nil);
369
  
370
  inherited Destroy;
371
end;
372
373
function TLandscape.GetBlockSubscriptions(AX, AY: Word): TLinkedList;
374
begin
375
  if (AX >= 0) and (AX <= FWidth) and (AY >= 0) and (AY <= FHeight) then
376
    Result := FBlockSubscriptions[(AY * FWidth) + AX]
377
  else
378
    Result := nil;
379
end;
380
381
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
382
var
383
  block: TMapBlock;
384
begin
385
  Result := nil;
386
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
387
  begin
388
    block := GetMapBlock(AX div 8, AY div 8);
389
    if block <> nil then
390
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
391
  end;
392
end;
393
394
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
395
begin
396
  if (AX >= 0) and (AX < FCellWidth) and (AY >= 0) and (AY < FCellHeight) then
397
    Result := MapCell[AX, AY].Altitude
398
  else
399
    Result := ADefault;
400
end;
401
402
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
403
var
404
  block: TSeperatedStaticBlock;
405
begin
406
  Result := nil;
407
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
408
  begin
409
    block := GetStaticBlock(AX div 8, AY div 8);
410
    if block <> nil then
411
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
412
  end;
413
end;
414
415
procedure TLandscape.UpdateRadar(AX, AY: Word);
416
var
417
  mapTile: TMapCell;
418
  tile: TWorldItem;
419
  staticItems: TStaticItemList;
420
  tiles: TWorldItemList;
421
  i: Integer;
422
begin
423
  if (AX mod 8 = 0) and (AY mod 8 = 0) then
424
  begin
425
    staticItems := GetStaticList(AX, AY);
426
    if staticItems <> nil then
427
    begin
428
      tiles := TWorldItemList.Create(False);
429
      mapTile := GetMapCell(AX, AY);
430
      if mapTile <> nil then
431
      begin
432
        mapTile.Priority := GetEffectiveAltitude(mapTile);
433
        mapTile.PriorityBonus := 0;
434
        mapTile.PrioritySolver := 0;
435
        tiles.Add(mapTile);
436
      end;
437
      for i := 0 to staticItems.Count - 1 do
438
      begin
439
        if staticItems[i].TileID < FTiledataProvider.StaticCount then
440
        begin
441
          staticItems[i].UpdatePriorities(
442
            FTiledataProvider.StaticTiles[staticItems[i].TileID],
443
            i + 1);
444
        end else
445
          Logger.Send([lcLandscape, lcServer, lcError], 'Cannot find Tiledata ' +
446
            'for the Static Item with ID $%x.', [staticItems[i].TileID]);
447
        tiles.Add(staticItems[i]);
448
      end;
449
      tiles.Sort(@CompareWorldItems);
450
451
      if tiles.Count > 0 then
452
      begin
453
        tile := tiles[tiles.Count - 1];
454
        if tile is TStaticItem then
455
          FRadarMap.Update(AX div 8, AY div 8, tile.TileID + $4000)
456
        else
457
          FRadarMap.Update(AX div 8, AY div 8, tile.TileID)
458
      end;
459
460
      tiles.Free;
461
    end;
462
  end;
463
end;
464
465
procedure TLandscape.SortStaticsList(AStatics: TStaticItemList);
466
var
467
  i: Integer;
468
begin
469
  for i := 0 to AStatics.Count - 1 do
470
    if AStatics[i].TileID < FTiledataProvider.StaticCount then
471
    begin
472
      AStatics[i].UpdatePriorities(
473
        FTiledataProvider.StaticTiles[AStatics[i].TileID],
474
        i + 1);
475
    end else
476
      Logger.Send([lcLandscape, lcServer, lcError], 'Cannot find Tiledata ' +
477
        'for the Static Item with ID $%x.', [AStatics[i].TileID]);
478
  AStatics.Sort(@CompareStaticItems);
479
end;
480
481
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
482
var
483
  north, west, south, east: ShortInt;
484
begin
485
  north := ATile.Altitude;
486
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
487
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
488
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
489
490
  if Abs(north - south) > Abs(west - east) then
491
    Result := (north + south) div 2
492
  else
493
    Result := (west + east) div 2;
494
end;
495
496
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
497
begin
498
  if ABlock <> nil then
499
  begin
500
    if ABlock.Map.Changed then SaveBlock(ABlock.Map);
501
    if ABlock.Static.Changed then SaveBlock(ABlock.Static);
502
  end;
503
end;
504
505
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
506
var
507
  block: TBlock;
508
begin
509
  Result := nil;
510
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
511
  begin
512
    if FBlockCache.QueryID(GetID(AX, AY), block) then
513
      Result := block.Map
514
    else
515
      Result := LoadBlock(AX, AY).Map;
516
  end;
517
end;
518
519
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
520
var
521
  block: TBlock;
522
begin
523
  Result := nil;
524
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
525
  begin
526
    if FBlockCache.QueryID(GetID(AX, AY), block) then
527
      Result := TSeperatedStaticBlock(block.Static)
528
    else
529
      Result := TSeperatedStaticBlock(LoadBlock(AX, AY).Static);
530
  end;
531
end;
532
533
function TLandscape.LoadBlock(AX, AY: Word): TBlock;
534
var
535
  map: TMapBlock;
536
  statics: TSeperatedStaticBlock;
537
  index: TGenericIndex;
538
begin
539
  FMap.Position := ((AX * FHeight) + AY) * 196;
540
  map := TMapBlock.Create(FMap, AX, AY);
541
542
  FStaIdx.Position := ((AX * FHeight) + AY) * 12;
543
  index := TGenericIndex.Create(FStaIdx);
544
  statics := TSeperatedStaticBlock.Create(FStatics, index, AX, AY);
545
  statics.TiledataProvider := FTiledataProvider;
546
  index.Free;
547
  
548
  Result := TBlock.Create(map, statics);
549
  FBlockCache.StoreID(GetID(AX, AY), Result);
550
end;
551
552
//Intelligent write: replace if possible, otherwise extend
553
554
procedure TLandscape.Flush;
555
begin
556
  FBlockCache.Clear; //Clear writes modified blocks before removing them from the cache
557
end;
558
559
procedure TLandscape.SaveBlock(AWorldBlock: TWorldBlock);
560
var
561
  i, j, size: Integer;
562
  index: TGenericIndex;
563
begin
564
  if AWorldBlock is TMapBlock then
565
  begin
566
    FMap.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 196;
567
    AWorldBlock.Write(FMap);
568
    AWorldBlock.Changed := False;
569
  end else if AWorldBlock is TStaticBlock then
570
  begin
571
    FStaIdx.Position := ((AWorldBlock.X * FHeight) + AWorldBlock.Y) * 12;
572
    index := TGenericIndex.Create(FStaIdx);
573
    size := AWorldBlock.GetSize;
574
    if (size > index.Size) or (index.Lookup < 0) then
575
    begin
576
      FStatics.Position := FStatics.Size;
577
      index.Lookup := FStatics.Position;
578
    end;
579
    index.Size := size;
580
    if size = 0 then
581
      index.Lookup := -1
582
    else
583
    begin
584
      FStatics.Position := index.Lookup;
585
      AWorldBlock.Write(FStatics);
586
    end;
587
    FStaIdx.Seek(-12, soFromCurrent);
588
    index.Write(FStaIdx);
589
    index.Free;
590
    AWorldBlock.Changed := False;
591
  end;
592
end;
593
594
function TLandscape.Validate: Boolean;
595
var
596
  blocks: Integer;
597
begin
598
  blocks := FWidth * FHeight;
599
  FStaIdx.Seek(0, soFromEnd); //workaround for TBufferedStream
600
  Result := (FMap.Size = (blocks * 196)) and (FStaIdx.Position = (blocks * 12));
601
end;
602
603
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream;
604
  ANetState: TNetState);
605
var
606
  x, y: Word;
607
  cell: TMapCell;
608
  subscriptions: TLinkedList;
609
  subscriptionItem: PLinkedItem;
610
  packet: TDrawMapPacket;
611
begin
612
  x := ABuffer.ReadWord;
613
  y := ABuffer.ReadWord;
614
615
  if not ValidateAccess(ANetState, alNormal, x, y) then Exit;
616
617
  cell := GetMapCell(x, y);
618
  if cell <> nil then
619
  begin
620
    cell.Altitude := ABuffer.ReadShortInt;
621
    cell.TileID := ABuffer.ReadWord;
622
    
623
    packet := TDrawMapPacket.Create(cell);
624
    subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)];
625
    subscriptionItem := nil;
626
    while subscriptions.Iterate(subscriptionItem) do
627
      CEDServerInstance.SendPacket(TNetState(subscriptionItem^.Data), packet, False);
628
    packet.Free;
629
    
630
    UpdateRadar(x, y);
631
  end;
632
end;
633
634
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream;
635
  ANetState: TNetState);
636
var
637
  x, y: Word;
638
  block: TSeperatedStaticBlock;
639
  staticItem: TStaticItem;
640
  targetStaticList: TStaticItemList;
641
  subscriptions: TLinkedList;
642
  subscriptionItem: PLinkedItem;
643
  packet: TInsertStaticPacket;
644
begin
645
  x := ABuffer.ReadWord;
646
  y := ABuffer.ReadWord;
647
648
  if not ValidateAccess(ANetState, alNormal, x, y) then Exit;
649
650
  block := GetStaticBlock(x div 8, y div 8);
651
  if block <> nil then
652
  begin
653
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
654
    staticItem.X := x;
655
    staticItem.Y := y;
656
    staticItem.Z := ABuffer.ReadShortInt;
657
    staticItem.TileID := ABuffer.ReadWord;
658
    staticItem.Hue := ABuffer.ReadWord;
659
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
660
    targetStaticList.Add(staticItem);
661
    SortStaticsList(targetStaticList);
662
    staticItem.Owner := block;
663
    
664
    packet := TInsertStaticPacket.Create(staticItem);
665
    subscriptions := FBlockSubscriptions[(y div 8) * FWidth + (x div 8)];
666
    subscriptionItem := nil;
667
    while subscriptions.Iterate(subscriptionItem) do
668
      CEDServerInstance.SendPacket(TNetState(subscriptionItem^.Data), packet, False);
669
    packet.Free;
670
    
671
    UpdateRadar(x, y);
672
  end;
673
end;
674
675
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream;
676
  ANetState: TNetState);
677
var
678
  block: TSeperatedStaticBlock;
679
  i: Integer;
680
  statics: TStaticItemList;
681
  staticInfo: TStaticInfo;
682
  staticItem: TStaticItem;
683
  subscriptions: TLinkedList;
684
  subscriptionItem: PLinkedItem;
685
  packet: TDeleteStaticPacket;
686
begin
687
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
688
689
  if not ValidateAccess(ANetState, alNormal, staticInfo.X, staticInfo.Y) then Exit;
690
691
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
692
  if block <> nil then
693
  begin
694
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
695
    for i := 0 to statics.Count - 1 do
696
    begin
697
      staticItem := statics[i];
698
      if (staticItem.Z = staticInfo.Z) and
699
         (staticItem.TileID = staticInfo.TileID) and
700
         (staticItem.Hue = staticInfo.Hue) then
701
      begin
702
        packet := TDeleteStaticPacket.Create(staticItem);
703
704
        staticItem.Delete;
705
        statics.Delete(i);
706
        
707
        subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth +
708
          (staticInfo.X div 8)];
709
        subscriptionItem := nil;
710
        while subscriptions.Iterate(subscriptionItem) do
711
          CEDServerInstance.SendPacket(TNetState(subscriptionItem^.Data),
712
            packet, False);
713
        packet.Free;
714
        
715
        UpdateRadar(staticInfo.X, staticInfo.Y);
716
        
717
        Break;
718
      end;
719
    end;
720
  end;
721
end;
722
723
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream;
724
  ANetState: TNetState);
725
var
726
  block: TSeperatedStaticBlock;
727
  i: Integer;
728
  statics: TStaticItemList;
729
  staticInfo: TStaticInfo;
730
  staticItem: TStaticItem;
731
  newZ: ShortInt;
732
  subscriptions: TLinkedList;
733
  subscriptionItem: PLinkedItem;
734
  packet: TElevateStaticPacket;
735
begin
736
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
737
738
  if not ValidateAccess(ANetState, alNormal, staticInfo.X, staticInfo.Y) then Exit;
739
740
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
741
  if block <> nil then
742
  begin
743
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
744
    for i := 0 to statics.Count - 1 do
745
    begin
746
      staticItem := statics[i];
747
      if (staticItem.Z = staticInfo.Z) and
748
         (staticItem.TileID = staticInfo.TileID) and
749
         (staticItem.Hue = staticInfo.Hue) then
750
      begin
751
        newZ := ABuffer.ReadShortInt;
752
        packet := TElevateStaticPacket.Create(staticItem, newZ);
753
754
        staticItem.Z := newZ;
755
        SortStaticsList(statics);
756
757
        subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth +
758
          (staticInfo.X div 8)];
759
        subscriptionItem := nil;
760
        while subscriptions.Iterate(subscriptionItem) do
761
          CEDServerInstance.SendPacket(TNetState(subscriptionItem^.Data),
762
            packet, False);
763
        packet.Free;
764
        
765
        UpdateRadar(staticInfo.X, staticInfo.Y);
766
767
        Break;
768
      end;
769
    end;
770
  end;
771
end;
772
773
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream;
774
  ANetState: TNetState);
775
var
776
  sourceBlock, targetBlock: TSeperatedStaticBlock;
777
  sourceSubscriptions, targetSubscriptions: TList;
778
  i: Integer;
779
  statics: TStaticItemList;
780
  staticInfo: TStaticInfo;
781
  staticItem: TStaticItem;
782
  newX, newY: Word;
783
  subscriptions: TLinkedList;
784
  subscriptionItem: PLinkedItem;
785
  insertPacket: TInsertStaticPacket;
786
  deletePacket: TDeleteStaticPacket;
787
  movePacket: TMoveStaticPacket;
788
begin
789
  staticItem := nil;
790
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
791
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
792
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
793
794
  //Check, if both, source and target, are within a valid region
795
  if not ValidateAccess(ANetState, alNormal, staticInfo.X, staticInfo.Y) then Exit;
796
  if not ValidateAccess(ANetState, alNormal, newX, newY) then Exit;
797
  
798
  if (staticInfo.X = newX) and (staticInfo.Y = newY) then Exit;
799
  
800
  if ((abs(staticInfo.X - newX) > 8) or (abs(staticInfo.Y - newY) > 8)) and
801
     (not ValidateAccess(ANetState, alAdministrator)) then Exit;
802
  
803
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
804
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
805
  if (sourceBlock <> nil) and (targetBlock <> nil) then
806
  begin
807
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
808
    i := 0;
809
    while (i < statics.Count) and (staticItem = nil) do
810
    begin
811
      staticItem := statics[i];
812
      if (staticItem.Z <> staticInfo.Z) or
813
         (staticItem.TileID <> staticInfo.TileID) or
814
         (staticItem.Hue <> staticInfo.Hue) then
815
      begin
816
        staticItem := nil;
817
      end;
818
      Inc(i);
819
    end;
820
    
821
    if staticItem <> nil then
822
    begin
823
      deletePacket := TDeleteStaticPacket.Create(staticItem);
824
      movePacket := TMoveStaticPacket.Create(staticItem, newX, newY);
825
826
      i := statics.IndexOf(staticItem);
827
      statics[i] := nil;
828
      statics.Delete(i);
829
830
      statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
831
      statics.Add(staticItem);
832
      staticItem.UpdatePos(newX, newY, staticItem.Z);
833
      staticItem.Owner := targetBlock;
834
835
      insertPacket := TInsertStaticPacket.Create(staticItem);
836
837
      SortStaticsList(statics);
838
839
      sourceSubscriptions := TList.Create;
840
      subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)];
841
      subscriptionItem := nil;
842
      while subscriptions.Iterate(subscriptionItem) do
843
        sourceSubscriptions.Add(subscriptionItem^.Data);
844
845
      targetSubscriptions := TList.Create;
846
      subscriptions := FBlockSubscriptions[(newY div 8) * FWidth + (newX div 8)];
847
      subscriptionItem := nil;
848
      while subscriptions.Iterate(subscriptionItem) do
849
        targetSubscriptions.Add(subscriptionItem^.Data);
850
851
      for i := 0 to sourceSubscriptions.Count - 1 do
852
      begin
853
        if targetSubscriptions.IndexOf(sourceSubscriptions.Items[i]) > -1 then
854
          CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), movePacket, False)
855
        else
856
          CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), deletePacket, False);
857
      end;
858
859
      for i := 0 to targetSubscriptions.Count - 1 do
860
      begin
861
        if sourceSubscriptions.IndexOf(targetSubscriptions.Items[i]) = -1 then
862
          CEDServerInstance.SendPacket(TNetState(sourceSubscriptions.Items[i]), insertPacket, False);
863
      end;
864
      
865
      UpdateRadar(staticInfo.X, staticInfo.Y);
866
      UpdateRadar(newX, newY);
867
868
      insertPacket.Free;
869
      deletePacket.Free;
870
      movePacket.Free;
871
872
      sourceSubscriptions.Free;
873
      targetSubscriptions.Free;
874
    end;
875
  end;
876
end;
877
878
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream;
879
  ANetState: TNetState);
880
var
881
  block: TSeperatedStaticBlock;
882
  i: Integer;
883
  statics: TStaticItemList;
884
  staticInfo: TStaticInfo;
885
  staticItem: TStaticItem;
886
  newHue: Word;
887
  subscriptions: TLinkedList;
888
  subscriptionItem: PLinkedItem;
889
  packet: THueStaticPacket;
890
begin
891
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
892
893
  if not ValidateAccess(ANetState, alNormal, staticInfo.X, staticInfo.Y) then Exit;
894
895
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
896
  if block <> nil then
897
  begin
898
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
899
    for i := 0 to statics.Count - 1 do
900
    begin
901
      staticItem := statics[i];
902
      if (staticItem.Z = staticInfo.Z) and
903
         (staticItem.TileID = staticInfo.TileID) and
904
         (staticItem.Hue = staticInfo.Hue) then
905
      begin
906
        newHue := ABuffer.ReadWord;
907
        packet := THueStaticPacket.Create(staticItem, newHue);
908
909
        staticItem.Hue := newHue;
910
911
        subscriptions := FBlockSubscriptions[(staticInfo.Y div 8) * FWidth + (staticInfo.X div 8)];
912
        subscriptionItem := nil;
913
        while subscriptions.Iterate(subscriptionItem) do
914
          CEDServerInstance.SendPacket(TNetState(subscriptionItem^.Data), packet, False);
915
        packet.Free;
916
917
        Break;
918
      end;
919
    end;
920
  end;
921
end;
922
923
procedure TLandscape.OnLargeScaleCommandPacket(ABuffer: TEnhancedMemoryStream;
924
  ANetState: TNetState);
925
var
926
  areaInfo: array of TAreaInfo;
927
  areaCount: Byte;
928
  i: Integer;
929
  blockX, blockY, cellX, cellY, x, y: Word;
930
  realBlockX, realBlockY, realCellX, realCellY: Word;
931
  blockOffX, cellOffX, modX, blockOffY, cellOffY, modY: Integer;
932
  blockID, cellID: Cardinal;
933
  emptyBits: TBits;
934
  bitMask: array of TBits;
935
  mapTile: TMapCell;
936
  statics: TStaticItemList;
937
  operations: TList;
938
  clients: array of record
939
    NetState: TNetState;
940
    Blocks: TBlockCoordsArray;
941
  end;
942
  netState: TNetState;
943
  subscriptions: TLinkedList;
944
  subscriptionItem: PLinkedItem;
945
  cmOperation: TLSCopyMove;
946
  additionalAffectedBlocks: TBits;
947
begin
948
  if not ValidateAccess(ANetState, alAdministrator) then Exit;
949
  Writeln(TimeStamp, ANetState.Account.Name, ' begins large scale operation');
950
  CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssOther,
951
    Format('%s is performing large scale operations ...', [ANetState.Account.Name])));
952
953
  //Bitmask
954
  emptyBits := TBits.Create(64);
955
  SetLength(bitMask, FWidth * FHeight);
956
  for i := Low(bitMask) to High(bitMask) do
957
    bitMask[i] := TBits.Create(64);
958
  //'additionalAffectedBlocks' is used to store whether a certain block was
959
  //touched during an operation which was designated to another block (for
960
  //example by moving items with an offset). This is (indirectly) merged later
961
  //on.
962
  additionalAffectedBlocks := TBits.Create(FWidth * FHeight);
963
964
  areaCount := ABuffer.ReadByte;
965
  SetLength(areaInfo, areaCount);
966
  for i := 0 to areaCount - 1 do
967
  begin
968
    areaInfo[i].Left := Max(ABuffer.ReadWord, 0);
969
    areaInfo[i].Top := Max(ABuffer.ReadWord, 0);
970
    areaInfo[i].Right := Min(ABuffer.ReadWord, FCellWidth - 1);
971
    areaInfo[i].Bottom := Min(ABuffer.ReadWord, FCellHeight - 1);
972
    for x := areaInfo[i].Left to areaInfo[i].Right do
973
      for y := areaInfo[i].Top to areaInfo[i].Bottom do
974
      begin
975
        blockID := (x div 8) * FHeight + (y div 8);
976
        cellID := (y mod 8) * 8 + (x mod 8);
977
        bitMask[blockID].Bits[cellID] := True;
978
      end;
979
  end;
980
  
981
  //client blocks
982
  SetLength(clients, 0);
983
  CEDServerInstance.TCPServer.IterReset;
984
  while CEDServerInstance.TCPServer.IterNext do
985
  begin
986
    netState := TNetState(CEDServerInstance.TCPServer.Iterator.UserData);
987
    if netState <> nil then
988
    begin
989
      SetLength(clients, Length(clients) + 1);
990
      clients[High(clients)].NetState := netState;
991
      SetLength(clients[High(clients)].Blocks, 0);
992
    end;
993
  end;
994
995
  operations := TList.Create;
996
  
997
  cmOperation := nil;
998
  if ABuffer.ReadBoolean then
999
  begin
1000
    cmOperation := TLSCopyMove.Init(ABuffer, Self);
1001
    if (cmOperation.OffsetX <> 0) or (cmOperation.OffsetY <> 0) then
1002
    begin
1003
      operations.Add(cmOperation);
1004
1005
      if cmOperation.OffsetX > 0 then
1006
      begin
1007
        blockOffX := FWidth - 1;
1008
        cellOffX := 7;
1009
        modX := -1;
1010
      end else
1011
      begin
1012
        blockOffX := 0;
1013
        cellOffX := 0;
1014
        modX := 1;
1015
      end;
1016
1017
      if cmOperation.OffsetY > 0 then
1018
      begin
1019
        blockOffY := FHeight - 1;
1020
        cellOffY := 7;
1021
        modY := -1;
1022
      end else
1023
      begin
1024
        blockOffY := 0;
1025
        cellOffY := 0;
1026
        modY := 1;
1027
      end;
1028
    end else
1029
      FreeAndNil(cmOperation);
1030
  end;
1031
  if cmOperation = nil then
1032
  begin
1033
    blockOffX := 0;
1034
    cellOffX := 0;
1035
    modX := 1;
1036
    blockOffY := 0;
1037
    cellOffY := 0;
1038
    modY := 1;
1039
  end;
1040
  if ABuffer.ReadBoolean then operations.Add(TLSSetAltitude.Init(ABuffer, Self));
1041
  if ABuffer.ReadBoolean then operations.Add(TLSDrawTerrain.Init(ABuffer, Self));
1042
  if ABuffer.ReadBoolean then operations.Add(TLSDeleteStatics.Init(ABuffer, Self));
1043
  if ABuffer.ReadBoolean then operations.Add(TLSInsertStatics.Init(ABuffer, Self));
1044
  
1045
  FRadarMap.BeginUpdate;
1046
  for blockX := 0 to FWidth - 1 do
1047
  begin
1048
    realBlockX := blockOffX + modX * blockX;
1049
    for blockY := 0 to FHeight - 1 do
1050
    begin
1051
      realBlockY := blockOffY + modY * blockY;
1052
      blockID := (realBlockX * FHeight) + realBlockY;
1053
      if bitMask[blockID].Equals(emptyBits) then Continue;
1054
      
1055
      for cellY := 0 to 7 do
1056
      begin
1057
        realCellY := cellOffY + modY * cellY;
1058
        for cellX := 0 to 7 do
1059
        begin
1060
          realCellX := cellOffX + modX * cellX;
1061
          if bitMask[blockID].Bits[(realCellY * 8) + realCellX] then
1062
          begin
1063
            x := realBlockX * 8 + realCellX;
1064
            y := realBlockY * 8 + realCellY;
1065
            mapTile := GetMapCell(x, y);
1066
            statics := GetStaticList(x, y);
1067
            for i := 0 to operations.Count - 1 do
1068
              TLargeScaleOperation(operations.Items[i]).Apply(mapTile, statics,
1069
                additionalAffectedBlocks);
1070
            SortStaticsList(statics);
1071
              
1072
            UpdateRadar(x, y);
1073
          end;
1074
        end;
1075
      end;
1076
1077
      //Find out, which clients are affected by which blocks.
1078
      //This is used to efficiently update the block subscriptions.
1079
      subscriptions := FBlockSubscriptions[realBlockY * FWidth + realBlockX];
1080
      for i := Low(clients) to High(clients) do
1081
      begin
1082
        subscriptionItem := nil;
1083
        while subscriptions.Iterate(subscriptionItem) do
1084
        begin
1085
          if TNetState(subscriptionItem^.Data) = clients[i].NetState then
1086
          begin
1087
            SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1);
1088
            with clients[i].Blocks[High(clients[i].Blocks)] do
1089
            begin
1090
              X := realBlockX;
1091
              Y := realBlockY;
1092
            end;
1093
            Break;
1094
          end;
1095
        end;
1096
      end;
1097
      
1098
    end;
1099
  end;
1100
  
1101
  //additional blocks
1102
  for blockX := 0 to FWidth - 1 do
1103
  begin
1104
    for blockY := 0 to FHeight - 1 do
1105
    begin
1106
      blockID := (blockX * FHeight) + blockY;
1107
      if bitMask[blockID].Equals(emptyBits) and additionalAffectedBlocks[blockID] then
1108
      begin
1109
        //Update the information, which client is affected on which subscribed
1110
        //block.
1111
        subscriptions := FBlockSubscriptions[blockY * FWidth + blockX];
1112
        for i := Low(clients) to High(clients) do
1113
        begin
1114
          subscriptionItem := nil;
1115
          while subscriptions.Iterate(subscriptionItem) do
1116
          begin
1117
            if TNetState(subscriptionItem^.Data) = clients[i].NetState then
1118
            begin
1119
              SetLength(clients[i].Blocks, Length(clients[i].Blocks) + 1);
1120
              with clients[i].Blocks[High(clients[i].Blocks)] do
1121
              begin
1122
                X := blockX;
1123
                Y := blockY;
1124
              end;
1125
              Break;
1126
            end;
1127
          end;
1128
        end;
1129
        
1130
        UpdateRadar(blockX * 8, blockY * 8);
1131
        
1132
      end;
1133
    end;
1134
  end;
1135
  
1136
  //clean up
1137
  for i := Low(bitMask) to High(bitMask) do
1138
    bitMask[i].Free;
1139
  emptyBits.Free;
1140
  additionalAffectedBlocks.Free;
1141
  
1142
  for i := 0 to operations.Count - 1 do
1143
    TLargeScaleOperation(operations.Items[i]).Free;
1144
  operations.Free;
1145
  
1146
  //Update clients
1147
  FRadarMap.EndUpdate;
1148
  for i := Low(clients) to High(clients) do
1149
  begin
1150
    if Length(clients[i].Blocks) > 0 then
1151
    begin
1152
      CEDServerInstance.SendPacket(clients[i].NetState, TCompressedPacket.Create(
1153
        TBlockPacket.Create(clients[i].Blocks, nil)));
1154
      clients[i].NetState.LastAction := Now;
1155
    end;
1156
  end;
1157
1158
  CEDServerInstance.SendPacket(nil, TServerStatePacket.Create(ssRunning));
1159
  Writeln(TimeStamp, 'Large scale operation ended.');
1160
end;
1161
1162
end.
1163