sendMessage, massmessage and sysmessage are POSTed to directly, bypassing
admin.php's central csrf_verify(). Add csrf_verify() (after the admin access
check, via the shared GameEngine/Admin/csrf.php) and csrf_field() in their
forms (Newmessage.tpl, massmessage.tpl, sysmessage.tpl; the mass/sys templates
have both a prepare and an execute form).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The post-delete admin-log block referenced variables that were never defined
($admid/$adminID/$medalid/$uid), so on PHP 8.1+ (mysqli throws on error) the
malformed INSERT raised an uncaught mysqli_sql_exception → HTTP 500 after the
medal was already deleted. Use the correct ids ($admid from session, $uid from
POST), look up the target player's username (escaped), and redirect to the
sanitized $uid.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
editAli, delAli, medals, delallymedal, delallymedalbyaid, delallymedalbyweek
and deletemedalbyweek are POSTed to directly, bypassing admin.php's central
csrf_verify(). Add csrf_verify() (after the admin access check, via the shared
GameEngine/Admin/csrf.php) and csrf_field() in their forms (playermedals.tpl,
editAli.tpl, delAli.tpl, delmedal.tpl, allymedals.tpl, delallymedal.tpl).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The alliance/editAli/delAli pages are linked all over the admin panel
(?p=alliance&aid=, ?p=editAli, ?p=delAli) but were never in
admin_validated_page()'s whitelist, so admin.php fell back to search.tpl and
the pages never showed. Add them to the whitelist plus switch cases for the
breadcrumb (the templates resolve $aid/$alidata themselves from $_GET, like
editSitter/editPassword).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
editVillageOwner, renameVillage, editBuildings and editResources are POSTed
to directly, bypassing admin.php's central csrf_verify(). Add csrf_verify()
(after the admin access check, via the shared GameEngine/Admin/csrf.php) and
csrf_field() in their forms (editVillage.tpl, village.tpl, editResources.tpl).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
addTroops and addABTroops are POSTed to directly, bypassing admin.php's
central csrf_verify(). Add csrf_verify() (after the admin access check, via
the shared GameEngine/Admin/csrf.php) and csrf_field() in their forms.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
On the rally point incoming tab, the number of an incoming unit type is never
revealed: it is always shown as a "?". When that stack is smaller than the
defender's rally point (gid 16) level, the "?" is rendered in solid black
bold, matching original Travian behaviour (e.g. rally point level 20 and an
incoming 19 praetorians shows a bold "?"). The eyesight artifact still reveals
which troop types are present (0 for the absent ones). Scope: village
attacks/raids only.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Extract the per-branch defender target resolution and battle-environment
setup into two private helpers: resolveVillageTarget() and
resolveOasisTarget(). Each returns the target owner (tribe/alliance), map
info, conquest flag and the battle parameters (wall, armory/blacksmith
tech, residence, siege masonry); the village helper also returns the
evasion inputs. Both are read-only (no DB writes).
The foreach body keeps handleEvasion(), buildDefenderUnits() and
buildAttackerUnits() as explicit, ordered calls, so the village and oasis
branches are now symmetric orchestration.
Behaviour-preserving. The building/tech reads now run inside the helper
before handleEvasion(); they read buildings and technology only (never the
troops handleEvasion() may move), so the result is unchanged. A few
dead locals are dropped (playerunit, wallgid, w; the redundant
DefenderUnit/def_ab re-inits).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the per-attack, target-independent context resolution (attacker
village/owner tribe and alliance, war references, base flags) into a
private helper. Read-only, behaviour-preserving: the three repeated
getCachedUser() lookups on the attacker owner are collapsed into one
(the user cache makes them idempotent).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The defender's units were gathered by two near-identical inline blocks
(village and oasis targets). Extract them into a single private method
buildDefenderUnits() returning the defender's own troops (normalised to
non-negative ints), the aggregated reinforcement totals (enforDefender) and
the raw reinforcement rows (enforcementarray).
Pure behaviour-preserving extraction:
- Both call sites assign the returned bundle; all downstream usages unchanged.
- The oasis reinforcement aggregation now uses the same isset-guarded loop as
the village one: identical numeric result, minus a latent PHP 8.3
"undefined array key" notice.
- The dead `$def_ab[$i] = 0` init that lived in the village normalisation loop
is dropped: it was unconditionally wiped by the later `$def_ab = []` before
any use.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The marketplace send tab (17.tpl) was refactored with an empty <script>
block, which dropped the `haendler` (available merchants) and `carry`
(per-merchant capacity) globals that add_res()/upd_res() in unx.js rely on.
Without them `ic = haendler * carry` evaluates to NaN, so clicking the
"(capacity)" link next to a resource (or the resource icon) no longer fills
the input. Restore the two globals so the max-per-merchant fill works again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>