Commit Graph

1552 Commits

Author SHA1 Message Date
Ferywir 8d1a1cab38 fix(admin): verify CSRF token in troop admin Mods [#139] (#258)
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>
2026-06-23 08:43:18 +03:00
Ferywir b880622fd1 fix(admin): route password/protection pages and align username validation (#257) 2026-06-23 06:55:13 +03:00
Ferywir e49069a9c6 fix(admin): verify CSRF token in player-management admin Mods [#139] (#256) 2026-06-23 06:54:03 +03:00
Ferywir 6888a09b5f fix(admin): let editPlus subtract Plus/bonus days as the form advertises (#254) 2026-06-22 18:41:28 +03:00
Ferywir 6e79c47951 fix(admin): verify CSRF token in Plus/Gold admin Mods [#139] (#253) 2026-06-22 18:40:43 +03:00
Ferywir 9d2d7699a9 fix(profile): neutralize stored XSS in profile descriptions [#250] (#252) 2026-06-22 16:31:20 +03:00
novgorodschi catalin b9d36ba311 Fix also in admin panel removexss
Fix also in admin panel removexss
2026-06-22 12:40:07 +03:00
novgorodschi catalin c250a19f5a Fix #250
Fix #250
2026-06-22 12:34:31 +03:00
novgorodschi catalin 561933b23b Fix fLevel
Fix fLevel
2026-06-22 11:42:44 +03:00
novgorodschi catalin 41e0fd6313 Some fix
Some fix
2026-06-22 10:41:25 +03:00
novgorodschi catalin 337c94b981 Fix Admin Panel view & some PHP 8.1+
Fix Admin Panel view & some PHP 8.1+
2026-06-22 09:20:16 +03:00
Ferywir f581add125 feature(rally-point): mark incoming attacks + show per-troop travel time [#245] (#248) 2026-06-22 06:54:41 +03:00
Ferywir b7e943ea90 fix(admin): wire CSRF token into admin.php-routed forms [#139] (#244) 2026-06-20 06:44:12 +03:00
Ferywir 1ce1003153 fix(chat): stop echoing the raw INSERT query in add_data() [#139] (#243) 2026-06-19 13:31:21 +03:00
Ferywir b0e6680705 fix(chat): replace eval() of server output with JSON.parse() [#139] (#242) 2026-06-19 13:30:54 +03:00
Ferywir 1a797bab80 fix(admin): prevent SQL injection in editAccess Mod [#139] (#241) 2026-06-19 13:30:25 +03:00
Ferywir 9c726a05b9 fix(admin): re-check admin rank in addUsers Mod (defense-in-depth) [#139] (#240) 2026-06-18 19:09:02 +03:00
Ferywir 1b978470c6 fix(session): refresh the 30s user-cache after a player's own changes (#239) 2026-06-18 14:59:44 +03:00
Ferywir cdc736839e i18n(alliance): render alliance news-feed notices in each reader's language (EN/FR/RO) (#238) 2026-06-18 10:22:15 +03:00
Ferywir d008261678 i18n: render system in-game messages in each reader's language (EN/FR/RO) (#237) 2026-06-17 18:19:24 +03:00
Ferywir 51c9cc5b2c i18n: render battle reports in each reader's language (EN/FR/RO) (#236) 2026-06-17 16:24:37 +03:00
Catalin Novgorodschi abe36748d7 Clea Automation.php
Clean unused code
2026-06-17 08:56:34 +03:00
Ferywir 0b1bd44d22 Perf: cache zero building levels in getFieldLevelInVillage [#155] (#235) 2026-06-16 18:22:10 +03:00
Ferywir b7feb7f2a1 Perf(sendunitsComplete): batch-preload getMInfo in preloadBattleData [#155] (#234) 2026-06-16 16:53:51 +03:00
Ferywir 3cdc6a6920 Refactor(sendunitsComplete): extract handleVillageDestruction() [#155] (#233) 2026-06-16 13:26:51 +03:00
Ferywir b3e49a6ecf Refactor(sendunitsComplete): extract finalizeReturnOrDeath() [#155] (#232) 2026-06-16 13:06:56 +03:00
Ferywir 0750755bc1 Refactor(sendunitsComplete): extract sendBattleNotifications() [#155] (#231) 2026-06-16 12:48:44 +03:00
Ferywir 070df6d479 Refactor(sendunitsComplete): extract handleHeroPostBattle() [#155] (#230) 2026-06-16 11:47:34 +03:00
Ferywir 483da9fb18 Refactor(sendunitsComplete): extract resolveResourcesAfterBattle() [#155] (#229) 2026-06-16 10:57:55 +03:00
Ferywir 6d46c76dfb Refactor(sendunitsComplete): extract calculateHeroXpAndPoints() [#155] (#228) 2026-06-16 09:42:12 +03:00
Ferywir 509a4d4137 Refactor(sendunitsComplete): extract resolveVillageTarget()/resolveOasisTarget() [#155]
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>
2026-06-15 18:13:19 +03:00
Ferywir 4ba2507f9d Refactor(sendunitsComplete): extract resolveAttackContext() [#155]
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>
2026-06-15 17:40:04 +03:00
Ferywir 89b0fa50f5 Refactor(sendunitsComplete): extract buildDefenderUnits() [#155]
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>
2026-06-15 16:41:14 +03:00
Ferywir a2f5cfdbac Refactor(sendunitsComplete): extract buildAttackerUnits() [#155]
The attacking army was built by two near-identical inline blocks (village
and oasis targets). Extract them into a single private method
buildAttackerUnits() that returns the Attacker unit array (u<start..end> +
uhero) together with the catapult / ram / chief / scout unit ids used in the
report. The oasis target keeps its Nature siege/chief slots (37/38/39) via
the $isoasis flag.

Pure behaviour-preserving extraction: both call sites now assign the returned
bundle, so all downstream usages remain unchanged. The unit-id picks are
initialised to null (they are always set for the real attacker tribes 1/2/3/5;
only the unreachable Nature-attacker case differs, which silences a latent
PHP 8.3 undefined-variable notice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:27:09 +03:00
Ferywir 76aa781ee6 Refactor(sendunitsComplete): extract applyRamDamage() [#155]
Move the ram-damage handling out of sendunitsComplete() into a dedicated
private method applyRamDamage(). For a normal attack (type 3) with rams, it
computes the new wall level, updates it in the database (recounting the
village population when the wall is destroyed), builds the report fragment,
and recalculates the battle when the wall level changed.

Pure behaviour-preserving extraction: the battle-recalc context is passed in
a single $ctx array; the call site keeps the t7 guard and assigns the
returned battlepart / info_ram, so all downstream usages remain unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:33:38 +03:00
Ferywir c97fa6c71a Refactor(sendunitsComplete): extract calculateTrappedUnits() [#155]
Move the trapper resolution block out of sendunitsComplete() into a
dedicated private method calculateTrappedUnits(). It computes how many
incoming attacker units are caught in the defender's traps (Gaul trapper
or Natar capital), updates the trap counters and the prisoners table, and
subtracts the trapped troops from the attacking army.

Pure behaviour-preserving extraction: the inline `${'traped'.$i}`
variables are rehydrated at the call site from the returned bundle, so all
downstream usages remain unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:53:34 +03:00
Ferywir 7ec51902fd Refactor(sendunitsComplete): extract calculatePopulations() [#155]
Move the attacker/defender total-population computation (and the two
getProfileVillages() lookups that feed it) out of the per-attack loop into a
dedicated private method. Behaviour-preserving: the method takes the initial
$defpop/$attpop (0 for villages, 500 for the oasis branch) and accumulates onto
them exactly as before, and returns the village lists ($varray/$varray1) used
later for the can-destroy check and handleConquest().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 10:32:41 +03:00
Ferywir 4842b7edc1 Fix(i18n/fr): correct remaining old unit names in descriptions and lore
Follow-up to the unit/tribe rename: a few strings still used the old names.

- MANUAL_UDESC_11: "Les Massues" -> "Les Combattants au gourdin" (U11),
  so the in-game manual description matches the unit name.
- TZ_MACEMAN: "Massue" -> "Combattant au gourdin" (used as the U11 label in
  the alliance forum troop picker).
- Conquest help text: the loyalty-reduction units listed "COMMANDANTS, CHEFS"
  (old Teuton name) -> "CHEFS, CHEFS DE TRIBU", matching Senator/Chief/
  Chieftain (Romain/Germain/Gaulois) like the English version.
- Wonder-of-the-World lore: anglicism "Natarian" -> "natarien/natarienne"
  (empire natarien, capitale natarienne, menace natarienne), consistent with
  the Natar unit names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:37:23 +03:00
Ferywir 84d736a342 Fix(i18n/fr): correct French tribe and unit names to match Travian
- Tribe 2 renamed Teutons -> Germains (the French name for this tribe),
  including all manual/tooltip/description texts and adjective forms
  (heros germain, armee germaine, troupes germaines).
- Teuton units: Massueur -> Combattant au gourdin (U11),
  Hallebardier -> Combattant a la lance (U12),
  Chevalier teutonique -> Cavalier teuton (U16), Commandant -> Chef (U19).
- Roman units: Imperial -> Imperian (U3) and official Latin cavalry names
  Equites Legati/Imperatoris/Caesaris (U4-U6).
- Gaul units: Foudre de Teutates -> Teutates accent (U24),
  Haeduen -> Hedui (U26).
- Natar units: anglicism natarian -> natarien (U46/U49/U50) and
  "artefact natarian" -> "artefact natarien" in building descriptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:37:23 +03:00
Ferywir 2e09148bb7 Fix(farmlist): raid bugs and PHP 8.3 warnings (#217) 2026-06-15 07:53:14 +03:00
Ferywir 8398265641 Refactor(sendunitsComplete): extract collectReinforcementReport() [#155]
Extract the pre-casualty defender report block into a new private method
collectReinforcementReport(): reset and rebuild the reinforcement unit totals
($DefenderEnf) and per-tribe hero totals from a fresh re-select, fold
reinforcement heroes into the defender's hero count, build the "units sent"
report rows (own troops + reinforcements and their masked variants), and
(re)initialise the per-tribe dead-hero accumulator. The bundle is returned to
the caller and the defender's folded hero count is reassigned at the call site.

Pure behaviour-preserving extraction. Method name matches the maintainer's
roadmap for #155.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:52:36 +03:00
Ferywir ae0ab66099 Refactor(sendunitsComplete): extract applyReinforcementCasualties() [#155]
Extract the "kill other defence in village" block (reinforcement casualties)
into a new private method applyReinforcementCasualties(): compute each
reinforcement's dead units/hero, persist them (modifyEnforce), notify the
reinforcing players, and delete fully-wiped reinforcements. Dead counts are
accumulated into $alldead and dead heroes into $DefenderHeroesDeadArray (both
passed by reference); the tribe-presence flags (rom/ger/gal/nat/natar) are
returned to the caller.

Pure behaviour-preserving extraction. Also drops two dead recomputations of
$totalsend_att/$totaldead_att that sat inside the loop and were immediately
overwritten right after it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 21:38:43 +03:00
Ferywir e167322d12 Refactor(sendunitsComplete): extract applyOwnDefenceCasualties() [#155]
Move the in-village defender casualty handling (kill ratio applied to the
defender's own troops + hero, persisted via modifyUnit) out of the giant
sendunitsComplete() loop into a dedicated private method returning the
$owndead map. Pure behaviour-preserving extraction; also drops a dead $u
variable that was assigned but never read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 11:39:26 +03:00
Catalin Novgorodschi 6b560ed3b3 Merge pull request #212 from Ferywir/pr/npc-resource-distribution 2026-06-14 06:31:07 +03:00
Ferywir 2cdeea463e fix(preferences): restore saving of custom direct links [#204] (#213) 2026-06-14 06:30:36 +03:00
Ferywir dc971770e1 fix(market): harden NPC resource distribution against NaN [#211]
On some servers the NPC ("merchant") distribution screen could lock on
"Rest: NaN", hiding the trade button and making the NPC unusable. The "Rest"
value never returned to 0, so the player could not complete the trade.

Two layers of hardening:

* Client (Templates/Build/17_3.tpl):
  - add a safe npcDiv() helper (returns 0 on a 0 divisor or non-finite value)
    and route every division in portionOut() through it, so Infinity/NaN can no
    longer leak into the inputs;
  - in calculateRest(), coerce "Rest"/overall to a finite value (fall back to
    the total when org4 cannot be parsed) so the submit button can always
    reappear once the distribution is balanced;
  - coerce each resource, $totalRes, $maxstore and $maxcrop to a valid,
    non-negative integer before they reach the page/JS, so a resource computed
    as NaN/INF cannot poison org4/summe in the first place.

* Server (Market::tradeResource): clamp each requested amount to [0, maxstore]
  (crop to maxcrop) before persisting, so a forged or corrupted POST can no
  longer write out-of-range resources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 21:38:46 +02:00
Ferywir 827354a622 feat(admin): add transparent debug error-log mode
Add an admin-controlled debug mode that captures PHP errors of all
players into var/log/debug-players.log, to hunt remaining PHP 8.3 bugs
from real play sessions. Fully transparent to players: no redirect, no
gameplay change, errors are never displayed.

- DB: new debug_log table (one row), mirroring the maintenance pattern.
- Database: getDebugMode()/setDebugMode()/setDebugSettings(), defensive
  when the table is absent (no blank page).
- Session: register a custom error + shutdown handler when enabled; the
  handler runs even when php.ini error_reporting masks warnings/notices,
  so capture is complete without a Docker rebuild. Auto-disables after a
  configurable window.
- DebugErrorLogger: size-capped file with a single .log.1 rotation,
  honours the @ operator, never throws.
- Admin: new "Debug Error Log" page (levels, size cap, auto-off, on-page
  viewer, clear, download) + debugLog action mod.
- Menu: admin-only quick on/off widget (TZ_DEBUG_ON/OFF, EN/FR/RO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:18:39 +03:00
Ferywir 63c94fa961 feat(preferences): apply time preference (timezone + date format) [#198]
Implement the "Time Preference" section of the player preferences (issue
#198), which until now was stored in DB but never applied in-game.

procMtime() — the central date/time formatter used across reports, messages,
notices and troop movements — now honours the per-user settings:
  - timezone: the stored value is mapped to a DateTimeZone, either a named
    DST-aware region (Europe, UK, Turkey, Kolkata, Bangkok, New York,
    Chicago, New Zealand) or a fixed UTC offset (general zones); timestamps
    are converted before formatting.
  - tformat: drives the date layout and 12h/24h clock (0=EU dd.mm.yy 24h,
    1=US mm/dd/yy 12h, 2=UK dd/mm/yy 12h, 3=ISO yy/mm/dd 24h). tformat 0
    keeps the previous EU output unchanged.

The today/yesterday sentinels are preserved (callers compare them) and are
computed in the player's timezone. The time-only path (mode 9) stays 24h
H:i:s because it feeds the live JS clock counters (unx.js), which parse
"H:i:s" by splitting on ":". A few alliance/report/farm-list rows that
combined procMtime's day part with a raw date('H:i') now take the time from
the same procMtime result, to avoid mixing timezones.

A second "local time" clock is shown next to the server-time one: a small
vanilla-JS snippet emitted once from menu.tpl finds the visible #tp1 clock
and appends a row ticking in the player's timezone (Date.now() + UTC offset),
independent of the browser timezone and of the unx.js counters. It aligns the
value under the server time, is skipped when the player's timezone matches
the server's, and lets the clock box grow so the extra row fits the frame.

Adds the LOCAL_TIME string (EN/FR/RO). When no player session is available
(admin / pre-login) procMtime falls back to the server timezone and EU
layout, i.e. the previous behaviour. Removes the last "not coded yet" tag
from the preferences form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:46:33 +03:00
Ferywir af1b8c7ce7 fix(report): restore field separator in combat report data [#203]
The #155 refactor that extracted buildCombatReport() turned one comma
into a literal dot in the non-scout data2 string:

    ...$info_chief.'.'.(isset($info_spy) ? ...   // was ','

That dropped one CSV field, shifting every following field left by one
(hero troops t11, casualties, prisoners 182-192) and injecting a stray
"." into a numeric cell. Visible attack reports rendered with misplaced
hero/prisoner data, and the shifted text in the prisoner slots is what
made Templates/Notice/1.tpl array_sum() warn under PHP 8.

Restore the comma so the layout matches what the report template
(Notice/1.tpl) and the pre-refactor code expect. Reports stored before
this fix keep their corrupted string; the int-cast guard added in the
previous commit keeps them from re-emitting the warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:35:50 +03:00
Ferywir 6030727a0d fix(php8): silence warnings raised by the attack/report flow [#203]
PHP 8.x turned several long-standing implicit accesses into runtime
warnings that flooded the error log during attacks and report viewing:

- Battle.report (Templates/Notice/1.tpl): the prisoner check ran
  array_sum() over slots that may hold non-numeric text (the
  "Information" line can shift indices), raising
  "Addition is not supported on type string". Sum int-cast values.
- Database::getUnitsNumber(): movement/reinforcement arrays can be null
  or miss tribe-specific unit keys, raising "Undefined array key" and
  "Trying to access array offset on null". Null-coalesce to 0.
- Automation (combat resolver): $scout was only defined for scouting
  attacks (type 1) yet always passed to buildCombatReport(), raising
  "Undefined variable $scout". Initialize it for every attack type.
- Automation: the destroy-village check read $to['natar'] which is not
  always set, raising "Undefined array key". Use empty().

Pure null-safety / behaviour-preserving changes; no gameplay logic
altered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:35:50 +03:00