tdengine 0.1.2

game server for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
-- container.lua
-- 容器类

-- 创建类模板
CONTAINER_TDCLS = tdcls();
CONTAINER_TDCLS.name = "CONTAINER_TDCLS";

-- 构造函数
function CONTAINER_TDCLS:create(para)
    -- 普通背包
    self.carry = {};
    setmetatable(self.carry, { __mode = "v" });
    self.owner = para.rid
end

-- 析构函数
function CONTAINER_TDCLS:destruct()
    -- 析构玩家容器
    for _, ob in pairs(self.carry) do
        destruct_object(ob);
    end
end

-- 定义公共接口,按照字母顺序排序

-- 合并道具
function CONTAINER_TDCLS:combine_to_pos(property, dst_pos)
    local pre_property = dst_pos;
    if not is_object(pre_property) then

        -- 取得目前位置对象
        pre_property = self:get_pos_carry(dst_pos);
        if not is_object(pre_property) then
            return (self:load_property(property, dst_pos));
        end
    end

    -- 判断是否可以合并
    if not pre_property:can_combine(property) then
        trace("原道具(%s/%d)无法与道具(%s/%d)进行合并。\n", pre_property:query("rid"),
              pre_property:query("class_id"), property:query("rid"), property:query("class_id"));
        return false;
    end

    -- 判断合并数量是否溢出
    if pre_property:query("amount") + property:query("amount") >
        CALC_ITEM_MAX_AMOUNT(property) then
        trace("原道具(%s/%d)与道具(%s/%d)进行合并后数量超过最大叠加数。\n", pre_property:query("rid"),
              pre_property:query("class_id"), property:query("rid"), property:query("class_id"));
        return false;
    end

    -- 合并道具

    -- 原道具增加个数
    pre_property:add_amount(property:query("amount"));

    -- 析构道具
    self:drop(property);

    return true;
end

function CONTAINER_TDCLS:drop(property)
    --判断道具对象是否存在
    if not property or not is_object(property) then
       trace("drop的道具对象不存在\n");
       return;
    end

    if string.len(property:query("pos") or "") > 0 then
        self:get_owner():notify_property_delete(get_ob_rid(property))
    end
        
    if is_not_in_db(property) then        
        -- 该道具未在数据库中,不需要执行删除数据库记录的操作
        self:unload_property(property, not_auto_notify);
        destruct_object(property);        
        return;
    end

    check_rid_vaild(get_ob_rid(property))

    -- 取得道具所在的数据库表
    local table_name = property:get_save_oper();
    local sql = SQL_D.delete_sql(table_name, {rid = property:query("rid")})
    DB_D.execute_db(table_name, sql)

    LOG_D.to_log(LOG_TYPE_DESTRUCT_PROPERTY, self.owner, property:get_rid(), tostring(property:query("class_id")), tostring(property:query("amount")), self:get_owner():query_log_channel());
    self:unload_property(property, not_auto_notify);
    destruct_object(property);
end

-- 取得所有下属物件
function CONTAINER_TDCLS:get_carry()
    return (dup(self.carry));
end

-- 根据class_id取得所有class_id对象
function CONTAINER_TDCLS:get_carry_by_class_id(class_id)

    local arr = {};
    local x, y;
    local read_pos = READ_POS;

    for pos, ob in pairs(self.carry) do
        x, y = read_pos(pos);
        if ob:query("class_id") == class_id then
            arr[#arr + 1] = ob;
        end
    end

    return arr;
end

-- 取得某一页面的所有物件
function CONTAINER_TDCLS:get_page_carry(page)

    local arr = {};
    local x, y;
    local read_pos = READ_POS;

    for pos, ob in pairs(self.carry) do
        x, y = read_pos(pos);
        if x == page then
            arr[#arr + 1] = ob;
        end
    end

    return arr;
end

function CONTAINER_TDCLS:get_range_page_carry(ps, pe, pages)
    local arr = {};
    local x, y;
    local read_pos = READ_POS;
    local page_table = {}
    for _,v in ipairs(pages or {}) do
        page_table[v] = true
    end

    for pos, ob in pairs(self.carry) do
        x, y = read_pos(pos);
        if (x >= ps and x <= pe) or (page_table[x]) then
            arr[#arr + 1] = ob;
        end
    end

    return arr;
end

-- 根据道具类型id获得所属页的所有物件
function CONTAINER_TDCLS:get_page_carry_by_class_id(class_id)

    -- 根据道具类型ID获得道具所在的page
    local page = PROPERTY_D.get_property_info(class_id)["item_type"];

    return (self:get_page_carry(page));
end

-- 根据道具类型ID获得背包中数量
function CONTAINER_TDCLS:get_property_amount(class_id)

    local amount = 0;
    for _, ob in pairs(self.carry) do
        if ob:query("class_id") == class_id then
            amount = amount + ob:query("amount");
        end
    end

    return amount;
end


-- 扣除指定rid 和 数量的道具
function CONTAINER_TDCLS:cost_property_by_rid(rid, amount, bonus_type)
    local ob = find_object_by_rid(rid)
    if not ob then
        return false
    end

    if not amount and ob:query("ob_type") and  ob:query("ob_type") ==  OB_TYPE_EQUIP then
        amount= 1
    end

    -- 先判断玩家背包是否有足够的物品,没有不做处理
    local num = ob:query("amount");
    if num < amount then
        return false;
    end
    
    -- 扣除背包道具
    local deduct_amount = ob:cost_amount(amount, bonus_type);
    amount = amount - deduct_amount;
    if amount <= 0 then
        return true;
    end
end

-- 扣除指定数量的道具
function CONTAINER_TDCLS:cost_property(class_id, amount, bonus_type)
    if not amount or amount <= 0 then
        return true
    end
    -- 先判断玩家背包是否有足够的物品,没有不做处理
    local num = self:get_property_amount(class_id);
    if num < amount then
        return false;
    end

    -- 扣除背包道具
    local deduct_amount, ob_rid;
    for _, ob in pairs(self.carry) do
        if ob:query("class_id") == class_id then

            ob_rid = ob:get_rid();
            deduct_amount = ob:cost_amount(amount, bonus_type);
            amount = amount - deduct_amount;
            if amount <= 0 then
                return true;
            end
        end
    end
end

-- 根据page , (pos begin->end) 获得物件集合
function  CONTAINER_TDCLS:get_page_range_carry(page, b, e)
    local arr = self:get_page_carry(page);
    if not arr then
        return;
    end

    local arr2 = {};
    local x, y;
    local read_pos = READ_POS;
    for _, ob in ipairs(arr) do
        x, y = read_pos(ob:query("pos"));
        if y >= b and y <= e then
            arr2[#arr2 + 1] = ob;
        end
    end

    return arr2;
end

-- 取得某物件的所在的位置
function CONTAINER_TDCLS:get_property_pos(property)
    return (property:query("pos"));
end

-- 取得某一位置上的物件
function CONTAINER_TDCLS:get_pos_carry(pos)
    --trace("__ CONTAINER_TDCLS:get_pos_carry(pos=%o) ___ self.carry=%o \n ", pos, self.carry)
    return self.carry[pos];
end

-- 取得空闲位置
function CONTAINER_TDCLS:get_free_pos(property, can_combine)
    local page = CONTAINER_D.get_page(property, self);
    local x, y;
    local size = self:get_container_size(page);
    if not size then
        return;
    end

    local pos, ob;
    -- 若 property 为可叠加道具,则优先取得已存在的可叠加的道具位置
    if can_combine and property:query("combine") and
        property:query("combine") > 0 then

        -- 从第一个位置开始遍历
        for i = 0, size - 1 do
            pos = MAKE_POS(page, i);

            -- 取得该位置的对象
            ob = self:get_pos_carry(pos);
            if is_object(ob) then
                -- 只对位置上存在的可叠加对象,进行判断
                if ob:can_combine(property) then
                    return pos;
                end
            end
        end
    end

    -- 从第一个位置开始遍历该页面的 pos,找到第一个空位
    for i = 0, size - 1 do
        pos = MAKE_POS(page, i);

        -- 取得该位置的对象
        ob = self:get_pos_carry(pos);
        if not ob then
            return pos;
        end
    end

    return nil;
end

-- 根据指定的page找到该page空格子的数量
function CONTAINER_TDCLS:get_free_amount_by_page(page)
    local size = self:get_container_size(page);

    if not size then
        return;
    end

    local number = 0;
    -- 从第一个位置开始遍历该页面的 pos
    for i = 0, size - 1 do
        pos = MAKE_POS(page, i);
        if not self:get_pos_carry(pos) then
            number = number + 1;
        end
    end

    return number;
end

-- 判断是否为可用的POS
function CONTAINER_TDCLS:is_available_pos(pos)
    local x,y = READ_POS(pos);

    local size = self:get_container_size(x);
    if not size then
        return;
    end

    return (y <= size - 1);
end

-- 根据class_id找到空闲位置的数量
-- can_combine: 是否考虑可叠加
function CONTAINER_TDCLS:get_free_amount_by_class_id(class_id, can_combine)

    -- 取得item_info
    local item_info = PROPERTY_D.get_property_info(class_id);
    if not item_info then
        return 0;
    end

    -- 取得所在的page
    local page = item_info["item_type"];

    -- 取得空格子数量
    local free_amount = self:get_free_amount_by_page(page);

    -- 不考虑可叠加
    if not can_combine then
        return free_amount;

    -- 考虑可叠加
    else
        -- 取得可叠加最大值
        local combine_amount = CALC_ITEM_MAX_AMOUNT(class_id);

        -- 本身就不可叠加
        if combine_amount <= 1 then
            return free_amount;
        end

        -- 开始计算可叠加数量
        free_amount = free_amount * combine_amount;

        -- 取得指定class_id的对象数组
        local properties = self:get_carry_by_class_id(class_id);
        for i, property in ipairs(properties) do
            free_amount = free_amount + (combine_amount - property:query("amount"));
        end

        return free_amount;
    end
end

-- 根据page和number判断空闲位置是否足够
function CONTAINER_TDCLS:check_free_amount_by_page(page, number)
    -- 空闲数量
    local free_amount = 0;
    local pos;

    -- 遍历该页面的空pos
    local size = self:get_container_size(page);
    if not size then
        return false;
    end
    for i = 0, size - 1 do
        pos = MAKE_POS(page, i);
        if not self:get_pos_carry(pos) then
            free_amount = free_amount + 1;
            if free_amount >= number then
                -- 空格子足够则返回
                return true;
            end
        end
    end

    return false;
end

-- 根据class_id和number判断空闲位置是否足够
function CONTAINER_TDCLS:check_free_amount_by_class_id(class_id, number)

    -- 空闲数量
    local free_amount = 0;

    -- 取得可叠加最大值
    local combine_amount = CALC_ITEM_MAX_AMOUNT(class_id);
    if combine_amount < 1 then
        return false;
    end

    -- 遍历该页面的空pos
    local page = PROPERTY_D.get_property_info(class_id)["item_type"];
    local size = self:get_container_size(page);
    if not size then
        return false;
    end
    for i = 0, size - 1 do
        pos = MAKE_POS(page, i);
        if not self:get_pos_carry(pos) then
            free_amount = free_amount + combine_amount;
            if free_amount >= number then
                -- 空格子足够则返回
                return true;
            end
        end
    end

    -- 如果是不可叠加,直接返回
    if combine_amount <= 1 then
        return false;
    end

    -- 遍历玩家的carry
    for _, property in pairs(self.carry) do
        if property:query("class_id") == class_id then
            free_amount = free_amount + (combine_amount - property:query("amount"));
            if free_amount >= number then
                return true;
            end
        end
    end

    return false;
end

-- 根据多个class_id和amount判断空闲位置是否足够
-- (不考虑可叠加,只判断空格数量和所需数量)
-- item_list =
-- {
--    {class_id=*, amount=*},
--    {class_id=*, amount=*},
--    ...
-- }
function CONTAINER_TDCLS:check_free_amount_by_multi_class_id(item_list)

    -- 先按照背包页面分类,每个页面所需空格数量
    local page_amount = {};
    local page;
    for _, info in ipairs(item_list) do
        page = PROPERTY_D.get_property_info(info.class_id)["item_type"];
        -- 认为info.amount>1是可叠加的并且不超过可叠加数
        page_amount[page] = (page_amount[page] or 0) + 1;
    end

    local rest_amount;
    -- 遍历每个页面,看剩余空格够不够
    for page, amount in pairs(page_amount) do
        rest_amount = self:get_free_amount_by_page(page);
        if rest_amount < amount then
            return false;
        end
    end

    return true;
end

-- 增加指定数量的道具、装备
function CONTAINER_TDCLS:add_property(class_id, amount, bonus_type)

    if not amount then
        amount = 1;
    end

    if not bonus_type then
        bonus_type = BONUS_TYPE_GD;
    end

    local combine = 1;
    if PROPERTY_D.get_item_dbase(class_id) then
        combine = CALC_ITEM_MAX_AMOUNT(class_id);
    end

    -- 根据可叠加数来构造bonus_info
    local property_list = {};
    while amount > 0 do
        local info = { ob=self, class_id=class_id };
        if amount > combine then
            info["amount"] = combine;
        else
            info["amount"] = amount;
        end
        property_list[#property_list+1] = info;
        amount = amount - combine;
    end

    -- 执行奖励
    local bonus_info = { property = property_list };
    BONUS_D.do_bonus(bonus_info, bonus_type);
end

-- 根据指定的page找到该page空闲位置
function CONTAINER_TDCLS:get_free_pos_by_page(page)
    local size = self:get_container_size(page);

    if not size then
        return;
    end

    local pos;
    -- 从第一个位置开始遍历该页面的 pos
    for i = 0, size - 1 do
        pos = MAKE_POS(page, i);
        if not self:get_pos_carry(pos) then
            return pos;
        end
    end

    return nil;
end

-- 取得某一页面指定范围的物件
function CONTAINER_TDCLS:get_page_carry_by_region(page, range, idx)

    local size = self:get_container_size(page);
    if not size then
        return;
    end

    -- 取得指定区间开始和结束索引
    local b = math.floor(size/range)*(idx-1);
    local e = b + range - 1;

    return (self:get_page_range_carry(page, b, e));
end

-- 根据指定的page找到该page和指定区间的空闲位置
function CONTAINER_TDCLS:get_free_pos_by_region(page, range, idx)
    local size = self:get_container_size(page);
    if not size then
        return;
    end

    -- 取得指定区间开始和结束索引
    local b = math.floor(size/range)*(idx-1);
    local e = b + range - 1;

    local pos;
    -- 从第一个位置开始遍历该页面的pos
    for i = b, e - 1 do
        pos = MAKE_POS(page, i);
        if not self:get_pos_carry(pos) then
            return pos, b;
        end
    end

    return nil;
end

function CONTAINER_TDCLS:init_property(property)
    assert(property["pos"] and property["class_id"] and property["rid"], "pos class_id rid must be exist")
    assert(self.carry[property["pos"]] == nil, "carry pos must nil")
    property = PROPERTY_D.clone_object_from(property["class_id"], property, true)
    if not property then
        return false
    end
    -- 将物件放入窗口中
    property:set("owner", self.owner);
    property:set_temp("container", self.owner);

    -- 记录物件的位置索引
    self.carry[property:query("pos")] = property;

    return true;
end

function CONTAINER_TDCLS:recieve_property(info, check_enough)

    assert(info["class_id"] ~= nil, "class_id must no empty")
    local item_info = PROPERTY_D.get_item_or_equip_info(info["class_id"])
    if not item_info then
        return false;
    end

    local page = CONTAINER_D.get_page_by_data(item_info);
    local size = self:get_container_size(page);
    if not size then
        return false;
    end
    info["amount"] = info["amount"] or 1
    local max_amount = CALC_ITEM_MAX_AMOUNT(item_info)
    local pos, left_amount = nil, info["amount"]

    local gain_list = {}
    local empty_slot = {}
    for i = 1, size - 1 do
        pos = MAKE_POS(page, i)
        local property = self:get_pos_carry(pos)
        if not property then
            empty_slot[#empty_slot+1] = pos
        elseif property:query("class_id") == info["class_id"] then
            local get_amount = math.max(0, max_amount - property:query("amount"))
            if get_amount > 0 then
                local real_get = math.min(get_amount, left_amount)
                table.insert(gain_list, { pos = pos, amount = real_get })
                left_amount = left_amount - real_get
            end
            if left_amount <= 0 then
                break
            end
        end
    end

    if left_amount > 0 and #empty_slot > 0 then 
        for _, pos in ipairs(empty_slot) do 
            local real_get = math.min(max_amount, left_amount)
            table.insert(gain_list, { pos = pos, amount = real_get })
            left_amount = left_amount - real_get
            if left_amount <= 0 then break end
        end
    end

    if check_enough and left_amount > 0 then 
        return false, "背包空间不足"
    end
    local result_list = {}
    for _,v in ipairs(gain_list) do
        local property = PROPERTY_D.clone_object_from(info["class_id"], merge(dup(info), {amount = v.amount}), false)
        self:load_property(property, v.pos)
        local property = self.carry[v.pos]
        table.insert(result_list, {rid = get_ob_rid(property), class_id = info["class_id"], amount = v.amount, pos = v.pos, ob_type = property:query("ob_type")})
    end
    return true, result_list
end

-- 加载道具
function CONTAINER_TDCLS:load_property(property, dst_pos, not_auto_notify, not_auto_arrange)
    if property:query_temp("container") == self.owner then
        -- 道具已在容器中,需要先从容器中移除
        self:unload_property(property, not_auto_notify);
    end

    -- 判断目标位置是否已被占用
    if self:is_pos_occuppied(dst_pos) then
        -- 若该位置为自动获取的,则尝试合并道具
        return (self:combine_to_pos(property, dst_pos));
    end
    local owner_rid = self.owner
    local owner_ob = self:get_owner()
    LOG_D.to_log(LOG_TYPE_CREATE_PROPERTY, self.owner, property:get_rid(), tostring(property:query("class_id")), tostring(property:query("amount")), self:get_owner():query_log_channel());
    -- 记录物件的位置
    property:set("pos", dst_pos);

    -- 将物件放入窗口中
    property:set("owner", self.owner);
    property:set_temp("container", self.owner);

    -- 记录物件的位置索引
    self.carry[dst_pos] = property;
    -- 通知物件加载
    self:get_owner():notify_property_loaded(property:get_rid())

    return true;
end

function CONTAINER_TDCLS:get_owner()
    return find_object_by_rid(self.owner)
end

-- 卸载道具
function CONTAINER_TDCLS:unload_property(property, not_auto_notify)
    local pos = property:query("pos");
    local property_rid = property:query("rid");

    -- 删除该物件记录的 container
    property:delete_temp("container");
    if pos then
        -- 背包物品析构,从 carry 中移除该物件
        self.carry[pos] = nil;
    end

    return true;
end

-- 判断位置是否已被占用
function CONTAINER_TDCLS:is_pos_occuppied(pos)
    return (is_object(self.carry[pos]));
end

-- 取得容器实际的容量,否则取定义的容量大小
function CONTAINER_TDCLS:get_container_size(page)
    local container_size = self:query("container_size") or {};
    local size = container_size[page] or MAX_PAGE_SIZE[page];
    return size;
end

-- 设置容器大小
function CONTAINER_TDCLS:set_container_size(page, size)
    local container_size = self:query("container_size") or {};
    container_size[page] = size;
    self:set("container_size", container_size);
end

-- 交换位置
function CONTAINER_TDCLS:switch_pos(src_pos, dst_pos)

    local src_ob = self.carry[src_pos];
    local dst_ob = self.carry[dst_pos];

    self.carry[src_pos] = nil;
    self.carry[dst_pos] = nil;

    if is_object(src_ob) then
        src_ob:set("pos", dst_pos);
        self.carry[dst_pos] = src_ob;
        src_ob:notify_fields_updated("pos");
    end

    if is_object(dst_ob) then
        dst_ob:set("pos", src_pos);
        self.carry[src_pos] = dst_ob;
        dst_ob:notify_fields_updated("pos");
    end
end

-- 交换位置不带通知
function CONTAINER_TDCLS:switch_carry_pos_without_notify(src_pos, dst_pos)

    local src_ob = self.carry[src_pos];
    local dst_ob = self.carry[dst_pos];

    self.carry[src_pos] = nil;
    self.carry[dst_pos] = nil;

    if src_ob and src_ob:is_equip() == true then
        --src_ob:set("pos", dst_pos);
        self.carry[dst_pos] = src_ob;
        --src_ob:notify_fields_updated("pos");
    end

    if dst_ob and dst_ob:is_equip() == true then
        --dst_ob:set("pos", src_pos);
        self.carry[src_pos] = dst_ob;
        --dst_ob:notify_fields_updated("pos");
    end
end