Statistics
| Branch: | Tag: | Revision:

root / Server / ULandscape.pas @ 13:c78b5eafa10e

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