Statistics
| Branch: | Tag: | Revision:

root / Server / ULandscape.pas @ 157:0b95089e72d4

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