diff --git a/Admin/admin.php b/Admin/admin.php index f7ce709b..718fd565 100644 --- a/Admin/admin.php +++ b/Admin/admin.php @@ -1,20 +1,23 @@ getUserArray($village['owner'], 1); $subpage = ADMIN_EDIT_RESOURCES . ' (' . e($village['name']) . ' » ' . e($user['username']) . ')'; } else { - // BUGFIX: original used $did which was only set in 'village' case, - // causing an undefined variable notice here. Now always defined above. $subpage = ADMIN_EDIT_RESOURCES . $did . ' not found)'; $village = null; } @@ -568,7 +560,6 @@ if ($page !== '') { } break; - // ── Alliance-context pages (require a valid ?aid=) ─────────────────── case 'alliance': $aid = admin_input_id($_GET, 'aid'); if ($aid !== null) { diff --git a/GameEngine/Automation/AutomationBattleResolution.php b/GameEngine/Automation/AutomationBattleResolution.php index 28711bd5..eff4c54e 100644 --- a/GameEngine/Automation/AutomationBattleResolution.php +++ b/GameEngine/Automation/AutomationBattleResolution.php @@ -1227,7 +1227,13 @@ trait AutomationBattleResolution { */ private function applyOwnDefenceCasualties($data, $targettribe, $battlepart) { global $database; - + + $targettribe = (int)$targettribe; + // FIX: daca satul a fost sters / owner 0 / oaza fara trib, nu avem ce sterge + if($targettribe < 1 || $targettribe > 9) { + return []; + } + $owndead = []; $unitlist = $database->getUnit($data['to'], false); $start = ($targettribe - 1) * 10 + 1; @@ -1319,7 +1325,7 @@ trait AutomationBattleResolution { } else { $tribe = 4; } - + if($tribe < 1 || $tribe > 9) $tribe = 4; // fallback la natura $start = ($tribe - 1) * 10 + 1; $end = ($tribe * 10); unset($dead); diff --git a/GameEngine/Database.php b/GameEngine/Database.php index cfce26ef..c7a7614e 100755 --- a/GameEngine/Database.php +++ b/GameEngine/Database.php @@ -60,6 +60,8 @@ include_once __DIR__ . '/Database/DatabaseHeroQueries.php'; include_once __DIR__ . '/Database/DatabaseStatisticsQueries.php'; include_once __DIR__ . '/Database/DatabaseArtefactQueries.php'; include_once __DIR__ . '/Database/DatabaseSystemQueries.php'; +// Chat general (server-wide), cerut de Catalin 07.09.2026 - separat de alliance chat (chat.php) +include_once __DIR__ . '/Database/DatabaseGlobalChatQueries.php'; use App\Database\IDbConnection; use App\Utils\Math; @@ -80,6 +82,7 @@ class MYSQLi_DB implements IDbConnection { use DatabaseStatisticsQueries; use DatabaseArtefactQueries; use DatabaseSystemQueries; + use DatabaseGlobalChatQueries; diff --git a/GameEngine/Database/DatabaseGlobalChatQueries.php b/GameEngine/Database/DatabaseGlobalChatQueries.php new file mode 100644 index 00000000..12a536fb --- /dev/null +++ b/GameEngine/Database/DatabaseGlobalChatQueries.php @@ -0,0 +1,176 @@ +bool, 'reason'=>string|null, 'mutedUntil'=>int|null] + */ + function postGlobalChatMessage($uid, $msg) { + $uid = (int) $uid; + $msg = trim((string) $msg); + + if ($uid <= 0) { + return ['ok' => false, 'reason' => 'notloggedin']; + } + + if ($msg === '') { + return ['ok' => false, 'reason' => 'empty']; + } + + // acelasi plafon ca la chat-ul de alianta existent (chat.msg e varchar(255); + // 250 lasa loc de siguranta pentru escaping/multi-byte) + $msg = function_exists('mb_substr') ? mb_substr($msg, 0, 250) : substr($msg, 0, 250); + + $mutedUntil = $this->getGlobalChatMuteStatus($uid); + if ($mutedUntil !== null) { + return ['ok' => false, 'reason' => 'muted', 'mutedUntil' => $mutedUntil]; + } + + list($euid) = $this->escape_input($uid); + + $lastRow = mysqli_fetch_assoc($this->query( + "SELECT date FROM " . TB_PREFIX . "chat_global WHERE id_user = $euid ORDER BY id DESC LIMIT 1" + )); + + $now = time(); + // rate-limit: minim 3 secunde intre 2 mesaje ale aceluiasi user + if ($lastRow && ($now - (int) $lastRow['date']) < 3) { + return ['ok' => false, 'reason' => 'ratelimit']; + } + + list($emsg) = $this->escape_input($msg); + + $this->query( + "INSERT INTO " . TB_PREFIX . "chat_global (id_user, date, msg) VALUES ($euid, $now, '$emsg')" + ); + + return ['ok' => true]; + } + + /** + * Mesajele din chat-ul general, gata de afisat (ordine cronologica). + * + * Mod 1 - incarcare initiala: ultimele $limit mesaje ($sinceId = 0). + * Mod 2 - poll incremental: doar mesajele noi, mai mari decat $sinceId + * (evita re-trimiterea ferestrei intregi la fiecare interogare din JS). + * + * Tag-ul de alianta si nivelul de access sunt rezolvate live, printr-un + * singur JOIN (nu o interogare per mesaj) - reflecta mereu alianta + * curenta a jucatorului, nu una "inghetata" la momentul postarii. + */ + function getGlobalChatMessages($limit = 30, $sinceId = 0) { + $limit = (int) $limit; + $sinceId = (int) $sinceId; + + $where = $sinceId > 0 ? "WHERE c.id > $sinceId" : ""; + $order = $sinceId > 0 ? "ORDER BY c.id ASC" : "ORDER BY c.id DESC"; + // plafon si pe polling-ul incremental (nu doar la incarcarea initiala) - + // daca un tab a stat minimizat/in background ore intregi (throttling de + // browser pe mobil), la revenire nu vrem un SELECT nemarginit + $limitSql = "LIMIT " . ($sinceId > 0 ? 200 : $limit); + + $q = "SELECT c.id, c.id_user, c.date, c.msg, u.username, u.access, a.tag AS ally_tag, a.id AS ally_id + FROM " . TB_PREFIX . "chat_global c + LEFT JOIN " . TB_PREFIX . "users u ON u.id = c.id_user + LEFT JOIN " . TB_PREFIX . "alidata a ON a.id = u.alliance AND u.alliance > 0 + $where + $order + $limitSql"; + + $rows = $this->mysqli_fetch_all($this->query($q)); + + // la incarcarea initiala am luat descrescator (ca sa prindem exact + // ultimele $limit), le intoarcem in ordine cronologica normala + if ($sinceId <= 0) { + $rows = array_reverse($rows); + } + + return $rows; + } + + /** + * Timestamp-ul (unix) pana la care userul e mutat din chat-ul general, + * sau null daca nu e mutat (inclusiv daca o mutare veche a expirat). + */ + function getGlobalChatMuteStatus($uid) { + $uid = (int) $uid; + + $row = mysqli_fetch_assoc($this->query( + "SELECT muted_until FROM " . TB_PREFIX . "chat_mutes WHERE id_user = $uid" + )); + + if (!$row) { + return null; + } + + $until = (int) $row['muted_until']; + return $until > time() ? $until : null; + } + + /** + * Info despre viewer-ul curent, folosita de client ca sa stie daca + * afiseaza controalele de moderare si daca dezactiveaza inputul (mutat). + */ + function getGlobalChatViewerInfo($uid) { + $uid = (int) $uid; + + $access = (int) $this->getUserField($uid, 'access', 0); + + return [ + 'uid' => $uid, + 'access' => $access, + 'isMod' => $access >= MULTIHUNTER, + 'mutedUntil' => $this->getGlobalChatMuteStatus($uid), + ]; + } + + /** + * Muteaza (temporar sau permanent, dupa $untilTimestamp) un user din + * chat-ul general. Ierarhia de rang (cine pe cine poate muta) se + * verifica in ajax.php inainte de a apela asta - metoda doar scrie + * starea, ca sa ramana usor de testat separat. + */ + function muteGlobalChatUser($targetUid, $mutedBy, $untilTimestamp, $reason = '') { + $targetUid = (int) $targetUid; + $mutedBy = (int) $mutedBy; + $untilTimestamp = (int) $untilTimestamp; + + list($ereason) = $this->escape_input((string) $reason); + $now = time(); + + $q = "INSERT INTO " . TB_PREFIX . "chat_mutes (id_user, muted_until, muted_by, reason, created) + VALUES ($targetUid, $untilTimestamp, $mutedBy, '$ereason', $now) + ON DUPLICATE KEY UPDATE muted_until = $untilTimestamp, muted_by = $mutedBy, reason = '$ereason', created = $now"; + + return $this->query($q) ? true : false; + } + + function unmuteGlobalChatUser($targetUid) { + $targetUid = (int) $targetUid; + + return $this->query( + "DELETE FROM " . TB_PREFIX . "chat_mutes WHERE id_user = $targetUid" + ) ? true : false; + } +} diff --git a/GameEngine/Lang/en.php b/GameEngine/Lang/en.php index edbde9f4..5b57607a 100755 --- a/GameEngine/Lang/en.php +++ b/GameEngine/Lang/en.php @@ -4404,3 +4404,22 @@ tz_def('TRAIN_BONUS_ARTIFACT', 'Artifact bonus'); tz_def('TRAIN_BONUS_HERO', 'Hero bonus'); tz_def('TRAIN_BONUS_ALLIANCE', 'Alliance bonus'); tz_def('TRAIN_BONUS_FINAL', 'Training time'); + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// GENERAL (SERVER-WIDE) CHAT - floating widget, footer.tpl + Templates/GlobalChat/widget.tpl +////////////////////////////////////////////////////////////////////////////////////////////////////// +tz_def('GCHAT_TITLE', 'World Chat'); +tz_def('GCHAT_PLACEHOLDER', 'Message...'); +tz_def('GCHAT_SEND', 'Send'); +tz_def('GCHAT_EMPTY', 'No messages yet.'); +tz_def('GCHAT_MUTED', 'You are muted from chat.'); +tz_def('GCHAT_RATELIMIT', 'You are sending messages too fast.'); +tz_def('GCHAT_CONFIRM_BLOCK', 'Block this player from chat permanently?'); +tz_def('GCHAT_MUTE_5M', 'Mute 5m'); +tz_def('GCHAT_MUTE_30M', 'Mute 30m'); +tz_def('GCHAT_MUTE_1H', 'Mute 1h'); +tz_def('GCHAT_MUTE_24H', 'Mute 24h'); +tz_def('GCHAT_BLOCK', 'Block'); +tz_def('GCHAT_UNMUTE', 'Unmute'); +tz_def('GCHAT_ADMIN_BADGE', 'Admin'); +tz_def('GCHAT_MH_BADGE', 'MH'); diff --git a/GameEngine/Lang/ro.php b/GameEngine/Lang/ro.php index 96c7f9f1..db5ab125 100644 --- a/GameEngine/Lang/ro.php +++ b/GameEngine/Lang/ro.php @@ -4141,4 +4141,22 @@ tz_def('SITTER_P_NOT_SITTING', 'Nu esti sitter pe niciun cont.'); tz_def('TRAIN_BONUS_ARTIFACT', 'Bonus artefact'); tz_def('TRAIN_BONUS_HERO', 'Bonus erou'); tz_def('TRAIN_BONUS_ALLIANCE', 'Bonus alianta'); -tz_def('TRAIN_BONUS_FINAL', 'Timp instruire'); \ No newline at end of file +tz_def('TRAIN_BONUS_FINAL', 'Timp instruire'); +////////////////////////////////////////////////////////////////////////////////////////////////////// +// CHAT GENERAL (server-wide) - widget plutitor, footer.tpl + Templates/GlobalChat/widget.tpl +////////////////////////////////////////////////////////////////////////////////////////////////////// +tz_def('GCHAT_TITLE', 'Chat general'); +tz_def('GCHAT_PLACEHOLDER', 'Mesaj...'); +tz_def('GCHAT_SEND', 'Trimite'); +tz_def('GCHAT_EMPTY', 'Niciun mesaj inca.'); +tz_def('GCHAT_MUTED', 'Esti mutat din chat.'); +tz_def('GCHAT_RATELIMIT', 'Trimiti mesaje prea repede.'); +tz_def('GCHAT_CONFIRM_BLOCK', 'Blochezi permanent acest jucator din chat?'); +tz_def('GCHAT_MUTE_5M', 'Mute 5m'); +tz_def('GCHAT_MUTE_30M', 'Mute 30m'); +tz_def('GCHAT_MUTE_1H', 'Mute 1h'); +tz_def('GCHAT_MUTE_24H', 'Mute 24h'); +tz_def('GCHAT_BLOCK', 'Blocheaza'); +tz_def('GCHAT_UNMUTE', 'Anuleaza mute'); +tz_def('GCHAT_ADMIN_BADGE', 'Admin'); +tz_def('GCHAT_MH_BADGE', 'MH'); diff --git a/Templates/GlobalChat/widget.tpl b/Templates/GlobalChat/widget.tpl new file mode 100644 index 00000000..d7d67f27 --- /dev/null +++ b/Templates/GlobalChat/widget.tpl @@ -0,0 +1,459 @@ + +