supernova-ws /
SuperNova
| 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)) { |
||
| 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); |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 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 |