Issues (1369)

classes/SN.php (1 issue)

1
<?php
2
/** @noinspection PhpDeprecationInspection */
3
/** @noinspection SqlResolve */
4
5
/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */
6
7
use DBAL\db_mysql;
8
use Player\userOptions;
9
use Common\Vector;
10
use Core\GlobalContainer;
11
use Unit\DBStaticUnit;
12
13
/**
14
 * Class SN
15
 *
16
 * Singleton
17
 */
18
class SN {
19
  /**
20
   * Flag that something was rendered
21
   *
22
   * @var bool
23
   */
24
  public static $gSomethingWasRendered = false;
25
26
  /**
27
   * @var GlobalContainer $gc
28
   */
29
  public static $gc;
30
31
  /**
32
   * Основная БД для доступа к данным
33
   *
34
   * @var db_mysql $db
35
   */
36
  public static $db;
37
  public static $db_name = '';
38
39
  /**
40
   * Настройки из файла конфигурации
41
   *
42
   * @var string
43
   */
44
  public static $cache_prefix = 'sn_';
45
  public static $sn_secret_word = '';
46
47
  /**
48
   * Конфигурация игры
49
   *
50
   * @var classConfig $config
51
   */
52
  public static $config;
53
54
55
  /**
56
   * Кэш игры
57
   *
58
   * @var classCache $cache
59
   */
60
  public static $cache;
61
62
  /**
63
   * @var classLocale $lang
64
   */
65
  public static $lang;
66
67
68
  /**
69
   * @var core_auth $auth
70
   */
71
  public static $auth = null;
72
73
74
  public static $user = array();
75
  /**
76
   * @var userOptions
77
   */
78
  public static $user_options;
79
80
  /** @var ?debug $debug */
81
  public static $debug = null;
82
83
84
  public static $options = array();
85
86
  /**
87
   * Is header already rendered?
88
   *
89
   * @var bool $headerRendered
90
   */
91
  public static $headerRendered = false;
92
93
  /** @var bool $sys_user_logged_in Is user logged in? TODO - move to user-related */
94
  public static $sys_user_logged_in = false;
95
96
  /*
97
  TODO Кэш:
98
  1. Всегда дешевле использовать процессор, чем локальную память
99
  2. Всегда дешевле использовать локальную память, чем общую память всех процессов
100
  3. Всегда дешевле использовать общую память всех процессов, чем обращаться к БД
101
102
  Кэш - многоуровневый: локальная память-общая память-БД
103
  БД может быть сверх-кэширующей - см. HyperNova. Это реализуется на уровне СН-драйвера БД
104
  Предусмотреть вариант, когда уровни кэширования совпадают, например когда нет xCache и используется общая память
105
  */
106
  //public static $cache; // Кэширующий объект - либо встроенная память, либо кэш в памяти с блокировками - находится внутри $db!!!!
107
  //public static $db; // Объект-БД - либо кэширующий объект с блокировками, либо БД
108
109
  // protected static $info = array(); // Кэш информации - инфо о юнитах, инфо о группах итд
110
111
  // TODO Автоматически заполнять эту таблицу. В случае кэша в памяти - делать show table при обращении к таблице
112
  public static $location_info = array(
113
    LOC_USER => array(
114
      P_TABLE_NAME => 'users',
115
      P_ID         => 'id',
116
      P_OWNER_INFO => array(),
117
    ),
118
119
    LOC_PLANET => array(
120
      P_TABLE_NAME => 'planets',
121
      P_ID         => 'id',
122
      P_OWNER_INFO => array(
123
        LOC_USER => array(
124
          P_LOCATION    => LOC_USER,
125
          P_OWNER_FIELD => 'id_owner',
126
        ),
127
      ),
128
    ),
129
130
    LOC_UNIT => array(
131
      P_TABLE_NAME => 'unit',
132
      P_ID         => 'unit_id',
133
      P_OWNER_INFO => array(
134
        LOC_USER => array(
135
          P_LOCATION    => LOC_USER,
136
          P_OWNER_FIELD => 'unit_player_id',
137
        ),
138
      ),
139
    ),
140
141
    LOC_QUE => array(
142
      P_TABLE_NAME => 'que',
143
      P_ID         => 'que_id',
144
      P_OWNER_INFO => array(
145
        array(
146
          P_LOCATION    => LOC_USER,
147
          P_OWNER_FIELD => 'que_player_id',
148
        ),
149
150
        array(
151
          P_LOCATION    => LOC_PLANET,
152
          P_OWNER_FIELD => 'que_planet_id_origin',
153
        ),
154
155
        array(
156
          P_LOCATION    => LOC_PLANET,
157
          P_OWNER_FIELD => 'que_planet_id',
158
        ),
159
      ),
160
    ),
161
162
    LOC_FLEET => array(
163
      P_TABLE_NAME => 'fleets',
164
      P_ID         => 'fleet_id',
165
      P_OWNER_INFO => array(
166
        array(
167
          P_LOCATION    => LOC_USER,
168
          P_OWNER_FIELD => 'fleet_owner',
169
        ),
170
171
        array(
172
          P_LOCATION    => LOC_USER,
173
          P_OWNER_FIELD => 'fleet_target_owner',
174
        ),
175
176
        array(
177
          P_LOCATION    => LOC_PLANET,
178
          P_OWNER_FIELD => 'fleet_start_planet_id',
179
        ),
180
181
        array(
182
          P_LOCATION    => LOC_PLANET,
183
          P_OWNER_FIELD => 'fleet_end_planet_id',
184
        ),
185
      ),
186
    ),
187
  );
188
189
  /**
190
   * @var callable[] $afterInit
191
   */
192
  public static $afterInit = [];
193
194
  public function __construct() {
195
196
  }
197
198
199
  public static function log_file($message, $spaces = 0) {
200
    if (self::$debug) {
201
      self::$debug->log_file($message, $spaces);
202
    }
203
  }
204
205
206
  /**
207
   * Возвращает информацию о записи по её ID
208
   *
209
   * @param int       $location_type
210
   * @param int|array $record_id_unsafe
211
   *    <p>int - ID записи</p>
212
   *    <p>array - запись пользователя с установленным полем P_ID</p>
213
   *
214
   * @return array|false
215
   *    <p>false - Нет записи с указанным ID</p>
216
   *    <p>array - запись</p>
217
   */
218
  public static function db_get_record_by_id($location_type, $record_id_unsafe) {
219
    $id_field       = static::$location_info[$location_type][P_ID];
220
    $record_id_safe = idval(is_array($record_id_unsafe) && isset($record_id_unsafe[$id_field]) ? $record_id_unsafe[$id_field] : $record_id_unsafe);
221
222
    return static::db_get_record_list($location_type, "`{$id_field}` = {$record_id_safe}", true);
223
  }
224
225
  public static function db_get_record_list($location_type, $filter = '', $fetch = false, $no_return = false) {
226
    $location_info = &static::$location_info[$location_type];
227
    $id_field      = $location_info[P_ID];
228
    $tableName     = $location_info[P_TABLE_NAME];
229
230
    $result = false;
231
232
//    $sqlResult = static::db_query_select(
233
//      "SELECT * FROM {{{$tableName}}}" . (($filter = trim($filter)) ? " WHERE {$filter}" : '')
234
//    );
235
    $query = "SELECT * FROM {{{$tableName}}}" . (($filter = trim($filter)) ? " WHERE {$filter}" : '');
236
    $query .= db_mysql::db_transaction_check(db_mysql::DB_TRANSACTION_WHATEVER) ? ' FOR UPDATE' : '';
237
238
    $sqlResult = self::$db->doquery($query, false);
239
240
    while ($row = db_fetch($sqlResult)) {
0 ignored issues
show
Deprecated Code introduced by
The function db_fetch() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

240
    while ($row = /** @scrutinizer ignore-deprecated */ db_fetch($sqlResult)) {
Loading history...
241
      $result[$row[$id_field]] = $row;
242
      if ($fetch) {
243
        break;
244
      }
245
    }
246
247
    if ($no_return) {
248
      return true;
249
    } else {
250
      return $fetch ? (is_array($result) ? reset($result) : false) : $result;
251
    }
252
  }
253
254
  public static function db_upd_record_by_id($location_type, $record_id, $set) {
255
    if (!($record_id = idval($record_id)) || !($set = trim($set))) {
256
      return false;
257
    }
258
259
    $location_info = &static::$location_info[$location_type];
260
    $id_field      = $location_info[P_ID];
261
    $table_name    = $location_info[P_TABLE_NAME];
262
    if ($result = self::$db->doquery("UPDATE {{{$table_name}}} SET {$set} WHERE `{$id_field}` = {$record_id}", false)) // TODO Как-то вернуть может быть LIMIT 1 ?
263
    {
264
      if (static::$db->db_affected_rows()) {
265
        // Обновляем данные только если ряд был затронут
266
        DBStaticUnit::cache_clear();
267
      }
268
    }
269
270
    return $result;
271
  }
272
273
  public static function db_upd_record_list($location_type, $condition, $set) {
274
    if (!($set = trim($set))) {
275
      return false;
276
    }
277
278
    $condition  = trim($condition);
279
    $table_name = static::$location_info[$location_type][P_TABLE_NAME];
280
281
    if ($result = self::$db->doquery("UPDATE {{{$table_name}}} SET " . $set . ($condition ? ' WHERE ' . $condition : ''))) {
282
283
      if (static::$db->db_affected_rows()) { // Обновляем данные только если ряд был затронут
284
        // Поскольку нам неизвестно, что и как обновилось - сбрасываем кэш этого типа полностью
285
        DBStaticUnit::cache_clear();
286
      }
287
    }
288
289
    return $result;
290
  }
291
292
  public static function db_ins_record($location_type, $set) {
293
    $set        = trim($set);
294
    $table_name = static::$location_info[$location_type][P_TABLE_NAME];
295
    if ($result = self::$db->doquery("INSERT INTO `{{{$table_name}}}` SET {$set}")) {
296
      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут
297
      {
298
        $record_id = SN::$db->db_insert_id();
299
        // Вытаскиваем запись целиком, потому что в $set могли быть "данные по умолчанию"
300
        $result = static::db_get_record_by_id($location_type, $record_id);
301
        // Очищаем второстепенные кэши - потому что вставленная запись могла повлиять на результаты запросов или локация или еще чего
302
        DBStaticUnit::cache_clear();
303
      }
304
    }
305
306
    return $result;
307
  }
308
309
  public static function db_del_record_by_id($location_type, $safe_record_id) {
310
    if (!($safe_record_id = idval($safe_record_id))) {
311
      return false;
312
    }
313
314
    $location_info = &static::$location_info[$location_type];
315
    $id_field      = $location_info[P_ID];
316
    $table_name    = $location_info[P_TABLE_NAME];
317
    if ($result = self::$db->doquery("DELETE FROM `{{{$table_name}}}` WHERE `{$id_field}` = {$safe_record_id}")) {
318
      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут
319
      {
320
        DBStaticUnit::cache_clear();
321
      }
322
    }
323
324
    return $result;
325
  }
326
327
  public static function db_del_record_list($location_type, $condition) {
328
    if (!($condition = trim($condition))) {
329
      return false;
330
    }
331
332
    $table_name = static::$location_info[$location_type][P_TABLE_NAME];
333
334
    if ($result = self::$db->doquery("DELETE FROM `{{{$table_name}}}` WHERE {$condition}")) {
335
      if (static::$db->db_affected_rows()) // Обновляем данные только если ряд был затронут
336
      {
337
        // Обнуление кэша, потому что непонятно, что поменялось
338
        DBStaticUnit::cache_clear();
339
      }
340
    }
341
342
    return $result;
343
  }
344
345
346
  /*
347
   * С $for_update === true эта функция должна вызываться только из транзакции! Все соответствующие записи в users и planets должны быть уже блокированы!
348
   *
349
   * $que_type
350
   *   !$que_type - все очереди
351
   *   QUE_XXX - конкретная очередь по планете
352
   * $user_id - ID пользователя
353
   * $planet_id
354
   *   $que_type == QUE_RESEARCH - игнорируется
355
   *   null - обработка очередей планет не производится
356
   *   false/0 - обрабатываются очереди всех планет по $user_id
357
   *   (integer) - обрабатываются локальные очереди для планеты. Нужно, например, в обработчике флотов
358
   *   иначе - $que_type для указанной планеты
359
   * $for_update - true == нужно блокировать записи
360
   *
361
   * TODO Работа при !$user_id
362
   * TODO Переформатировать вывод данных, что бы можно было возвращать данные по всем планетам и юзерам в одном запросе: добавить под-массивы 'que', 'planets', 'players'
363
   *
364
   */
365
  /** @noinspection PhpUnusedParameterInspection */
366
  public static function db_que_list_by_type_location($user_id, $planet_id = null, $que_type = false, $for_update = false) {
367
    if (!$user_id) {
368
      pdump(debug_backtrace());
369
      die('No user_id for que_get_que()');
370
    }
371
372
    $ques = array();
373
374
    $query = array();
375
376
    if ($user_id = idval($user_id)) {
377
      $query[] = "`que_player_id` = {$user_id}";
378
    }
379
380
    if ($que_type == QUE_RESEARCH || $planet_id === null) {
381
      $query[] = "`que_planet_id` IS NULL";
382
    } elseif ($planet_id) {
383
      $query[] = "(`que_planet_id` = {$planet_id}" . ($que_type ? '' : ' OR que_planet_id IS NULL') . ")";
384
    }
385
    if ($que_type) {
386
      $query[] = "`que_type` = {$que_type}";
387
    }
388
389
    $ques['items'] = static::db_get_record_list(LOC_QUE, implode(' AND ', $query));
390
391
    return que_recalculate($ques);
392
  }
393
394
395
  public static function loadFileSettings() {
396
    $dbsettings = array();
397
398
    require(SN_CONFIG_PATH);
399
    //self::$db_prefix = $dbsettings['prefix'];
400
    self::$cache_prefix = !empty($dbsettings['cache_prefix']) ? $dbsettings['cache_prefix'] : $dbsettings['prefix'];
401
    self::$db_name      = $dbsettings['name'];
402
    /** @noinspection SpellCheckingInspection */
403
    self::$sn_secret_word = $dbsettings['secretword'];
404
405
    self::services();
406
407
    unset($dbsettings);
408
  }
409
410
  public static function init_global_objects() {
411
    global $sn_cache, $config, $debug;
412
413
    $debug    = self::$debug = self::$gc->debug;
414
    self::$db = self::$gc->db;
415
    self::$db->sn_db_connect();
416
417
    self::$user_options = new userOptions(0);
418
419
    // Initializing global `cache` object
420
    $sn_cache = static::$cache = self::$gc->cache;
421
    $tables   = SN::$db->schema()->getSnTables();
422
    empty($tables) && die('DB error - cannot find any table. Halting...');
423
424
    // Initializing global `config` object
425
    $config = self::$config = self::$gc->config;
426
427
    // Initializing statics
428
    Vector::_staticInit(self::$config);
429
430
    // After init callbacks
431
    foreach (static::$afterInit as $callback) {
432
      if (is_callable($callback)) {
433
        $callback();
434
      }
435
    }
436
  }
437
438
  /**
439
   * @param int    $newStatus
440
   * @param string $newMessage
441
   *
442
   * @return int
443
   * @noinspection PhpUnusedParameterInspection
444
   */
445
  public static function gameDisable($newStatus = GAME_DISABLE_REASON, $newMessage = '') {
446
    /** @noinspection PhpCastIsUnnecessaryInspection */
447
    $old_server_status = intval(self::$config->pass()->game_disable);
448
449
    self::$config->pass()->game_disable = $newStatus;
450
451
    return $old_server_status;
452
  }
453
454
//  public static function gameEnable() {
455
//    self::$config->pass()->game_disable = GAME_DISABLE_NONE;
456
//  }
457
458
  /**
459
   * Is game disabled?
460
   *
461
   * @return bool
462
   */
463
  public static function gameIsDisabled() {
464
    return self::$config->pass()->game_disable != GAME_DISABLE_NONE;
465
  }
466
467
468
  /**
469
   * @return GlobalContainer
470
   */
471
  public static function services() {
472
    if (empty(self::$gc)) {
473
      self::$gc = new GlobalContainer(array(
474
        'cachePrefix' => self::$cache_prefix,
475
      ));
476
    }
477
478
    return self::$gc;
479
  }
480
481
}
482