Compare commits

..
Author SHA1 Message Date
admin b9ebaf516f Use direct vector declarations in action tests 2026-06-23 12:54:28 -07:00
adminandGitHub 549cf37127 Require eagle map on GameState (#7924) 2026-06-23 12:44:12 -07:00
adminandGitHub 237d5ce7c3 Tidy transposition table accessors (#7918) 2026-06-23 12:25:57 -07:00
adminandGitHub 05fc72c611 Tidy AI command evaluator result state (#7917) 2026-06-23 12:25:36 -07:00
adminandGitHub 5d889b71aa Remove GameState bootstrap date helper (#7916) 2026-06-23 12:24:57 -07:00
adminandGitHub 8641d15fc7 Remove single-future reserve hints (#7915) 2026-06-23 12:24:00 -07:00
adminandGitHub e4fc742990 Make GameState currentDate required (#7913) 2026-06-23 12:10:56 -07:00
adminandGitHub a27dfe895f Use span for odds other factors (#7914) 2026-06-23 12:07:11 -07:00
adminandGitHub 098d4b4dcc Tidy random battle runner setup (#7903) 2026-06-23 11:52:39 -07:00
adminandGitHub 765ca02255 Use clamped perf test unit counts (#7902) 2026-06-23 11:52:13 -07:00
adminandGitHub 186dec0ad9 Reserve controller update fields (#7901) 2026-06-23 11:50:01 -07:00
adminandGitHub 14a6d143bc Reserve odds helper factors (#7904) 2026-06-23 11:48:42 -07:00
adminandGitHub ade5a4fa10 Size game state guesser buffers first (#7905) 2026-06-23 11:47:38 -07:00
adminandGitHub 85d3ae8fce Tidy action point distance setup (#7906) 2026-06-23 11:47:22 -07:00
adminandGitHub fb200278ae Tidy available command assembly (#7907) 2026-06-23 11:47:02 -07:00
adminandGitHub 34da29bef0 Reserve reinforcement setup commands (#7908) 2026-06-23 11:46:47 -07:00
adminandGitHub f164fbb150 Add GameState required current date helper (#7910) 2026-06-23 11:46:03 -07:00
adminandGitHub fe1537bb12 Trim map names by extension (#7909) 2026-06-23 11:45:06 -07:00
adminandGitHub 69cc336cef Tidy map info position loop (#7911) 2026-06-23 11:44:48 -07:00
adminandGitHub d05030f99a Improve remaining iPad touch targets (#7900) 2026-06-23 11:03:39 -07:00
adminandGitHub 9699d095c5 Reserve command execution result buffers (#7883) 2026-06-23 11:03:01 -07:00
adminandGitHub 6ca2d7a37c Reserve action execution buffers (#7884) 2026-06-23 11:02:40 -07:00
adminandGitHub b0c4944a6c Remove Postgres text store early returns (#7881) 2026-06-23 11:02:13 -07:00
adminandGitHub d25925d525 Avoid implicit food lookup insertion (#7885) 2026-06-23 11:01:38 -07:00
adminandGitHub 07a6834216 Handle zero-length cube lines (#7880) 2026-06-23 11:00:47 -07:00
adminandGitHub 69f1f51460 Tighten shardok server request conversion (#7879) 2026-06-23 11:00:14 -07:00
adminandGitHub 8b30a2c5f2 Reserve tutorial reinforcement units (#7886) 2026-06-23 10:59:49 -07:00
adminandGitHub 65a534ebae Remove SQLite text store early returns (#7887) 2026-06-23 10:59:31 -07:00
adminandGitHub ac0a154fc4 Reserve game state view fields (#7888) 2026-06-23 10:59:10 -07:00
adminandGitHub 1b510007fd Use Await.result in server setup (#7889) 2026-06-23 10:58:23 -07:00
adminandGitHub 692c363264 Guard action filter actor lookup (#7890) 2026-06-23 10:58:02 -07:00
adminandGitHub cf5d88c69d Avoid copying filtered history (#7892) 2026-06-23 10:57:36 -07:00
adminandGitHub 109f49e522 Clarify custom battle player view lookup (#7891) 2026-06-23 10:57:05 -07:00
adminandGitHub 53e3f7a1b0 Use fixed adjacent tile offsets (#7893) 2026-06-23 10:56:51 -07:00
adminandGitHub c7d3e162b9 Clarify ResolveBattle required winners (#7894) 2026-06-23 10:56:23 -07:00
adminandGitHub 7b2bb34a36 Clarify mid-game AI required values (#7895) 2026-06-23 10:56:07 -07:00
adminandGitHub c7b1082954 Clarify GamesManager required loads (#7896) 2026-06-23 10:54:42 -07:00
adminandGitHub aa18d0dd1a Clarify chronicle required leaders (#7897) 2026-06-23 10:54:22 -07:00
adminandGitHub 711d0382f3 Make province distance lookup explicit (#7898) 2026-06-23 10:53:55 -07:00
adminandGitHub 7b02c39172 Make tutorial loyalty lookups explicit (#7899) 2026-06-23 10:53:04 -07:00
adminandGitHub b178f11bea Improve iPad touch targets in popup UI (#7882) 2026-06-23 10:49:29 -07:00
adminandGitHub 42da8a7d15 Clarify great person faction requirement (#7870) 2026-06-23 10:08:24 -07:00
adminandGitHub bd9b62d7de Reserve AI evaluator lookahead vectors (#7869) 2026-06-23 10:08:07 -07:00
adminandGitHub 98fcd94614 Reserve engine output vectors (#7868) 2026-06-23 10:07:49 -07:00
adminandGitHub 06e7112db1 Clarify battle participant lookups (#7847) 2026-06-23 10:07:32 -07:00
adminandGitHub 9ba023b0fd Reserve hex map proto fields (#7871) 2026-06-23 10:07:08 -07:00
adminandGitHub ff63c0fff2 Reserve game state helper collections (#7872) 2026-06-23 10:06:52 -07:00
adminandGitHub 421dbcae80 Clarify required quest request fields (#7873) 2026-06-23 10:06:34 -07:00
adminandGitHub 4c321967f4 Reserve geometry utility vectors (#7874) 2026-06-23 10:06:18 -07:00
adminandGitHub b3c0138074 Clarify required proto submessages (#7875) 2026-06-23 10:05:53 -07:00
adminandGitHub 4b7c862ac8 Clarify service invariant lookups (#7876) 2026-06-23 10:05:31 -07:00
adminandGitHub cf4b99710c Clarify Shardok interface required fields (#7877) 2026-06-23 10:05:11 -07:00
adminandGitHub d25e0121a6 Clarify required hero library conjunctions (#7878) 2026-06-23 10:04:52 -07:00
adminandGitHub 213a339d34 Delete stale editor log zip (#7845) 2026-06-23 08:38:58 -07:00
adminandGitHub d2205dc361 Reserve Shardok test helper vectors (#7846) 2026-06-23 08:38:46 -07:00
adminandGitHub 21e0586add Reserve AI test helper vectors (#7848) 2026-06-23 08:38:07 -07:00
adminandGitHub cbbe200c98 Avoid raw battle battalion options (#7862) 2026-06-23 08:37:50 -07:00
adminandGitHub fbeddc7b19 Avoid raw heads in string parser (#7849) 2026-06-23 08:29:08 -07:00
adminandGitHub 1ed6c85b5d Improve iPad touch targets in Eagle UI (#7850) 2026-06-23 08:28:25 -07:00
adminandGitHub c7f10dbcbc Clarify required engine history result (#7851) 2026-06-23 08:28:08 -07:00
adminandGitHub 81f747c75b Reserve simulator setup vectors (#7852) 2026-06-23 08:27:41 -07:00
adminandGitHub a76dc0515c Clarify required dates in round advancement (#7853) 2026-06-23 08:26:39 -07:00
adminandGitHub bb77ecbd73 Clarify province resource date requirement (#7854) 2026-06-23 08:26:21 -07:00
adminandGitHub 5d35e7d78b Extract MCTS action sorting helper (#7855) 2026-06-23 08:25:56 -07:00
adminandGitHub e20c4e7027 Avoid vector allocation in ranged bonus scoring (#7856) 2026-06-23 08:25:25 -07:00
adminandGitHub 3977df70f4 Clarify more current date requirements (#7857) 2026-06-23 08:24:52 -07:00
adminandGitHub 7f72a8bb39 Reserve AI strategy working vectors (#7858) 2026-06-23 08:24:26 -07:00
adminandGitHub de27e9fcb8 Right-size APD working vectors (#7859) 2026-06-23 08:24:05 -07:00
adminandGitHub 76bd94ddb5 Clarify config parse failures (#7860) 2026-06-23 08:23:35 -07:00
adminandGitHub 651e209f07 Move completed action results into vectors (#7861) 2026-06-23 08:23:03 -07:00
adminandGitHub 448ec1e0ee Reserve simulator CLI path vectors (#7863) 2026-06-23 08:21:32 -07:00
adminandGitHub 6e2afeee3c Clarify required battle ended result (#7864) 2026-06-23 08:21:08 -07:00
adminandGitHub edce49467c Reserve map info results (#7865) 2026-06-23 08:20:51 -07:00
adminandGitHub 2934027437 Clarify battle unit return lookups (#7866) 2026-06-23 08:20:35 -07:00
adminandGitHub b2250cda79 Reserve tutorial controller result vectors (#7867) 2026-06-23 08:20:17 -07:00
adminandGitHub 96d69b349f Reserve Shardok filter result vectors (#7839) 2026-06-23 07:49:53 -07:00
adminandGitHub 4a6ae0380a Clarify current date requirements in command helpers (#7840) 2026-06-23 07:49:38 -07:00
adminandGitHub e339ddd227 Require factions for leader-aware command choices (#7841) 2026-06-23 07:49:18 -07:00
adminandGitHub afaf6c9e19 Move Shardok vector temporaries (#7842) 2026-06-23 07:49:03 -07:00
adminandGitHub e21eaf8606 Add narrow March command flow (#7747) 2026-06-23 07:48:32 -07:00
adminandGitHub a43a650d17 Document PR review commit history preference (#7844) 2026-06-23 07:48:17 -07:00
adminandGitHub 17cb742964 Clarify province conquest required values (#7843) 2026-06-23 07:40:12 -07:00
adminandGitHub 9d8231b253 Clarify current date requirements in aftermath phases (#7836) 2026-06-23 07:00:16 -07:00
adminandGitHub 5bf2ea95e5 Clarify current date requirements in resolution events (#7837) 2026-06-23 07:00:02 -07:00
adminandGitHub 2f48330d74 Document cleanup PR batching guidance (#7838) 2026-06-23 06:59:31 -07:00
adminandGitHub b0b516a990 Clarify current date requirements in event phases (#7835) 2026-06-23 06:59:05 -07:00
adminandGitHub 1057271163 Reserve move destination vector (#7830) 2026-06-23 06:58:47 -07:00
adminandGitHub 296d5df16f Reserve fall into water helper vectors (#7829) 2026-06-23 06:58:33 -07:00
adminandGitHub b2abe00b14 Reserve possible chargee ids (#7828) 2026-06-23 06:58:20 -07:00
adminandGitHub 793d665d80 Reserve allied player id vector (#7826) 2026-06-23 06:58:07 -07:00
adminandGitHub bbbe596e72 Reserve game state guesser vectors (#7825) 2026-06-23 06:57:54 -07:00
adminandGitHub c3c148f5db Reserve Shardok game setup vectors (#7823) 2026-06-23 06:57:33 -07:00
adminandGitHub 4b8f1d2d20 Reserve hex cube result vectors (#7821) 2026-06-23 06:57:18 -07:00
adminandGitHub 8a3222ed54 Reserve map name vector capacity (#7819) 2026-06-23 06:56:56 -07:00
adminandGitHub 9f82af2e12 Make remaining Shardok test casts explicit (#7818) 2026-06-23 06:56:35 -07:00
adminandGitHub 2536e5146c Fix empty AI score reserve underflow (#7832) 2026-06-23 06:54:09 -07:00
adminandGitHub 98b0502e20 Use reinterpret cast for resolved unit bytes (#7811) 2026-06-23 06:08:33 -07:00
adminandGitHub 25c1198c0a Clarify captured hero command acting hero (#7790) 2026-06-23 06:05:58 -07:00
adminandGitHub a6e31596d6 Qualify flatbuffer wrapper memcpy calls (#7791) 2026-06-23 06:05:42 -07:00
adminandGitHub c9b3cd6fc0 Clarify beast info requirement (#7792) 2026-06-23 06:05:26 -07:00
adminandGitHub 2c18d1d39b Move AI command evaluator sample sequence (#7794) 2026-06-23 06:05:09 -07:00
adminandGitHub 28895e3e04 Clarify captured hero command choice requirement (#7793) 2026-06-23 06:04:54 -07:00
adminandGitHub f8ef044226 Include cstring in unit helpers (#7789) 2026-06-23 06:04:36 -07:00
adminandGitHub b8da5750c7 Clarify please recruit command choice requirements (#7795) 2026-06-23 06:02:59 -07:00
adminandGitHub fbdc0f79a8 Move AI battle simulator setup objects (#7796) 2026-06-23 06:02:35 -07:00
adminandGitHub ea7f666e36 Move game state helper unit structs (#7797) 2026-06-23 06:02:18 -07:00
adminandGitHub 48ff8a7e1b Bind march origin command choice (#7798) 2026-06-23 06:02:00 -07:00
adminandGitHub 033a378e14 Use static cast for game status conversion (#7799) 2026-06-23 06:01:41 -07:00
adminandGitHub a3d19f246e Move random battle runner units (#7800) 2026-06-23 06:01:25 -07:00
adminandGitHub ec67215bc2 Clean up percentile roll casts (#7801) 2026-06-23 06:00:59 -07:00
adminandGitHub f207bef4a8 Clarify armament cost lookup (#7802) 2026-06-23 06:00:34 -07:00
adminandGitHub 41bb944c26 Use static casts for action result sizes (#7803) 2026-06-23 06:00:19 -07:00
adminandGitHub 38eb33367d Bind attack command current date (#7804) 2026-06-23 05:59:45 -07:00
adminandGitHub 5ae59006fb Bind alms command current date (#7805) 2026-06-23 05:59:26 -07:00
adminandGitHub e66e7d4a03 Clarify update game status player id check (#7806) 2026-06-23 05:59:05 -07:00
adminandGitHub 34bbfaf256 Clarify backstory update counter parsing (#7807) 2026-06-23 05:58:30 -07:00
adminandGitHub abd323a232 Use static casts in hex map utilities (#7808) 2026-06-23 05:58:00 -07:00
adminandGitHub 97ba1d0be1 Use static casts in hex cube fixup (#7809) 2026-06-23 05:57:43 -07:00
adminandGitHub deb211a420 Clarify new round current date requirement (#7810) 2026-06-23 05:57:19 -07:00
adminandGitHub 9481eed7b8 Use static cast for new wind direction (#7812) 2026-06-23 05:51:51 -07:00
adminandGitHub 50c457d877 Use static cast for battalion type size check (#7813) 2026-06-23 05:51:34 -07:00
adminandGitHub a544a0d15e Use static casts in hex map direction arithmetic (#7814) 2026-06-22 23:52:15 -07:00
adminandGitHub 8c26ac5fca Clarify expand selector required hero checks (#7777) 2026-06-22 21:19:09 -07:00
adminandGitHub d56e0de9fd Clarify new hero province selection (#7778) 2026-06-22 21:18:40 -07:00
adminandGitHub 5132152334 Catch Shardok server exceptions by const reference (#7779) 2026-06-22 21:18:11 -07:00
adminandGitHub ef6ba7e585 Throw battle simulator CLI helper errors (#7780) 2026-06-22 21:17:51 -07:00
adminandGitHub 695a9d19d4 Return from AI performance argument parsing (#7781) 2026-06-22 21:17:30 -07:00
adminandGitHub 45682dff9b Parse real battle extractor integers with stoi (#7782) 2026-06-22 21:17:08 -07:00
adminandGitHub 47c5653bc0 Parse AI experiment id with from_chars (#7783) 2026-06-22 21:16:43 -07:00
adminandGitHub 033ebc802a Parse Shardok server config without strtok (#7784) 2026-06-22 21:16:12 -07:00
adminandGitHub 14e0fb66fb Drop unused random battle srand calls (#7785) 2026-06-22 21:15:28 -07:00
adminandGitHub 18d9756fc5 Make AI evaluation counter noncopyable (#7786) 2026-06-22 21:15:15 -07:00
adminandGitHub f976548fa1 Make attacker score timer noncopyable (#7787) 2026-06-22 21:14:58 -07:00
adminandGitHub a3fed6836d Include cstring in flatbuffer wrapper (#7788) 2026-06-22 21:14:38 -07:00
adminandGitHub 78d89f83da Scope remaining Shardok cpp tests to namespace (#7776) 2026-06-22 17:04:14 -07:00
adminandGitHub 66b93b0c83 Clarify tribute demand fallback (#7775) 2026-06-22 17:03:21 -07:00
adminandGitHub fe06ead2e2 Scope flee command tests to shardok namespace (#7767) 2026-06-22 17:03:09 -07:00
adminandGitHub 71e5b1b866 Clarify faction change victor lookups (#7774) 2026-06-22 17:02:38 -07:00
adminandGitHub 35ac2d6297 Scope ice action tests to shardok namespace (#7764) 2026-06-22 17:02:00 -07:00
adminandGitHub ff33d13822 Scope snow action tests to shardok namespace (#7762) 2026-06-22 17:01:47 -07:00
adminandGitHub ffb02d1d6c Scope build bridge command tests to shardok namespace (#7771) 2026-06-22 17:01:34 -07:00
adminandGitHub 86f7e85ce9 Clarify prisoner move required hero lookup (#7770) 2026-06-22 17:01:20 -07:00
adminandGitHub cd6318ba84 Clarify faction change required lookups (#7772) 2026-06-22 17:01:07 -07:00
adminandGitHub 7385b8ced2 Scope evacuate prisoners command tests to shardok namespace (#7773) 2026-06-22 17:00:54 -07:00
adminandGitHub a36ccd4157 Clarify captured hero recruitment requirements (#7765) 2026-06-22 17:00:40 -07:00
adminandGitHub 29a3459649 Scope blow bridge command tests to shardok namespace (#7766) 2026-06-22 17:00:27 -07:00
adminandGitHub 8dd7df989d Bind selected captured hero options once (#7763) 2026-06-22 17:00:09 -07:00
adminandGitHub d40b9a9ba6 Clarify captured hero return destination lookup (#7768) 2026-06-22 16:59:49 -07:00
adminandGitHub dd5d34f7aa Scope holy wave command tests to shardok namespace (#7769) 2026-06-22 16:58:50 -07:00
adminandGitHub 5685c8430f Clarify required CommandFactory province fields (#7761) 2026-06-22 16:58:37 -07:00
adminandGitHub 723a195d89 Scope ambush odds tests to shardok namespace (#7760) 2026-06-22 16:58:22 -07:00
adminandGitHub 68a20cba27 Scope percentile roll tests to shardok namespace (#7759) 2026-06-22 16:53:03 -07:00
adminandGitHub 27802e54e6 Move game settings test include inside guard (#7758) 2026-06-22 16:49:18 -07:00
adminandGitHub 3e91ca3bc3 Qualify standard library types in ShardokGameController (#7756) 2026-06-22 16:49:07 -07:00
adminandGitHub 5a4e98bd71 Remove unsafe runtime validator lookups (#7752) 2026-06-22 16:44:57 -07:00
adminandGitHub 225fe66afa Qualify standard library types in ShardokEngine (#7753) 2026-06-22 16:44:27 -07:00
adminandGitHub 619f0ca3e9 Report alliance destination lookup failures explicitly (#7754) 2026-06-22 16:44:09 -07:00
adminandGitHub fa3f4cd06b Report chronicle attack faction name lookup failures explicitly (#7755) 2026-06-22 16:43:54 -07:00
adminandGitHub 7694d2e676 Handle empty conjunction results without unsafe get (#7757) 2026-06-22 16:43:24 -07:00
adminandGitHub ea6792f21b Report quest creation lookup failures explicitly (#7751) 2026-06-22 16:42:10 -07:00
adminandGitHub 5e4dace755 Report battalion suitability type lookup failures explicitly (#7742) 2026-06-22 16:33:14 -07:00
adminandGitHub 276dbf5d42 Qualify vectors in hex map helpers (#7743)
* Qualify vectors in hex map helpers

* Qualify ZOC vector parameters
2026-06-22 16:32:57 -07:00
adminandGitHub 0ca2685bf3 Use province ruling faction helper for command availability (#7736) 2026-06-22 16:32:33 -07:00
adminandGitHub 4bab97ec62 Report arm troops availability cost lookup failures explicitly (#7741) 2026-06-22 16:32:11 -07:00
adminandGitHub 47bd5f6291 Qualify std iterators in Eagle gRPC server (#7740) 2026-06-22 16:31:57 -07:00
adminandGitHub 7ee1441b8e Report march neighbor lookup failures explicitly (#7739) 2026-06-22 16:31:43 -07:00
adminandGitHub 034270a119 Use province ruling faction helper for diplomacy availability (#7738) 2026-06-22 16:31:29 -07:00
adminandGitHub 9e4bff03d9 Qualify string constant in FearCommand (#7737) 2026-06-22 16:31:11 -07:00
adminandGitHub 29b9954954 Qualify std algorithms in AI unit scoring (#7735) 2026-06-22 16:30:48 -07:00
adminandGitHub 3aa62b9eeb Report quest fulfillment lookup failures explicitly (#7734) 2026-06-22 16:30:28 -07:00
adminandGitHub d8bd976204 Report unaffiliated hero province lookup failures explicitly (#7744) 2026-06-22 16:30:07 -07:00
adminandGitHub 3ccb8f5dc8 Qualify shared pointers in Eagle gRPC server (#7745) 2026-06-22 16:29:54 -07:00
adminandGitHub cb05990260 Report hero utils faction lookup failures explicitly (#7746) 2026-06-22 16:29:20 -07:00
adminandGitHub 8a2c21c88c Report combat unit suitability lookup failures explicitly (#7748) 2026-06-22 16:28:47 -07:00
adminandGitHub 204cd81462 Remove stale std aliases from Unit header (#7749) 2026-06-22 16:28:30 -07:00
adminandGitHub 71505b5419 Report manage prisoners return province lookup failures explicitly (#7750) 2026-06-22 16:27:59 -07:00
adminandGitHub 50e958e2e3 Use province ruling faction helper in actions (#7724) 2026-06-22 14:58:25 -07:00
adminandGitHub 3ffab58cbf Report LLM lookup failures explicitly (#7723) 2026-06-22 14:58:12 -07:00
adminandGitHub febb9e2fe0 Report organize troops lookup failures explicitly (#7722) 2026-06-22 14:57:52 -07:00
adminandGitHub b1af1b7254 Report quest ruling faction lookup failures explicitly (#7725) 2026-06-22 14:57:07 -07:00
adminandGitHub 32a4da1826 Report alliance resolution lookup failures explicitly (#7726) 2026-06-22 14:56:31 -07:00
adminandGitHub cc44eef174 Report invitation resolution lookup failures explicitly (#7727) 2026-06-22 14:55:41 -07:00
adminandGitHub c608a5890a Report diplomacy origin province lookup failures explicitly (#7728) 2026-06-22 14:53:43 -07:00
adminandGitHub 52253908eb Report AI diplomacy faction lookup failures explicitly (#7729) 2026-06-22 14:53:27 -07:00
adminandGitHub a50956cdb0 Report AI ransom hero lookup failures explicitly (#7730) 2026-06-22 14:53:14 -07:00
adminandGitHub ee55e4b216 Move deploy transport off Bazel runners (#7732) 2026-06-22 14:52:27 -07:00
adminandGitHub 9d041926d7 Preserve iOS scene manifest for TestFlight builds (#7733) 2026-06-22 14:51:36 -07:00
adminandGitHub 72ea2de25c Version Addressables by Unity editor (#7731) 2026-06-22 08:33:15 -07:00
adminandGitHub 4b559c2278 Report validated command lookup failures explicitly (#7720) 2026-06-22 07:38:14 -07:00
adminandGitHub 327ecf1aed Qualify std algorithms in hex map utils (#7721) 2026-06-22 07:37:26 -07:00
adminandGitHub 8fc893ce08 Qualify iterators in game state guesser (#7719) 2026-06-22 07:36:38 -07:00
adminandGitHub 0ec0e10c0f Remove stale iterator aliases from APD (#7718) 2026-06-22 07:34:02 -07:00
adminandGitHub 38c7a01743 Qualify std types in fixed APD test (#7717) 2026-06-22 07:33:50 -07:00
adminandGitHub a481b5ce60 Report recruit heroes ruling hero explicitly (#7716) 2026-06-22 07:32:35 -07:00
adminandGitHub 7786db1040 Qualify std types in Shardok games manager (#7715) 2026-06-22 07:30:48 -07:00
adminandGitHub 200aa0d319 Report exile vassal lookup failures explicitly (#7714) 2026-06-22 07:30:27 -07:00
adminandGitHub c8bb543907 Remove stale std aliases from AI headers (#7713) 2026-06-22 07:30:08 -07:00
adminandGitHub de7ecf45d1 Avoid repeated eligible gift get (#7712) 2026-06-22 07:27:18 -07:00
adminandGitHub f25298ed9e Report command factory lookup failures explicitly (#7711) 2026-06-22 07:27:01 -07:00
adminandGitHub d4ea2fbd93 Report arm troops lookup failures explicitly (#7710) 2026-06-22 07:24:24 -07:00
adminandGitHub 79c51fb21a Qualify STL containers in game state helpers (#7709) 2026-06-22 07:23:48 -07:00
adminandGitHub c57c9b5f18 Qualify std helpers in Shardok production code (#7708) 2026-06-22 07:20:09 -07:00
adminandGitHub 823cd73d33 Report missing hero descriptions explicitly (#7707) 2026-06-22 07:19:50 -07:00
adminandGitHub 54db4b6e34 Report missing suppress beasts effect text (#7706) 2026-06-22 07:18:56 -07:00
adminandGitHub 112399d67c Qualify smart pointer helpers in Shardok tests (#7705) 2026-06-22 07:18:42 -07:00
adminandGitHub 01b80d8798 Report missing province ruling faction explicitly (#7704) 2026-06-22 07:18:14 -07:00
adminandGitHub f6a3f36a13 Remove unused vector aliases in Shardok tests (#7703) 2026-06-22 07:18:01 -07:00
adminandGitHub d674e23987 Report missing common unit faction explicitly (#7702) 2026-06-22 07:17:46 -07:00
adminandGitHub 51aebf5e40 Qualify std vector in Shardok tests (#7701) 2026-06-22 07:15:59 -07:00
adminandGitHub 2ec6d2f1d7 Report missing seek-more-leaders faction explicitly (#7700) 2026-06-22 07:15:23 -07:00
adminandGitHub 9f94917a2d Report required lookup failures explicitly (#7687) 2026-06-22 07:08:10 -07:00
adminandGitHub 06f2104750 Report new game lookup failures explicitly (#7688) 2026-06-22 07:07:53 -07:00
adminandGitHub b1e1e36236 Report empty in-memory history explicitly (#7690) 2026-06-22 07:06:02 -07:00
adminandGitHub 9eb2056ac6 Report map load failures with context (#7691) 2026-06-22 07:05:40 -07:00
adminandGitHub 0c3cb22e83 Report unknown hex terrain types explicitly (#7692) 2026-06-22 07:05:24 -07:00
adminandGitHub 92f27ba273 Report missing faction utility lookups explicitly (#7693) 2026-06-22 07:05:04 -07:00
adminandGitHub 47d682e7c1 Report unaffiliated phase lookup failures explicitly (#7694) 2026-06-22 07:04:23 -07:00
adminandGitHub 75d84b67ff Report missing action ruling factions explicitly (#7695) 2026-06-22 07:03:52 -07:00
adminandGitHub be8f5ca5da Report missing beast fallback explicitly (#7696) 2026-06-22 07:03:24 -07:00
adminandGitHub c7910a6183 Report missing withdrawing flee province explicitly (#7697) 2026-06-22 07:03:08 -07:00
renovate[bot]andGitHub 98afc28762 Update alpine_linux:3.24 Docker digest to 28bd5fe (#7698) 2026-06-22 06:17:57 -07:00
adminandGitHub 4cd1349a5a Fix AI victory score test std types (#7699) 2026-06-22 05:44:05 -07:00
adminandGitHub cebac64264 Avoid unsafe diplomacy hero fallback head (#7668) 2026-06-21 21:48:23 -07:00
adminandGitHub 172125e03b Avoid unsafe AI game arg head (#7667) 2026-06-21 21:48:09 -07:00
adminandGitHub 15006091b4 Report missing AI commands explicitly (#7670) 2026-06-21 21:46:19 -07:00
adminandGitHub 2175f1d957 Report missing diplomacy offers explicitly (#7671) 2026-06-21 21:45:56 -07:00
adminandGitHub d445cbfe25 Avoid unsafe ruling hero head (#7673) 2026-06-21 21:44:07 -07:00
adminandGitHub 5fc56c342d Avoid unsafe shuffle tail heads (#7674) 2026-06-21 21:43:53 -07:00
adminandGitHub e5350d03a8 Report missing recon targets explicitly (#7676) 2026-06-21 21:43:40 -07:00
adminandGitHub 097daf3dc1 Report missing province ruling heroes explicitly (#7678) 2026-06-21 21:43:25 -07:00
adminandGitHub 3c3dc85187 Avoid standard aliases in command factory headers (#7675)
* Avoid standard aliases in command factory headers

* Fix available commands std type qualification
2026-06-21 21:42:47 -07:00
adminandGitHub a775a667f6 Remove unused vector alias from unit filter (#7677) 2026-06-21 21:41:27 -07:00
adminandGitHub a1a7a69aeb Remove unused vector alias from game settings (#7679) 2026-06-21 21:41:08 -07:00
adminandGitHub ff549ab337 Qualify shared pointers in cache and settings headers (#7686)
* Qualify shared pointers in cache and settings headers

* Fix map info test shared pointer spelling
2026-06-21 21:40:51 -07:00
adminandGitHub 464651503e Avoid vector aliases in utility headers (#7672) 2026-06-21 21:36:38 -07:00
adminandGitHub 2f93250c8e Avoid vector aliases in AI headers (#7669) 2026-06-21 21:36:14 -07:00
adminandGitHub 22ce7ba750 Report missing tutorial ruling heroes explicitly (#7680) 2026-06-21 21:35:54 -07:00
adminandGitHub acbb09f6a1 Qualify vector usage in action point distances (#7681) 2026-06-21 21:35:40 -07:00
adminandGitHub 900dd23ae7 Use explicit backstory text id accessors (#7683) 2026-06-21 21:35:14 -07:00
adminandGitHub 0f391776aa Qualify vector usage in duel utilities (#7682) 2026-06-21 21:34:42 -07:00
adminandGitHub 817fb613ab Clean up library implementation std aliases (#7684) 2026-06-21 21:34:16 -07:00
adminandGitHub 2f57ea0160 Qualify std types in available commands header (#7685) 2026-06-21 21:33:59 -07:00
adminandGitHub f5d5aedf9d Avoid std aliases in map loader header (#7647) 2026-06-21 19:56:47 -07:00
adminandGitHub c5114e9bf1 Avoid unsafe OpenAI choice head (#7648) 2026-06-21 19:56:35 -07:00
adminandGitHub 7514aed437 Remove unused vector alias from unit filter header (#7649) 2026-06-21 19:56:19 -07:00
adminandGitHub f4d381c9d4 Report missing headshot output path (#7650) 2026-06-21 19:56:09 -07:00
adminandGitHub 8698036f7c Avoid unsafe migration version lookup (#7651) 2026-06-21 19:55:51 -07:00
adminandGitHub 21f04cfd46 Avoid string alias in shardok exception header (#7652) 2026-06-21 19:55:30 -07:00
adminandGitHub 074cf62cc9 Avoid unsafe recruit province head (#7653) 2026-06-21 19:52:34 -07:00
adminandGitHub a66f833c90 Avoid unsafe aftermath auto-resolve head (#7654) 2026-06-21 19:51:53 -07:00
adminandGitHub d704d23d39 Avoid unsafe aftermath hostile error head (#7655) 2026-06-21 19:48:34 -07:00
adminandGitHub e2570aec4a Use explicit vectors in implementation files (#7656) 2026-06-21 19:48:06 -07:00
adminandGitHub 6b97fde9ab Report missing tutorial battle explicitly (#7657) 2026-06-21 19:47:50 -07:00
adminandGitHub 30adf1bcbf Avoid vector aliases in APD headers (#7658) 2026-06-21 19:46:25 -07:00
adminandGitHub f205b49a54 Avoid string alias in action result helper header (#7659) 2026-06-21 19:46:06 -07:00
adminandGitHub 5c7e18696e Avoid unsafe quest chooser hero heads (#7660) 2026-06-21 19:45:50 -07:00
adminandGitHub c78b00b0fc Avoid vector alias in coords set header (#7661) 2026-06-21 19:45:19 -07:00
adminandGitHub b05d6e1e1b Avoid unsafe recon candidate head (#7662) 2026-06-21 19:45:05 -07:00
adminandGitHub da0bce655c Avoid standard aliases in shardok action headers (#7663) 2026-06-21 19:44:41 -07:00
adminandGitHub d4dcbcc9d5 Report missing uncontested conqueror explicitly (#7664) 2026-06-21 19:44:26 -07:00
adminandGitHub f132e5b87b Remove unused string alias from hero header (#7665) 2026-06-21 19:42:05 -07:00
adminandGitHub 872475e82b Avoid map alias in unit header (#7666) 2026-06-21 19:41:48 -07:00
adminandGitHub 66d062bfda Avoid unsafe pregenerated hero head (#7631) 2026-06-21 15:55:51 -07:00
adminandGitHub a514ceb62f Cache repeated loop bounds (#7632) 2026-06-21 15:55:38 -07:00
adminandGitHub b0d8a5bc41 Avoid unsafe province distance frontier lookup (#7633) 2026-06-21 15:55:19 -07:00
adminandGitHub 8f4bb5130e Avoid namespace import in map info implementation (#7634) 2026-06-21 15:55:03 -07:00
adminandGitHub 9386589558 Avoid unsafe attack decision unit head (#7638) 2026-06-21 15:54:47 -07:00
adminandGitHub 5945289a81 Use explicit std types in map loader (#7639) 2026-06-21 15:54:31 -07:00
adminandGitHub 53346cc85b Use explicit std types in game over response populator (#7640) 2026-06-21 15:26:45 -07:00
adminandGitHub a6fad3ac4e Use explicit string type in shardok controller (#7641) 2026-06-21 15:26:32 -07:00
adminandGitHub cb7e2b5d5c Use explicit std types in shardok server main (#7642) 2026-06-21 15:26:15 -07:00
adminandGitHub 0f764416a3 Avoid namespace import in AI performance runner (#7643) 2026-06-21 15:25:56 -07:00
adminandGitHub c9e0012bf7 Avoid namespace import in random battle runner (#7644) 2026-06-21 15:25:37 -07:00
adminandGitHub 457f2a677a Avoid vector alias in game update receiver header (#7645) 2026-06-21 15:25:10 -07:00
adminandGitHub ced56ee41f Avoid string alias in server configuration header (#7646) 2026-06-21 15:24:56 -07:00
adminandGitHub 4e910f1f22 Report missing migration save files explicitly (#7637) 2026-06-21 15:16:53 -07:00
adminandGitHub 34bceebace Remove unused registrar header string import (#7636) 2026-06-21 15:16:34 -07:00
adminandGitHub b4f802ba77 Avoid unsafe LLM update group bounds (#7635) 2026-06-21 15:16:15 -07:00
adminandGitHub 3d9d464870 Avoid unsafe battle aftermath claimant head (#7630) 2026-06-21 15:04:00 -07:00
adminandGitHub a7e21427f9 Avoid namespace imports in map info header (#7627) 2026-06-21 15:03:43 -07:00
adminandGitHub bdbf10294b Avoid namespace import in map info runner (#7629) 2026-06-21 15:02:57 -07:00
adminandGitHub 8588430cf0 Avoid unsafe TSV header lookup (#7628) 2026-06-21 15:01:15 -07:00
adminandGitHub d920bb5c17 Report utility TSV load failures explicitly (#7626) 2026-06-21 14:59:01 -07:00
adminandGitHub 64fe9bf59d Cache map starting position count in tests (#7625) 2026-06-21 14:58:23 -07:00
adminandGitHub 101b0bf41f Cache hex map terrain count (#7624) 2026-06-21 14:54:31 -07:00
adminandGitHub 019865b5ee Reserve converted victory conditions (#7623) 2026-06-21 14:54:16 -07:00
adminandGitHub 799d34a1f1 Avoid unsafe Gemini response heads (#7622) 2026-06-21 14:54:03 -07:00
adminandGitHub 951d533ed3 Avoid unsafe rate limit header heads (#7619) 2026-06-21 14:53:49 -07:00
adminandGitHub 459eb0ef7b Cache Eagle interface player counts (#7621) 2026-06-21 14:53:15 -07:00
adminandGitHub f710105bd9 Cache action result repeated field counts (#7620) 2026-06-21 14:53:01 -07:00
adminandGitHub 4998e3ce40 Cache changed unit counts in gtest helpers (#7618) 2026-06-21 14:48:32 -07:00
adminandGitHub d739b374a4 Cache AI battle protobuf unit counts (#7617) 2026-06-21 14:48:15 -07:00
adminandGitHub bd61ebcbb6 Use flatbuffer-sized production knowledge indices (#7616) 2026-06-21 14:47:53 -07:00
adminandGitHub 3963275092 Report hero backstory load failures explicitly (#7569) 2026-06-21 14:40:52 -07:00
adminandGitHub a796e0fe67 Avoid unsafe battle request error message lookup (#7583) 2026-06-21 14:40:35 -07:00
adminandGitHub 52ac61fa1d Qualify flatbuffer wrapper abort calls (#7606) 2026-06-21 14:40:13 -07:00
adminandGitHub 29ca7665b1 Use size_t for AI integration unit loops (#7607) 2026-06-21 14:39:25 -07:00
adminandGitHub d9bdb814f4 Use size_t for MCTS reserve unit loops (#7608) 2026-06-21 14:39:09 -07:00
adminandGitHub 3dfb094a08 Use size_t for optimized score test unit loops (#7609) 2026-06-21 14:38:51 -07:00
adminandGitHub 7ce1818a7f Use flatbuffer-sized unit knowledge indices (#7610) 2026-06-21 14:37:45 -07:00
adminandGitHub 6e6eff7991 Use flatbuffer-sized retreat factory knowledge index (#7615) 2026-06-21 14:37:04 -07:00
adminandGitHub aa9c98f081 Use flatbuffer-sized game start knowledge indices (#7611) 2026-06-21 14:36:00 -07:00
adminandGitHub cdf0ad45f0 Use flatbuffer-sized move test knowledge index (#7612) 2026-06-21 14:35:14 -07:00
adminandGitHub 1b97b6673f Use flatbuffer-sized melee test knowledge index (#7613) 2026-06-21 14:34:10 -07:00
adminandGitHub 721d432bc6 Use flatbuffer-sized flee factory knowledge index (#7614) 2026-06-21 14:33:01 -07:00
adminandGitHub e513a784eb Report missing start game heroes explicitly (#7571) 2026-06-21 14:30:42 -07:00
adminandGitHub ff8da78992 Report missing tutorial battle players explicitly (#7577) 2026-06-21 14:30:11 -07:00
adminandGitHub 84a69ce982 Match prisoner leader faction options (#7572) 2026-06-21 14:28:27 -07:00
adminandGitHub 7d743f8678 Report missing proto converter fields explicitly (#7575) 2026-06-21 14:28:13 -07:00
adminandGitHub 3f24e15392 Bind custom battle human updates explicitly (#7578) 2026-06-21 14:27:49 -07:00
adminandGitHub 0f86bb6874 Match engine command lookup explicitly (#7580) 2026-06-21 14:27:29 -07:00
adminandGitHub 05ee7a2ab2 Report unruled defend flee provinces explicitly (#7582) 2026-06-21 14:25:46 -07:00
adminandGitHub b9321433d0 Report fixed hero load failures explicitly (#7567) 2026-06-21 14:24:29 -07:00
adminandGitHub 62791401e9 Use streams for raise dead setup output (#7581) 2026-06-21 14:18:59 -07:00
adminandGitHub 4d28bba9cc Use streams for MCTS decision investigation output (#7579) 2026-06-21 14:18:42 -07:00
adminandGitHub 1f3660e1f3 Use streams for reserve combat phase output (#7576) 2026-06-21 14:18:24 -07:00
adminandGitHub 95ff44e205 Use streams for reserve setup diagnostics (#7574) 2026-06-21 14:18:08 -07:00
adminandGitHub 987a164092 Report missing hostile army neighbor explicitly (#7584) 2026-06-21 14:17:51 -07:00
adminandGitHub 1b5b11fd5b Match pending aftermath info explicitly (#7586) 2026-06-21 14:17:35 -07:00
adminandGitHub ddcde52e27 Bind validated command options explicitly (#7587) 2026-06-21 14:12:54 -07:00
adminandGitHub ea2da84535 Report missing declined quest hero explicitly (#7589) 2026-06-21 14:12:04 -07:00
adminandGitHub abed8ee574 Bind control weather option explicitly (#7590) 2026-06-21 14:09:50 -07:00
adminandGitHub 556c59a9c7 Report S3 credential load failures explicitly (#7566) 2026-06-21 14:09:39 -07:00
adminandGitHub ad2d085b24 Stabilize sqlite history upload snapshot test (#7605) 2026-06-21 14:08:50 -07:00
adminandGitHub d7209102b8 Report missing battalion types explicitly (#7592) 2026-06-21 13:25:39 -07:00
adminandGitHub 14e1ba6795 Avoid unwrapping province distances (#7594) 2026-06-21 13:25:25 -07:00
adminandGitHub 65b56081ce Report beast info load failures explicitly (#7595) 2026-06-21 13:25:00 -07:00
adminandGitHub eafb51cbfa Report name generator load failures explicitly (#7564) 2026-06-21 13:24:37 -07:00
adminandGitHub 3444ae296e Report missing ransom offer prisoner explicitly (#7597) 2026-06-21 13:24:23 -07:00
adminandGitHub 4f2fff5da6 Use streams for MCTS start fire test output (#7565) 2026-06-21 13:23:50 -07:00
adminandGitHub 144483a45d Use streams for start fire cost explanation (#7573) 2026-06-21 13:21:53 -07:00
adminandGitHub 53345b2bb0 Use streams for MCTS rain fire output (#7568) 2026-06-21 13:17:20 -07:00
adminandGitHub 5d6bc429b0 Use streams for start fire cost summary (#7570) 2026-06-21 13:17:00 -07:00
adminandGitHub a4faf61613 Report missing divine acting province explicitly (#7599) 2026-06-21 13:16:41 -07:00
adminandGitHub fdd516598d Report missing apprehended outlaw explicitly (#7601) 2026-06-21 13:16:29 -07:00
adminandGitHub 70da6990c4 Match optional date protos explicitly (#7602) 2026-06-21 13:13:12 -07:00
adminandGitHub e8b18d0805 Use streams for raise dead filter output (#7585) 2026-06-21 13:12:03 -07:00
adminandGitHub 1ca9e8678c Use streams for MCTS investigation intro output (#7588) 2026-06-21 13:11:29 -07:00
adminandGitHub 94670149fe Use streams for MCTS end-turn score output (#7591) 2026-06-21 13:11:14 -07:00
adminandGitHub 8e253c2972 Use streams for MCTS start-fire failure output (#7596) 2026-06-21 13:11:00 -07:00
adminandGitHub 6e15c6d9cb Use streams for MCTS start-fire success output (#7593) 2026-06-21 13:10:21 -07:00
adminandGitHub ca00e90ad6 Use streams for AI command mismatch diagnostics (#7600) 2026-06-21 13:10:07 -07:00
adminandGitHub b1d76ea661 Use streams for MCTS path comparison output (#7598) 2026-06-21 13:09:52 -07:00
adminandGitHub 059d428cfa Make Shardok player id conversions explicit (#7603) 2026-06-21 13:08:19 -07:00
adminandGitHub a4b50eb357 Tighten map info index handling (#7604) 2026-06-21 13:07:43 -07:00
adminandGitHub 0dfd5aa11d Use streams for critical fire score output (#7563) 2026-06-21 10:32:50 -07:00
adminandGitHub 2b63ccd145 Report name format parse failures explicitly (#7562) 2026-06-21 10:32:38 -07:00
adminandGitHub 97cec5ec38 Add Shardok Linux ARM64 PR build (#7561) 2026-06-21 10:29:17 -07:00
adminandGitHub 31fb5a3616 Avoid partial battalion access in riot command errors (#7560) 2026-06-21 10:27:28 -07:00
adminandGitHub 7b7130d67d Use resource handling for Shardok map info (#7552) 2026-06-21 10:25:49 -07:00
adminandGitHub 6707ad55ff Report missing beasts event explicitly (#7559) 2026-06-21 10:25:37 -07:00
adminandGitHub 89aa73b63a Use resource handling for image paths (#7554) 2026-06-21 10:25:21 -07:00
adminandGitHub bb7453a002 Avoid partial Option access in HeroNameCache (#7555) 2026-06-21 10:25:09 -07:00
adminandGitHub 8cea29dcb0 Avoid partial command access in early AI (#7556) 2026-06-21 10:24:55 -07:00
adminandGitHub d5d74ec8d6 Avoid partial command access in mid-game AI (#7557) 2026-06-21 10:24:32 -07:00
adminandGitHub 9b000b94ac Report missing Shardok map info explicitly (#7558) 2026-06-21 10:24:13 -07:00
adminandGitHub 82ed9fcc83 Include array in CombatDamage (#7553) 2026-06-21 10:23:51 -07:00
adminandGitHub cd53283935 Use resource handling for LLM usage loads (#7551) 2026-06-21 10:17:08 -07:00
adminandGitHub 3faddf76f8 Use resource handling in JsonUtils (#7550) 2026-06-21 10:15:43 -07:00
adminandGitHub 6e24ae2f4f Use streams for MCTS archery debug output (#7538) 2026-06-21 10:10:27 -07:00
adminandGitHub 66912580c8 Use streams in C++ debug headers (#7540) 2026-06-21 10:10:13 -07:00
adminandGitHub 219dd830e1 Use streams for MCTS score sequence output (#7541) 2026-06-21 10:09:51 -07:00
adminandGitHub 3d5cdcc78e Use streams for combat damage strings (#7542) 2026-06-21 10:09:32 -07:00
adminandGitHub ab1d43fae3 Use streams for MCTS minimax debug output (#7543) 2026-06-21 10:09:19 -07:00
adminandGitHub 2d51f3c23d Use resource handling for name format reads (#7548) 2026-06-21 10:09:03 -07:00
adminandGitHub f9447cf7aa Use resource handling for map info migration (#7549) 2026-06-21 10:08:42 -07:00
adminandGitHub c6ed0bfc7d Use streams for AI choice diagnostics (#7534) 2026-06-21 10:06:57 -07:00
adminandGitHub 2d4621b1a6 Use pattern matching for parser literals (#7547) 2026-06-21 10:05:48 -07:00
adminandGitHub 3bf929b4a5 Use streams for MCTS fire score analysis (#7544) 2026-06-21 10:02:45 -07:00
adminandGitHub ebed01ba86 Use streams for abstract MCTS diagnostics (#7545) 2026-06-21 10:02:29 -07:00
adminandGitHub 880d005347 Use streams for Alah fire score analysis (#7546) 2026-06-21 10:02:12 -07:00
adminandGitHub e8ef216de8 Use streams for common C++ diagnostics (#7539) 2026-06-21 10:01:47 -07:00
adminandGitHub baa453594a Delete old dropped games automatically (#7535) 2026-06-21 09:46:01 -07:00
adminandGitHub 9c767d5c49 Use streams for MCTS score placement diagnostics (#7537) 2026-06-21 09:45:15 -07:00
adminandGitHub e0892bd2fa Use streams for MCTS setup diagnostics (#7536) 2026-06-21 09:44:55 -07:00
adminandGitHub 315be7c465 Use streams for ID AI performance logs (#7533) 2026-06-21 09:42:33 -07:00
adminandGitHub 9d34dc0954 Use streams for AI debug dump messages (#7532) 2026-06-21 09:42:14 -07:00
adminandGitHub bdedd43e3c Use streams for score timing diagnostic (#7531) 2026-06-21 09:42:02 -07:00
adminandGitHub 97e779675e Use streams in gtest contains diagnostics (#7528) 2026-06-21 09:41:43 -07:00
adminandGitHub 2ef4216886 Use cerr for flatbuffer allocator failures (#7530) 2026-06-21 09:41:28 -07:00
adminandGitHub 8c9764a57e Use cerr for APD thread diagnostic (#7529) 2026-06-21 09:41:09 -07:00
adminandGitHub eb94eb1a6d Use streams in available commands test diagnostics (#7527) 2026-06-21 09:40:53 -07:00
adminandGitHub 12e3fb99eb Keep dropped games in existing storage (#7525) 2026-06-21 09:32:34 -07:00
adminandGitHub 2bd90a26a8 Remove client text getTexts returns (#7526) 2026-06-21 09:32:04 -07:00
adminandGitHub 24db3a4cb1 Use streams for AI command count logging (#7524) 2026-06-21 09:31:02 -07:00
adminandGitHub 0d05ea1730 Use streams for iterative deepening diagnostics (#7523) 2026-06-21 09:30:45 -07:00
adminandGitHub 9c13b3c1bf Use streams for AI flee debug output (#7522) 2026-06-21 09:27:34 -07:00
adminandGitHub 492d9d16eb Remove StringConstructionParser returns (#7507) 2026-06-21 09:20:30 -07:00
adminandGitHub c4cd0eb683 Remove broken action point distance timer (#7509) 2026-06-21 09:15:53 -07:00
adminandGitHub 287f7b9818 Fix random battle runner build (#7521) 2026-06-21 09:15:35 -07:00
adminandGitHub 15b5a3e0c1 Remove SQLite client text guard returns (#7505) 2026-06-21 09:15:15 -07:00
adminandGitHub cf93c43ef2 Remove AI time budget debug print (#7506) 2026-06-21 09:14:55 -07:00
adminandGitHub 380c24537b Remove stale undead morale diagnostic (#7508) 2026-06-21 09:12:31 -07:00
adminandGitHub 6200bc7e02 Remove SqliteHistory flush return (#7510) 2026-06-21 09:12:05 -07:00
adminandGitHub e92465c894 Use explicit chrono imports in distance test (#7511) 2026-06-21 09:11:52 -07:00
adminandGitHub 6f432d221a Remove GameController debug return (#7512) 2026-06-21 09:11:28 -07:00
adminandGitHub b8ddca4e76 Route action point cache stats through streams (#7513) 2026-06-21 09:11:12 -07:00
adminandGitHub f343ee9f7f Send battalion type parse errors to stderr (#7514) 2026-06-21 09:10:53 -07:00
adminandGitHub 9a8c4c6fdd Remove GameController post battle return (#7515) 2026-06-21 09:10:38 -07:00
adminandGitHub 96f2dc0454 Send hex map migrator errors to stderr (#7516) 2026-06-21 09:10:21 -07:00
adminandGitHub e0988ef186 Remove GamesManager cache invalidation returns (#7517) 2026-06-21 09:07:20 -07:00
adminandGitHub a154174fa1 Remove broken Shardok inspector target (#7496) 2026-06-21 09:05:50 -07:00
adminandGitHub 042861f065 Remove GamesManager S3 archive return (#7520) 2026-06-21 09:04:54 -07:00
adminandGitHub 0f776af050 Remove GamesManager flush marker returns (#7518) 2026-06-21 09:04:18 -07:00
adminandGitHub 166e50924f Remove GamesManager LLM response return (#7519) 2026-06-21 09:04:00 -07:00
adminandGitHub efb34ede56 Remove Postgres client text guard returns (#7504) 2026-06-21 09:02:56 -07:00
adminandGitHub 22b55eb223 Allow routing transposition table stats output (#7503) 2026-06-21 08:41:33 -07:00
adminandGitHub f5d9027206 Allow routing MCTS cache stats output (#7502) 2026-06-21 08:39:52 -07:00
adminandGitHub 9dcbeff21a Avoid endl flushes in AI integration tests (#7501) 2026-06-21 08:39:29 -07:00
adminandGitHub 0cc7c4d0a2 Remove SQLite download recovery return (#7500) 2026-06-21 08:36:29 -07:00
adminandGitHub b0ca65b720 Use unique_ptr in attacker strategy selector test (#7499) 2026-06-21 08:36:09 -07:00
adminandGitHub e3eb3eb7bf Own nonstandard hex map adjacency cache entries (#7498) 2026-06-21 08:34:22 -07:00
adminandGitHub b8b2c92227 Remove SQLite legacy guard returns (#7497) 2026-06-21 08:33:33 -07:00
adminandGitHub 424dde5da6 Remove SQLite migration pending return (#7495) 2026-06-21 08:33:00 -07:00
adminandGitHub ce7e636987 Remove SQLite replay empty range return (#7494) 2026-06-21 08:31:12 -07:00
adminandGitHub c51f61ba56 Remove expand command selector return (#7492) 2026-06-21 08:28:42 -07:00
adminandGitHub 91623bcf7a Remove broken corrupt game checker (#7491) 2026-06-21 08:28:14 -07:00
adminandGitHub 34e14a3521 Check hex map migrator parse results (#7490) 2026-06-21 08:27:52 -07:00
adminandGitHub 6c1a79b342 Remove GameController AI guard returns (#7489) 2026-06-21 08:27:25 -07:00
adminandGitHub 6af5014866 Remove OAuth handler early returns (#7488) 2026-06-21 08:26:59 -07:00
adminandGitHub 16f53dc1b1 Use make_shared for battalion types (#7487) 2026-06-21 08:26:40 -07:00
adminandGitHub df09fe0684 Remove authorization interceptor early returns (#7486) 2026-06-21 08:26:09 -07:00
adminandGitHub e765764cea Remove SQLite shardok row early returns (#7485) 2026-06-21 08:25:13 -07:00
adminandGitHub 2e63bcb5c3 Write flatbuffer allocator diagnostics to stderr (#7484) 2026-06-21 08:23:55 -07:00
adminandGitHub e231f0c34e Use streams for Shardok controller diagnostics (#7483) 2026-06-21 08:23:35 -07:00
adminandGitHub 8155fe7b3f Remove Postgres shardok row early returns (#7482) 2026-06-21 08:23:17 -07:00
adminandGitHub 6fe1bcdff2 Use streams for map info diagnostics (#7481) 2026-06-21 08:23:00 -07:00
adminandGitHub b6b488bd46 Remove changed faction writer early returns (#7480) 2026-06-21 08:15:09 -07:00
adminandGitHub d97cc0cadc Use streams for Shardok server diagnostics (#7479) 2026-06-21 08:13:19 -07:00
adminandGitHub 2a3c742be9 Remove SQLite to Postgres migration admin flow (#7478) 2026-06-21 08:11:06 -07:00
adminandGitHub cdee86dbcb Remove S3 startup save sync (#7475) 2026-06-21 08:10:47 -07:00
adminandGitHub 60264e2bfc Make Shardok numeric casts explicit (#7477) 2026-06-21 08:09:58 -07:00
adminandGitHub 19fb315500 Make remaining unit byte casts explicit (#7476) 2026-06-21 08:09:34 -07:00
adminandGitHub 66710659c9 Tighten Shardok ownership cleanup (#7472) 2026-06-21 07:57:14 -07:00
adminandGitHub de5a34c485 Make game state dumper casts explicit (#7474) 2026-06-21 07:54:29 -07:00
adminandGitHub e0c7ba3874 Make Shardok casts explicit (#7473) 2026-06-21 07:54:04 -07:00
adminandGitHub 9e58fbee56 Remove S3 sync early returns (#7471) 2026-06-21 05:59:23 -07:00
adminandGitHub 5315941f88 Remove sqlite postgres migration return (#7470) 2026-06-21 05:59:01 -07:00
adminandGitHub 3ffeb0250b Remove battle progress early return (#7469) 2026-06-21 05:58:39 -07:00
adminandGitHub 451e6bb406 Avoid unnecessary endl flushes (#7465) 2026-06-21 05:58:06 -07:00
adminandGitHub 58d680bd59 Remove tutorial hero factions early return (#7464) 2026-06-20 21:47:23 -07:00
adminandGitHub 79eb708adb Normalize Shardok header guards (#7463) 2026-06-20 21:44:58 -07:00
adminandGitHub 1282e62781 Remove AsyncS3Persister early returns (#7466) 2026-06-20 21:44:29 -07:00
adminandGitHub 4af501feee Make gtest helper casts explicit (#7467) 2026-06-20 21:44:07 -07:00
adminandGitHub ef7c24f6de Make game over response casts explicit (#7468) 2026-06-20 21:43:29 -07:00
adminandGitHub 62edd02a22 Remove attack decision early returns (#7462) 2026-06-20 21:43:10 -07:00
adminandGitHub 6f22edc8f3 Remove GameMigrator early returns (#7461) 2026-06-20 18:43:08 -07:00
adminandGitHub 27bba1c2da Replace GNU alignment attributes with alignas (#7460) 2026-06-20 17:59:59 -07:00
adminandGitHub c95bc22f0e Remove simple Unit early returns (#7459) 2026-06-20 17:45:04 -07:00
adminandGitHub 844bab253d Rename test settings helper global (#7458) 2026-06-20 17:42:44 -07:00
adminandGitHub b8372487cf Remove backstory recovery early return (#7457) 2026-06-20 17:40:46 -07:00
adminandGitHub 8cc38466e6 Replace AI client flee debug macro (#7456) 2026-06-20 17:40:25 -07:00
adminandGitHub 14935b1a52 Remove march availability early returns (#7455) 2026-06-20 17:37:29 -07:00
adminandGitHub f7b14cd9ba Replace coords set index check macro (#7454) 2026-06-20 17:36:03 -07:00
adminandGitHub 9d1e61ef8d Remove defend availability early return (#7453) 2026-06-20 17:33:01 -07:00
adminandGitHub 8c912c1b3e Replace SuppressBeastsCommand type test (#7450) 2026-06-20 17:32:41 -07:00
adminandGitHub 1ff6ae0195 Replace test type predicates (#7452) 2026-06-20 17:32:12 -07:00
adminandGitHub 641bf456fa Replace iterative deepening debug macro (#7451) 2026-06-20 17:28:10 -07:00
adminandGitHub f21eaabb15 Modernize disabled C++ logging toggles (#7449) 2026-06-20 17:25:13 -07:00
adminandGitHub 7d38336e67 Remove EngineImpl applyActionResults return (#7448) 2026-06-20 17:23:56 -07:00
adminandGitHub f37ec78d10 Remove auth early returns (#7447) 2026-06-20 17:23:02 -07:00
adminandGitHub eb0f92dcd0 Remove simple utility early returns (#7446) 2026-06-20 17:21:20 -07:00
adminandGitHub ee06235cf4 Remove quest chooser early returns (#7445) 2026-06-20 17:20:53 -07:00
adminandGitHub 48dfe9054d Replace simple isInstanceOf predicates (#7443) 2026-06-20 17:20:33 -07:00
adminandGitHub c7e39e67aa Remove simple availability early returns (#7444) 2026-06-20 17:20:07 -07:00
adminandGitHub 86e51a11b7 Replace AI evaluator control macros with constexpr (#7442) 2026-06-20 16:35:44 -07:00
adminandGitHub 3c0e98280d Rename reserved test settings include guard (#7441) 2026-06-20 16:35:15 -07:00
adminandGitHub c54c9f7955 Remove Gemini parser early returns (#7440) 2026-06-20 16:34:50 -07:00
adminandGitHub 614c404bf4 Replace test roll macros with constexpr constants (#7439) 2026-06-20 16:15:09 -07:00
adminandGitHub c1ebcd49fc Replace combat macros with constexpr constants (#7437) 2026-06-20 16:11:00 -07:00
adminandGitHub 49551614fe Default simple C++ special members (#7436) 2026-06-20 16:10:21 -07:00
adminandGitHub b2b8603f23 Generalize FunctionalRandom sequence helpers (#7438) 2026-06-20 16:09:41 -07:00
adminandGitHub 4a6ecda8f8 Remove SettingsLoader asInstanceOf generation (#7432) 2026-06-20 15:58:17 -07:00
adminandGitHub b65d3c25e1 Remove observer asInstanceOf cast (#7427) 2026-06-20 15:56:58 -07:00
adminandGitHub 65f5ec1c38 Remove domain asInstanceOf casts (#7426) 2026-06-20 15:56:43 -07:00
adminandGitHub b8b225eb84 Rename reserved AI evaluator identifiers (#7433) 2026-06-20 14:47:37 -07:00
adminandGitHub c20239b9e8 Normalize static const declarations (#7434) 2026-06-20 14:47:12 -07:00
adminandGitHub a8df7228b3 Mark single-argument constructors explicit (#7431) 2026-06-20 14:46:47 -07:00
adminandGitHub b8ebc0efd8 Remove remaining inline body semicolons (#7428) 2026-06-20 14:46:21 -07:00
adminandGitHub 5ee86faf80 Remove redundant command constructor semicolons (#7425) 2026-06-20 14:44:41 -07:00
adminandGitHub 3d6524b2fa Remove key asInstanceOf casts (#7423) 2026-06-20 14:21:03 -07:00
adminandGitHub 82b671b234 Remove redundant factory constructor semicolons (#7422) 2026-06-20 14:20:16 -07:00
adminandGitHub 3ad6de784f Remove JSON asInstanceOf casts (#7421) 2026-06-20 14:19:46 -07:00
adminandGitHub 6a9e1d7ad7 Remove redundant virtual from retreat command (#7420) 2026-06-20 14:17:46 -07:00
adminandGitHub 84f182d74f Fail iOS build when Addressables runtime data is missing (#7424) 2026-06-20 14:16:26 -07:00
adminandGitHub d08402c502 Remove stale unused casts from Shardok server main (#7417) 2026-06-20 14:11:14 -07:00
adminandGitHub b6a7c38a70 Tidy unused MCTS parameters (#7419) 2026-06-20 14:10:54 -07:00
adminandGitHub 9513c1e404 Remove final test asInstanceOf uses (#7418) 2026-06-20 14:09:33 -07:00
adminandGitHub d67d8b9784 Use inside in attack decision hostile army fixtures (#7415) 2026-06-20 14:04:49 -07:00
adminandGitHub fdc643bac0 Use inside in attack decision allied army fixtures (#7413) 2026-06-20 14:04:36 -07:00
adminandGitHub 9e7890a1a6 Use inside in province events drought fixtures (#7411) 2026-06-20 14:04:23 -07:00
adminandGitHub e9d876e5c9 Use nullptr in server configuration parser (#7416) 2026-06-20 14:03:10 -07:00
adminandGitHub 524c15b824 Use using for remaining C++ aliases (#7414) 2026-06-20 14:02:48 -07:00
adminandGitHub 0891ccb0af Tidy remaining C++ include guards (#7412) 2026-06-20 14:02:22 -07:00
adminandGitHub 179288fa25 Use inside in province events conflict fixtures (#7410) 2026-06-20 13:11:31 -07:00
adminandGitHub 4a8b6df484 Use inside in province events riot condition fixtures (#7409) 2026-06-20 13:07:09 -07:00
adminandGitHub 73b5e1a8cc Use inside for resolve battle flee province change (#7407) 2026-06-20 13:06:08 -07:00
adminandGitHub 8fa012cb73 Tidy unit guard (#7408) 2026-06-20 13:05:21 -07:00
adminandGitHub 8561c606eb Tidy fire utils guard (#7406) 2026-06-20 13:05:06 -07:00
adminandGitHub abed097cad Tidy hex map utils guard (#7405) 2026-06-20 13:04:45 -07:00
adminandGitHub ccd7e6db18 Use inside in resolve battle quest province checks (#7404) 2026-06-20 13:00:40 -07:00
adminandGitHub e6c648c03e Use inside in resolve battle captured hero checks (#7403) 2026-06-20 13:00:21 -07:00
adminandGitHub a4278cb6f2 Use inside in attack decision battalion fixtures (#7402) 2026-06-20 13:00:02 -07:00
adminandGitHub ce9ddc049e Use inside in attack decision status fixtures (#7401) 2026-06-20 12:59:33 -07:00
adminandGitHub 3646fd868a Use inside in province events riot fixtures (#7398) 2026-06-20 12:59:11 -07:00
adminandGitHub 288428c874 Use using for tile modifier alias (#7399) 2026-06-20 12:58:03 -07:00
adminandGitHub 1f30d13454 Tidy zone control guard (#7400) 2026-06-20 12:57:48 -07:00
adminandGitHub 6712a495ff Tidy terrain guard (#7397) 2026-06-20 12:57:25 -07:00
adminandGitHub 8ac26578e3 Use inside in attack decision helper fixtures (#7396) 2026-06-20 12:44:50 -07:00
adminandGitHub 049c7abb81 Use inside in fulfill quests fixtures (#7395) 2026-06-20 12:44:17 -07:00
adminandGitHub 123e0066d7 Use inside in attack command chooser fixtures (#7394) 2026-06-20 12:43:56 -07:00
adminandGitHub 3cff277132 Use inside in seek more leaders fixtures (#7393) 2026-06-20 12:43:33 -07:00
adminandGitHub ebb6a55ef8 Use inside in province move resolution fixture (#7392) 2026-06-20 12:43:05 -07:00
adminandGitHub a2d371fcfc Use inside in manage prisoners availability fixtures (#7390) 2026-06-20 12:42:45 -07:00
adminandGitHub b1d30c4f03 Use inside in captured hero availability fixtures (#7389) 2026-06-20 12:42:21 -07:00
adminandGitHub 67911c12f6 Use inside in break alliance availability test (#7386) 2026-06-20 12:41:58 -07:00
adminandGitHub 25db6649f3 Use inside for new year fixture helpers (#7385) 2026-06-20 12:41:40 -07:00
adminandGitHub 6a5d9df1a4 Use inside for battle flee army assertions (#7383) 2026-06-20 12:41:18 -07:00
adminandGitHub 8254600b5c Tidy hex map direction guard (#7391) 2026-06-20 08:45:01 -07:00
adminandGitHub 8b0d43a5ee Tidy coordinates guard (#7388) 2026-06-20 08:44:45 -07:00
adminandGitHub 99b1827048 Tidy shardok c types guard (#7387) 2026-06-20 08:44:20 -07:00
adminandGitHub 8368f4f762 Tidy shardok action aliases (#7382) 2026-06-20 08:43:49 -07:00
adminandGitHub eda94bd881 Tidy percentile roll odds aliases (#7384) 2026-06-20 08:27:17 -07:00
adminandGitHub 2c8b3e6089 Pattern match attack decision phase filters (#7381) 2026-06-20 08:22:27 -07:00
adminandGitHub d9376ee870 Tidy battalion type helpers (#7380) 2026-06-20 08:21:55 -07:00
adminandGitHub 94473eaac6 Use inside for province event epidemic assertions (#7379) 2026-06-20 08:21:15 -07:00
adminandGitHub ff2caaac1c Use inside for forced turn back province assertions (#7378) 2026-06-20 08:20:51 -07:00
adminandGitHub 2fc7d91b6b Use inside in prisoner exchange test (#7375) 2026-06-20 08:20:27 -07:00
adminandGitHub f74ef641d8 Use inside for defender victory battle assertions (#7377) 2026-06-20 08:19:58 -07:00
adminandGitHub 1aa23d2992 Use inside for food consumption battalion assertions (#7374) 2026-06-20 08:19:01 -07:00
adminandGitHub 2fd86301ce Use inside for new year battalion assertions (#7372) 2026-06-20 08:16:17 -07:00
adminandGitHub 2c2a6a083b Tidy battalion damage helpers (#7376) 2026-06-20 08:15:13 -07:00
adminandGitHub 3462d7e6b3 Tidy sequence random generator header (#7370) 2026-06-20 08:14:45 -07:00
adminandGitHub d9a56d5cc5 Tidy tile modifier integrity bounds (#7373) 2026-06-20 08:14:11 -07:00
adminandGitHub db8a50edab Use inside in defense decision phase test (#7371) 2026-06-20 08:01:50 -07:00
adminandGitHub e815438212 Scope AttackOrientation enum (#7369) 2026-06-20 07:58:42 -07:00
adminandGitHub a31ce39009 Tidy ActionCost value type (#7366) 2026-06-20 07:58:20 -07:00
adminandGitHub 8d8abf0f90 Use inside in attack decision availability test (#7365) 2026-06-20 07:57:56 -07:00
adminandGitHub c9faf440ce Use inside for captured defender battle assertions (#7368) 2026-06-20 07:57:38 -07:00
adminandGitHub 14aec4f6d0 Use inside for outlawed defender battle assertions (#7367) 2026-06-20 07:57:07 -07:00
adminandGitHub 5b11135abb Use inside in captured hero availability test (#7364) 2026-06-20 07:56:25 -07:00
adminandGitHub 3ce4ad3770 Use inside in manage prisoner availability test (#7363) 2026-06-20 07:54:10 -07:00
adminandGitHub 65bc952a5a Use inside in forced turn back test (#7362) 2026-06-20 07:53:54 -07:00
adminandGitHub 01be354271 Use inside for new year province assertions (#7361) 2026-06-20 07:53:31 -07:00
adminandGitHub 2634c7c3c8 Tidy random generator header (#7358) 2026-06-20 07:49:34 -07:00
adminandGitHub 30789f86f6 Tidy Shardok unit helpers (#7355) 2026-06-20 07:49:09 -07:00
adminandGitHub 404028bb59 Always package Windows Addressables runtime data (#7360) 2026-06-20 07:47:26 -07:00
adminandGitHub ea25b9ea79 Use inside for new year hero assertions (#7359) 2026-06-20 07:43:03 -07:00
adminandGitHub 423940ad3b Use inside in send supplies test (#7357) 2026-06-20 07:42:47 -07:00
adminandGitHub 44d8ba6c40 Use inside for province event assertions (#7356) 2026-06-20 07:42:32 -07:00
adminandGitHub 87ea1f1727 Use inside in recon resolution tests (#7354) 2026-06-20 07:41:47 -07:00
adminandGitHub 76c70166ee Scope DamageType enum (#7353) 2026-06-20 07:30:13 -07:00
adminandGitHub 40706f96a3 Use inside for action result test casts (#7351) 2026-06-20 07:28:40 -07:00
adminandGitHub f04bcd61f7 Clean up CombatDamage storage (#7349) 2026-06-20 06:07:28 -07:00
adminandGitHub d415e97672 Use inside for selected Scala test casts (#7350) 2026-06-20 06:05:15 -07:00
adminandGitHub e22a6d987c Add clang-tidy runner script (#7347) 2026-06-19 22:23:03 -07:00
adminandGitHub ba60421024 Tighten Shardok C++ library tests (#7346) 2026-06-19 22:18:25 -07:00
adminandGitHub 0dd7d75878 Improve Shardok C++ safety checks (#7345) 2026-06-19 22:11:02 -07:00
adminandGitHub e77afba638 Increase handle riot give toggle tap targets (#7344) 2026-06-19 21:59:32 -07:00
adminandGitHub 2d69336245 Increase resource control tap targets (#7343) 2026-06-19 21:59:19 -07:00
adminandGitHub 934f3c594e Increase remaining command tap targets (#7342) 2026-06-19 21:59:04 -07:00
adminandGitHub 4cb56f80c6 Increase diplomacy tap targets (#7341) 2026-06-19 18:28:12 -07:00
adminandGitHub 212f19ba40 Increase attack decision tap targets (#7340) 2026-06-19 18:27:17 -07:00
adminandGitHub 9e4f052653 Increase command panel info tap target (#7339) 2026-06-19 18:20:05 -07:00
adminandGitHub afa85afebf Increase prisoner action tap targets (#7338) 2026-06-19 18:19:52 -07:00
adminandGitHub c6514be984 Increase ransom offer button tap targets (#7337) 2026-06-19 16:58:11 -07:00
adminandGitHub 719323d3c7 Restore stale text request invariant (#7336) 2026-06-19 14:20:23 -07:00
adminandGitHub 47fd3733b2 Prune generated text after rewind (#7335)
* Prune text store entries after rewind

* Fix rewind controller test text store expectation
2026-06-19 14:12:12 -07:00
adminandGitHub d7a6241a90 Include Shardok rows in Postgres save exports (#7334) 2026-06-19 13:51:12 -07:00
adminandGitHub ce37e37fb6 Resubmit orphaned outstanding Shardok battles (#7333) 2026-06-19 13:38:54 -07:00
adminandGitHub 536af1f547 Advance saved games after load or rewind (#7332) 2026-06-19 13:26:28 -07:00
adminandGitHub 7c69a9da0e Guard raw rewinds into missing battle state (#7331) 2026-06-19 13:23:38 -07:00
adminandGitHub 548f72e6fc Avoid reflection in raw Postgres history JSON (#7330) 2026-06-19 12:57:26 -07:00
adminandGitHub 1d5aa0c7aa Support raw Postgres history recovery (#7329) 2026-06-19 11:57:34 -07:00
adminandGitHub c00631d6dc Support raw rewind for Postgres saves (#7328) 2026-06-19 11:07:09 -07:00
adminandGitHub f3e6285828 Generate pleas for evacuated secured prisoners (#7327) 2026-06-19 07:47:06 -07:00
adminandGitHub 866c39283d Fix admin saved game transfer for Postgres (#7326) 2026-06-19 07:46:42 -07:00
adminandGitHub 6a98cd8d6d Upgrade Unity client to 6000.5.0f1 (#7325) 2026-06-19 07:46:13 -07:00
adminandGitHub 28000e389a Remove no profession icon (#7324) 2026-06-18 22:07:20 -07:00
adminandGitHub a336ee086c Replace fixed hero headshots (#7323)
* Replace fixed hero headshots

* Rework selected fixed hero portraits
2026-06-18 07:25:37 -07:00
adminandGitHub 08338c1910 Add Warden prisoner join bonus (#7322) 2026-06-17 16:29:47 -07:00
adminandGitHub 3fd9147567 Add Warden Great People (#7321) 2026-06-17 15:55:14 -07:00
adminandGitHub 1cd7f58817 Document Warden follow-up priorities (#7320) 2026-06-17 15:28:19 -07:00
adminandGitHub ebeb7cf2b1 Use shackles icon for Warden evacuation (#7319) 2026-06-17 13:26:00 -07:00
adminandGitHub 5ff379df96 Expose Warden evacuation command in Shardok UI (#7318) 2026-06-17 12:37:07 -07:00
adminandGitHub 4150f1de11 Carry Warden securement metadata through battle resolution (#7316) 2026-06-17 11:18:56 -07:00
adminandGitHub a4565196c2 Resolve secured prisoners from Warden evacuations (#7315) 2026-06-17 09:52:27 -07:00
adminandGitHub fb222665bf Limit Addressables rebuild detection to content inputs (#7314) 2026-06-17 09:50:08 -07:00
adminandGitHub 627f716eb4 Add Warden evacuation Shardok command (#7310) 2026-06-17 09:26:09 -07:00
adminandGitHub 1606a056b5 Guard Addressables groups against pack-together bundles (#7313) 2026-06-17 09:24:49 -07:00
adminandGitHub 3ac1225808 Guard against oversized Addressables uploads (#7312) 2026-06-17 08:44:37 -07:00
adminandGitHub 6805f9ef2d Preload only required tactical sound effects (#7309) 2026-06-17 07:22:48 -07:00
adminandGitHub 27e0f350cc Add Warden evacuation proto plumbing (#7308) 2026-06-17 06:42:12 -07:00
adminandGitHub 809beb77ea Lazy-load strategic sound effects (#7307) 2026-06-17 06:38:43 -07:00
adminandGitHub 2ca12b8f19 Remove gh dependency from TestFlight artifact cleanup (#7306) 2026-06-17 06:34:29 -07:00
adminandGitHub 7ca5106e5d Lazy-load beast effect addressables (#7304) 2026-06-17 06:30:41 -07:00
adminandGitHub 3a4476374f Add generated Warden heroes (#7299) 2026-06-16 22:01:30 -07:00
adminandGitHub ba6a776688 Lazy-load Addressables music (#7303)
* Lazy-load addressable music

* Clean Addressables output before rebuild
2026-06-16 18:20:44 -07:00
adminandGitHub 3a9d2f7eec Log Addressables label fanout (#7302) 2026-06-16 15:44:43 -07:00
adminandGitHub e1b77eabdd Add Addressables upload guardrails (#7301) 2026-06-16 15:22:06 -07:00
adminandGitHub 6f95e4a36c Reduce Addressables upload blast radius (#7300) 2026-06-16 14:40:25 -07:00
adminandGitHub 5de7c5bc0b Add generated wardens (#7297) 2026-06-16 13:54:10 -07:00
adminandGitHub 2523d87a75 Fix skeleton archer bow sprite weights (#7298) 2026-06-16 13:09:49 -07:00
adminandGitHub 5eae8ba403 Ignore generated Unity proto plugin metadata (#7296) 2026-06-16 13:05:17 -07:00
adminandGitHub a84555f1ce Wire Warden Unity profession assets (#7295) 2026-06-16 09:19:40 -07:00
adminandGitHub 0762a78b49 Document Warden prisoner conversion bonus (#7294) 2026-06-16 08:32:40 -07:00
adminandGitHub 735b3bed7f Test tutorial speaker image paths (#7290) 2026-06-16 08:13:56 -07:00
adminandGitHub 7bd62c2d3b Add Warden profession plumbing (#7292) 2026-06-16 08:13:22 -07:00
adminandGitHub a25e88894d Test tutorial dialogue display hints (#7289) 2026-06-16 08:00:15 -07:00
adminandGitHub 0f2cabd469 Build Unity protos as a Bazel DLL (#7291) 2026-06-16 07:54:23 -07:00
adminandGitHub b2f22bbeb1 Fix duplicate tutorial reinforcement test name (#7293) 2026-06-16 07:51:11 -07:00
adminandGitHub c3129ae47b Test tutorial dialogue trigger uniqueness (#7288) 2026-06-16 07:25:33 -07:00
adminandGitHub aa3b240653 Test tutorial reinforcement action payloads (#7287) 2026-06-16 07:25:18 -07:00
adminandGitHub f7c4638452 Test tutorial action result dialogue policy (#7286) 2026-06-16 07:24:54 -07:00
adminandGitHub 122277d3e5 Test tutorial dialogue step content (#7284) 2026-06-16 07:24:38 -07:00
adminandGitHub bf7aaaccd3 Test tutorial reinforcement hero selection (#7281) 2026-06-16 07:24:08 -07:00
adminandGitHub 4a752fcae1 Speed up Windows Unity publish uploads (#7280) 2026-06-16 07:14:04 -07:00
adminandGitHub d0b1e5b09e Remove obsolete Hetzner provisioning code (#7285) 2026-06-16 07:13:30 -07:00
adminandGitHub aab7df62e7 Test tutorial reinforcement event hero mapping (#7282) 2026-06-16 07:02:54 -07:00
adminandGitHub 7991ec0990 Inject DO Spaces endpoint into deploy (#7283) 2026-06-16 07:02:22 -07:00
adminandGitHub a970a1bd6c Test durable tutorial faction dialogue triggers (#7278) 2026-06-16 07:00:47 -07:00
adminandGitHub 8196e1dce4 Use separate storage cleanup credentials (#7279) 2026-06-16 06:48:22 -07:00
adminandGitHub 77ebe010f2 Test controller applies tutorial strategic events (#7277) 2026-06-15 21:39:41 -07:00
adminandGitHub 93c84ea22e Test tutorial strategic event result orchestration (#7275) 2026-06-15 21:39:21 -07:00
adminandGitHub f898d094a2 Gate Windows Unity publish on EditMode tests (#7276) 2026-06-15 21:38:58 -07:00
adminandGitHub 096da09f87 Test tutorial start fire trigger predicate (#7274) 2026-06-15 19:12:45 -07:00
adminandGitHub 9eca2d872c Test tutorial engineer proximity trigger (#7273) 2026-06-15 18:51:28 -07:00
adminandGitHub a3f88f1944 Test tutorial hide trigger predicate (#7272) 2026-06-15 18:35:19 -07:00
adminandGitHub a6dada807d Test tutorial battle running trigger predicate (#7271) 2026-06-15 16:42:50 -07:00
adminandGitHub fee2bced12 Test tutorial hero departure trigger mapping (#7270) 2026-06-15 16:35:36 -07:00
adminandGitHub 0d35c3b39b Test tutorial post-battle predicates (#7269) 2026-06-15 16:18:22 -07:00
adminandGitHub 40731fe88e Test tutorial tax trigger predicate (#7268) 2026-06-15 15:02:48 -07:00
adminandGitHub cbdff442a8 Assert tutorial Eagle event result type (#7267) 2026-06-15 14:56:39 -07:00
adminandGitHub 4db9f7b591 Test tutorial trigger mappings (#7266) 2026-06-15 13:11:25 -07:00
adminandGitHub 823225e97f Add tutorial dialogue contract tests (#7265) 2026-06-15 12:57:34 -07:00
adminandGitHub ede235ec7a Document current tutorial flow (#7264) 2026-06-15 11:04:55 -07:00
adminandGitHub 827d07e4af Speed up Unity editmode asset audits (#7263) 2026-06-15 10:53:08 -07:00
adminandGitHub 52bee7a092 Remove stale server workflow paths (#7262) 2026-06-15 10:41:43 -07:00
adminandGitHub a91ca6826e Share Addressables change detector (#7261) 2026-06-15 10:40:59 -07:00
adminandGitHub 73fc114a59 Consolidate Bazel cache maintenance workflows (#7260) 2026-06-15 10:36:31 -07:00
adminandGitHub c7f054e4b8 Combine artifact cleanup and storage check (#7258) 2026-06-15 10:32:48 -07:00
adminandGitHub fad88168e2 Harden Addressables change detection (#7259) 2026-06-15 10:32:32 -07:00
adminandGitHub 55ad961f19 Matrix server build jobs (#7257) 2026-06-15 10:14:18 -07:00
adminandGitHub 88a224c093 Matrix Renovate artifact updates (#7256) 2026-06-15 10:06:57 -07:00
adminandGitHub 31d41fbff9 Skip unchanged Addressables in Unity builds (#7255) 2026-06-15 10:06:35 -07:00
adminandGitHub 9a54732fae Matrix storage cleanup jobs (#7254) 2026-06-15 09:30:02 -07:00
adminandGitHub 42f8fd5982 Consolidate repository cleanup workflows (#7253) 2026-06-15 09:21:01 -07:00
adminandGitHub 4026e76a37 Consolidate Renovate artifact workflows (#7252) 2026-06-15 09:15:26 -07:00
adminandGitHub fd29767921 Matrix Linux sysroot builds (#7251) 2026-06-15 09:11:20 -07:00
adminandGitHub dc969c8305 Guard analyzer DLL player imports (#7250) 2026-06-15 09:05:34 -07:00
adminandGitHub 6d0bd6366c Skip iOS CDN uploads for build-only runs (#7248) 2026-06-15 08:49:53 -07:00
adminandGitHub dcc27c66d5 Consolidate storage cleanup workflows (#7249) 2026-06-15 08:49:23 -07:00
adminandGitHub dbf2cd17e2 Use shared Bazel setup action in workflows (#7247) 2026-06-15 08:38:05 -07:00
adminandGitHub 9a706dd219 Trigger Mac builds for Sparkle native changes (#7246) 2026-06-15 08:37:17 -07:00
adminandGitHub 95e6917d20 Run Mac build for Mac-specific Unity code (#7244) 2026-06-15 08:30:15 -07:00
adminandGitHub 0a7f356464 Consolidate Go service validation workflows (#7245) 2026-06-15 08:29:38 -07:00
adminandGitHub 6e56176d4c Extract shared Bazel runner setup action (#7242)
* Extract shared Bazel runner setup action

* Keep Git LFS setup before checkout
2026-06-15 08:22:05 -07:00
adminandGitHub b097e0c9ff Guard iOS CLR runtime settings (#7243) 2026-06-15 08:21:43 -07:00
adminandGitHub a71e411132 Guard package runtime DLL imports (#7241) 2026-06-15 08:16:54 -07:00
adminandGitHub 668371480e Harden UnityEditor runtime reference guard (#7240) 2026-06-15 08:10:05 -07:00
adminandGitHub ef660c9c61 Use NamedBuildTarget in Unity build script (#7238) 2026-06-15 08:09:45 -07:00
adminandGitHub 40c63cfa5a Consolidate server build workflows (#7239) 2026-06-15 08:09:16 -07:00
adminandGitHub 42c639d868 Run Unity tests alongside Windows build (#7237)
* Run Unity tests alongside Windows build

* Gate Unity deployment on tests and build

* Keep Unity workflow pushes limited to main
2026-06-15 07:57:17 -07:00
adminandGitHub e30b8fdb7c Split Beast Effects Addressables bundles (#7234)
* Split Beast Effects Addressables bundles

* Harden Windows Unity Addressables build

* Fix Burst editor option lookup

* Keep Windows Burst enabled in CI
2026-06-15 07:16:31 -07:00
adminandGitHub 4f57a2f41b Update checkout action examples (#7235) 2026-06-15 06:38:48 -07:00
adminandGitHub d029ed298e Reduce Beast Effects texture import sizes (#7233)
* Reduce beast effect texture import sizes

* Fall back to GitHub LFS for missing mirror objects

* Disable local LFS filter process for non-LFS checkouts
2026-06-15 06:33:28 -07:00
adminandGitHub 35bcd51cb8 Archive Addressables build layout reports (#7231) 2026-06-15 05:56:13 -07:00
renovate[bot]andGitHub ba98850de3 Update dependency toolchains_llvm to v1.8.0 (#7232) 2026-06-15 05:55:19 -07:00
adminandGitHub 61369a886f Report Addressables output sizes in Unity builds (#7230)
* Report Addressables output sizes in Unity builds

* Run Mac PR builds for Unity build script changes
2026-06-14 21:28:06 -07:00
adminandGitHub 441be82e45 Use Unity .NET profile for standalone builds (#7229)
* Use Unity .NET profile for standalone builds

* Use repo-local Git LFS config in CI

* Upload Windows Addressables directly from Unity CI
2026-06-14 21:20:06 -07:00
adminandGitHub 59d26ffa32 Use IL2CPP for Standalone Unity builds (#7228)
* Use IL2CPP for Standalone Unity builds

* Install .NET SDK for Unity builds

* Expose setup-dotnet SDK to Unity builds

* Set dotnet host path for Unity Addressables

* Build Standalone Addressables with Mono backend

* Disable Addressables player rebuilds in CI

* Require Unity IL2CPP modules in CI

* Use Mono for macOS Windows Unity cross-builds
2026-06-14 19:53:25 -07:00
adminandGitHub 6ca7ef8632 Guard runtime UnityEditor imports (#7227) 2026-06-14 14:13:17 -07:00
adminandGitHub d69dc14dd5 Add CLR migration runtime guardrails (#7226) 2026-06-14 14:05:06 -07:00
adminandGitHub 07c3ed6129 Mark URP migration complete (#7225) 2026-06-14 13:55:14 -07:00
adminandGitHub 65a97388c9 Guard province action animation sprites (#7224) 2026-06-14 13:06:50 -07:00
adminandGitHub 5da05a58b2 Guard Eagle map material wiring (#7223) 2026-06-14 12:57:12 -07:00
adminandGitHub bd483d0ef2 Guard epidemic effect materials (#7222) 2026-06-14 11:54:01 -07:00
adminandGitHub a0fb712cba Guard tutorial sprite scene wiring (#7221) 2026-06-14 10:37:35 -07:00
adminandGitHub b7db0f0cf9 Guard tutorial TMP sprite assets (#7220) 2026-06-14 08:36:49 -07:00
adminandGitHub 912fe03d59 Guard URP shader lookup targets (#7219) 2026-06-14 08:03:07 -07:00
adminandGitHub 3c9a11b6b3 Guard beast material compatibility coverage (#7217) 2026-06-14 07:38:15 -07:00
adminandGitHub 9ee717a89e Fix scheduled cleanup workflows (#7218) 2026-06-14 07:35:54 -07:00
adminandGitHub 5d0d49bd55 Guard production material shader assignments (#7216) 2026-06-14 07:28:36 -07:00
adminandGitHub 62633cf659 Guard Shardok Addressables coverage (#7215) 2026-06-13 21:19:35 -07:00
adminandGitHub 0a3ec59963 Guard beast effect Addressables coverage (#7214) 2026-06-13 20:10:47 -07:00
adminandGitHub deb93e45bd Guard production shader references (#7213) 2026-06-13 19:51:20 -07:00
adminandGitHub 55de6afc1a Guard post-processing stack removal (#7212) 2026-06-13 13:47:36 -07:00
adminandGitHub 5631ab25e5 Guard Eagle beast effect routing (#7211) 2026-06-13 13:39:57 -07:00
adminandGitHub c82aab3b87 Document Warden profession proposal (#7209)
* Document Warden profession proposal

* Add broader profession ideation

* Unify profession candidate ranking
2026-06-13 13:39:20 -07:00
adminandGitHub 9a94f2d510 Guard runtime shader lookups (#7208) 2026-06-13 13:05:20 -07:00
adminandGitHub 82d77e79c1 Clarify PR body temp file instructions (#7210) 2026-06-13 12:58:33 -07:00
adminandGitHub 5a5361762a Refresh URP runtime shader status (#7207) 2026-06-13 12:45:42 -07:00
adminandGitHub 5bf55f458e Require Fireworks particle shader reference (#7206) 2026-06-13 12:38:37 -07:00
adminandGitHub 305f98689f Require Eagle weather particle materials (#7205) 2026-06-13 12:32:36 -07:00
adminandGitHub 6f5ecbeeae Use serialized Shardok foreground shaders (#7203) 2026-06-13 10:03:01 -07:00
adminandGitHub b248b41a33 Skip unchanged Mac Addressables deploy uploads (#7202) 2026-06-12 21:49:28 -07:00
adminandGitHub 237147351e Refresh URP final status docs (#7201) 2026-06-12 21:49:15 -07:00
adminandGitHub 7a29782044 Prefer URP shader for fireworks fallback (#7200) 2026-06-12 21:05:24 -07:00
adminandGitHub b56a6c0c58 Include TMP overlay shader for runtime text (#7199) 2026-06-12 20:59:22 -07:00
adminandGitHub 64740b63b0 Test project shader URP patterns (#7198) 2026-06-12 20:46:16 -07:00
adminandGitHub f8bfb2dc0c Test URP pipeline settings (#7197) 2026-06-12 20:41:18 -07:00
adminandGitHub 304041dfff Test beast material URP compatibility (#7196) 2026-06-12 20:37:06 -07:00
adminandGitHub 94675b3492 Refresh URP third-party audit (#7195) 2026-06-12 20:31:55 -07:00
adminandGitHub 917f8df46e Make additive scene loads idempotent (#7194) 2026-06-12 20:28:02 -07:00
adminandGitHub af13c70848 Remove unused Hex Mesh shader (#7193) 2026-06-12 20:16:57 -07:00
adminandGitHub 4ca777bb2b Add runtime shader validation tests (#7192) 2026-06-12 20:11:46 -07:00
adminandGitHub a59e540051 Refresh URP shader follow-up docs (#7191) 2026-06-12 20:05:17 -07:00
adminandGitHub 88930d7e4d Remove Shardok foreground overlay Built-in fallback (#7190) 2026-06-12 19:59:44 -07:00
adminandGitHub 368c78d36b Remove Shardok fire overlay Built-in fallback (#7189) 2026-06-12 19:54:34 -07:00
adminandGitHub 48c1cebb69 Remove stale Eagle mask shader assets (#7188) 2026-06-12 19:53:51 -07:00
adminandGitHub 1b7479af28 Refresh URP shader debt inventory (#7186) 2026-06-12 19:53:33 -07:00
adminandGitHub 782c028c0a Convert Eagle particle shaders to URP (#7187) 2026-06-12 19:39:43 -07:00
adminandGitHub 6fcc8643b4 Document canonical PR body temp directory (#7185) 2026-06-12 19:19:28 -07:00
adminandGitHub 6f0259ede8 Harden Eagle weather shaders for URP (#7184) 2026-06-12 19:15:28 -07:00
adminandGitHub 1690e5b1f1 Include URP runtime beast shaders in builds (#7182) 2026-06-12 19:09:00 -07:00
adminandGitHub 991dd1b49c Document Unity process safety rule (#7183) 2026-06-12 19:08:47 -07:00
adminandGitHub e32461361c Skip unchanged Windows Addressables deploy uploads (#7181) 2026-06-12 18:58:03 -07:00
adminandGitHub 537a18e6e8 Clean up Windows deploy artifacts (#7180) 2026-06-12 18:53:40 -07:00
adminandGitHub 73ad823a41 Avoid premature ready hero notifications (#7178) 2026-06-12 18:36:03 -07:00
adminandGitHub 3eaf1ebf0a Include Shardok foreground overlay shader in builds (#7179) 2026-06-12 18:28:27 -07:00
adminandGitHub 3ef689b7b4 Harden Eagle province map shaders for URP (#7177) 2026-06-12 18:20:33 -07:00
adminandGitHub 60b34e93a1 Allow Unity and Mac builds to run in parallel (#7176) 2026-06-12 13:58:24 -07:00
adminandGitHub b09b114aa4 Avoid reuploading unchanged Addressables bundles (#7175) 2026-06-12 13:57:58 -07:00
adminandGitHub 2dd89afd30 Check generated Bazel lockfiles on dependency PRs (#7174) 2026-06-12 12:51:46 -07:00
adminandGitHub 7f916d72ad Keep active game subscriber on stale unsubscribe (#7012) 2026-06-12 12:47:21 -07:00
adminandGitHub cdfb2b1902 Handle stale suggested Eagle province (#7172) 2026-06-12 12:47:03 -07:00
adminandGitHub 6676b808f1 Skip hosted Renovate Bazel artifact updates (#7173) 2026-06-12 12:46:27 -07:00
adminandGitHub 4685aff0b1 Remove scene-split debug error panel (#7171) 2026-06-12 12:37:24 -07:00
adminandGitHub 02ede68108 Handle replacement Eagle command tokens (#7169) 2026-06-12 12:08:08 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
5e1a5bb6de Lock file maintenance (#7170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 11:23:47 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
06dec7b1fd Update ubuntu_24_04 Docker tag to v26 (#7167)
* Update ubuntu_24_04 Docker tag to v26

* Update generated dependency lockfiles

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 10:09:41 -07:00
adminandGitHub 0a8991d828 Document remaining URP shader debt (#7168) 2026-06-12 10:01:04 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
0e080d0a20 Update eclipse_temurin_25_jre Docker tag to v26 (#7166)
* Update eclipse_temurin_25_jre Docker tag to v26

* Update generated dependency lockfiles

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 09:56:53 -07:00
adminandGitHub bbde4eb00c Restore moving army hover popup (#7163) 2026-06-12 09:55:03 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
212ad4a72d Update actions/github-script action to v9 (#7164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 09:39:41 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
4f1827e75b Update eclipse_temurin_25_jdk Docker tag to v26 (#7165)
* Update eclipse_temurin_25_jdk Docker tag to v26

* Update generated dependency lockfiles

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 09:38:00 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
54f0b5442a Update go toolchain directive to v1.26.4 (#7157)
* Update go toolchain directive to v1.26.4

* Update generated Go artifacts

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 09:24:57 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>admin
6bf9867be3 Update Rust crate rand to 0.10 (#7159)
* Update Rust crate rand to 0.10

* Fix map generator for rand 0.10

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dan Crosby <dan@danielcrosby.net>
2026-06-12 09:20:33 -07:00
adminandGitHub b8ac4d7515 Ensure Git LFS for Renovate Go artifacts (#7162) 2026-06-12 09:20:10 -07:00
adminandGitHub 12cc9682fd Clean up URP migration naming and docs (#7161) 2026-06-12 09:12:37 -07:00
adminandGitHub da49e18b36 Handle Renovate Go artifact updates in CI (#7160) 2026-06-12 09:08:52 -07:00
adminandGitHub 2522a42021 Enable URP with beast material compatibility (#7140)
* Configure disposable URP probe

* Add URP beast material probe compatibility

* Broaden URP beast material conversion

* Preserve humanoid beast textures in URP probe

* Bake Polytope beast palette textures for URP probe
2026-06-12 09:02:28 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
dc73fa12b0 Update Rust crate ndarray to 0.17 (#7158)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 09:00:38 -07:00
adminandGitHub af1057e7b0 Remove Bazel native C++ autoload shim (#7152) 2026-06-12 08:59:56 -07:00
adminandGitHub e80abb24d1 Summarize Bazel BEP timing in CI (#7155) 2026-06-12 08:59:21 -07:00
adminandGitHub 4e4704174e Fix pointer-enter tooltip handling (#7156) 2026-06-12 08:54:30 -07:00
adminandGitHub bd51acb6a2 Add Bazel platform toolchain error messages (#7154) 2026-06-12 08:51:17 -07:00
adminandGitHub a319460f01 Prefer prebuilt protoc toolchain (#7153) 2026-06-12 08:50:44 -07:00
adminandGitHub e9e7fd1d61 Fix Shardok one-shot effect layering (#7151) 2026-06-12 08:50:06 -07:00
adminandGitHub a600086709 Use Bazel repo contents cache in CI (#7150) 2026-06-12 08:44:12 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
176ec1b03c Update alpine_linux Docker tag to v3.24 (#7149)
* Update alpine_linux Docker tag to v3.24

* Update generated dependency lockfiles

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 08:43:40 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
edf3d7596f Update guava monorepo to v33.6.0-jre (#7144)
* Update guava monorepo to v33.6.0-jre

* Update generated dependency lockfiles

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 08:30:19 -07:00
adminandGitHub 593f515c46 Fix Renovate generated dependency locks (#7148) 2026-06-12 08:24:58 -07:00
adminandGitHub 3452f906c4 Fix Renovate lockfile workflow actor guard (#7147) 2026-06-12 08:16:25 -07:00
adminandGitHub a3c64a504e Update Bazel lockfile on Renovate PRs (#7146) 2026-06-12 08:13:21 -07:00
adminandGitHub 9487f83053 Update URP probe playbook for installed package (#7139) 2026-06-12 07:24:26 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>admin
689b7a166a Configure Renovate (#7141)
* Add renovate.json

* Tune Renovate configuration

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dan Crosby <dan@danielcrosby.net>
2026-06-12 07:23:21 -07:00
adminandGitHub ae3b3b2872 Update apple_support to 2.6.1 (#7135) 2026-06-12 07:16:29 -07:00
adminandGitHub 489ddd0723 Document current URP inventory findings (#7138) 2026-06-12 07:09:25 -07:00
adminandGitHub 65cda21c5f Remove routine scene startup logs (#7137) 2026-06-12 07:04:42 -07:00
adminandGitHub 8c5dddfacb Report production material references in URP inventory (#7136) 2026-06-12 07:00:45 -07:00
adminandGitHub 87e7a17035 Update rules_scala to 7.2.6 (#7134) 2026-06-12 06:52:04 -07:00
adminandGitHub d40835e9ff Add URP baseline screenshot capture helper (#7133) 2026-06-12 06:49:46 -07:00
adminandGitHub e4c04401e1 Update Go AWS indirect modules (#7132) 2026-06-12 06:44:39 -07:00
adminandGitHub 6f7fb78b07 Update Go genproto modules (#7131) 2026-06-12 06:40:37 -07:00
adminandGitHub fbfaf19c52 Write URP inventory reports under Temp (#7130) 2026-06-12 06:40:14 -07:00
adminandGitHub 7f9e46314d Update Rust map generator rand dependencies (#7129) 2026-06-12 06:39:40 -07:00
adminandGitHub 885fbab556 Install Git LFS before cache parity checkout (#7128) 2026-06-12 06:36:34 -07:00
adminandGitHub 1099afbbf9 Update Go gRPC module (#7127) 2026-06-12 06:34:53 -07:00
adminandGitHub 9607e1da0b Update Go AWS SDK modules (#7126) 2026-06-12 06:34:40 -07:00
adminandGitHub 9be23e7ab9 Fix Shardok bridge and fire render order (#7115) 2026-06-12 06:34:15 -07:00
adminandGitHub 4f4a23ecd3 Update ml-stable-diffusion Swift package (#7125) 2026-06-12 06:30:14 -07:00
adminandGitHub 8a918f7027 Document module lock in Bazel cache key (#7124) 2026-06-12 05:15:38 -07:00
adminandGitHub c5eb8df481 Trigger Bazel workflows on module lock changes (#7123) 2026-06-11 22:04:21 -07:00
adminandGitHub c10724de53 Remove obsolete bazel-deps update script (#7122) 2026-06-11 21:58:59 -07:00
adminandGitHub 1bbb50877d Use CI build scripts for cache parity (#7120) 2026-06-11 21:47:52 -07:00
adminandGitHub 3bd3965372 Narrow Bazel autoload compatibility shim (#7121) 2026-06-11 21:47:10 -07:00
adminandGitHub c39a20636c Update protobuf to 35.1 (#7119) 2026-06-11 20:57:32 -07:00
adminandGitHub 19c262e28b Update LLVM toolchain to 22.1.7 (#7118) 2026-06-11 19:27:17 -07:00
adminandGitHub a7d9ed77b1 Update Go x/net dependency to 0.56.0 (#7116) 2026-06-11 16:58:57 -07:00
adminandGitHub 9228e21762 Trigger server builds on Go dependency changes (#7117) 2026-06-11 16:55:19 -07:00
adminandGitHub 77fff06a1d Update Go x/text dependency to 0.38.0 (#7114) 2026-06-11 16:36:59 -07:00
adminandGitHub 4c04964e4d Update Go x/sys dependency to 0.46.0 (#7113) 2026-06-11 16:15:56 -07:00
adminandGitHub db81a280d5 Add URP foreground UI overlay shader pass (#7112) 2026-06-11 16:14:53 -07:00
adminandGitHub b6f3a536aa Update Go JWT dependency to 5.3.1 (#7111) 2026-06-11 16:12:20 -07:00
adminandGitHub 45565f9544 Update Go protobuf to 1.36.11 (#7110) 2026-06-11 16:11:42 -07:00
adminandGitHub 2d1135eb5a Update Sparkle to 2.9.3 (#7108) 2026-06-11 16:08:18 -07:00
adminandGitHub 8794e3c1b7 Add URP bridge cutout shader pass (#7109) 2026-06-11 16:08:00 -07:00
adminandGitHub 8d59800f37 Update Temurin 25 image digests (#7106) 2026-06-11 16:00:35 -07:00
adminandGitHub aa44a894bf Install URP package without enabling pipeline (#7107) 2026-06-11 16:00:18 -07:00
adminandGitHub c240e295ed Update Alpine base image to 3.23 (#7105) 2026-06-11 15:48:54 -07:00
adminandGitHub db1a105d78 Remove stale Postprocessing csproj references (#7104) 2026-06-11 15:48:15 -07:00
adminandGitHub 55b478702a Persist URP migration audit reports (#7103) 2026-06-11 15:39:21 -07:00
adminandGitHub f8ce8075e5 Update AWS SDK to 2.46.9 (#7102) 2026-06-11 15:38:54 -07:00
adminandGitHub 4aeb8a66f9 Scope grpc layering check workaround (#7101) 2026-06-11 15:29:10 -07:00
adminandGitHub 52aea8ab81 Disable layering check for Shardok ARM64 build (#7100) 2026-06-11 15:24:12 -07:00
adminandGitHub 004e5725ce Update rules_jvm_external to 7.0 (#7096) 2026-06-11 15:13:39 -07:00
adminandGitHub df790953f0 Add URP probe playbook (#7099) 2026-06-11 15:11:51 -07:00
adminandGitHub e3f99c9649 Update AWS SDK to 2.46.8 (#7098) 2026-06-11 15:10:36 -07:00
adminandGitHub f0ae726510 Update aspect_rules_js to 3.2.2 (#7097) 2026-06-11 15:10:15 -07:00
adminandGitHub 0e63e98d38 Document third-party URP asset risk (#7093) 2026-06-11 15:09:14 -07:00
adminandGitHub 815ab5ff0b Remove redundant proto toolchain flag (#7092) 2026-06-11 15:08:51 -07:00
adminandGitHub 0177030c42 Remove redundant bzlmod flag (#7094) 2026-06-11 14:10:06 -07:00
adminandGitHub 8d08bbb70f Update grpc module to 1.81.1 (#7095) 2026-06-11 14:09:48 -07:00
adminandGitHub 8996fdd62c Remove unused heat shimmer shader (#7090) 2026-06-11 13:54:10 -07:00
adminandGitHub e7cad6a498 Remove redundant sandbox reuse flag (#7091) 2026-06-11 13:08:38 -07:00
adminandGitHub 33e0054422 Trigger app workflows on bazelrc changes (#7089) 2026-06-11 12:59:44 -07:00
adminandGitHub 279ed07811 Add URP visual baseline checklist (#7088) 2026-06-11 12:55:48 -07:00
adminandGitHub 0756135fcd Remove unused Post Processing Stack dependency (#7086) 2026-06-11 12:52:28 -07:00
adminandGitHub 73cdc8d948 Add URP migration inventory tool (#7085) 2026-06-11 12:25:12 -07:00
adminandGitHub 0f03b77de4 Remove stale grpc module map workaround (#7083) 2026-06-11 12:24:24 -07:00
adminandGitHub 93b7301ee0 Align local Bazel hydration with CI builds (#7084) 2026-06-11 12:24:00 -07:00
adminandGitHub 63feec92f4 Refresh URP migration plan (#7082) 2026-06-11 11:40:14 -07:00
adminandGitHub b104038221 Fix snake beast animation states (#7063)
* Fix snake beast animation states

* Use dedicated map animation controller for snakes
2026-06-11 11:27:59 -07:00
adminandGitHub d0a6527446 Use target-specific LLVM cross toolchains (#7081) 2026-06-11 11:27:32 -07:00
adminandGitHub 7379208c91 Update LLVM toolchain to 21.1.8 (#7079) 2026-06-11 10:09:27 -07:00
adminandGitHub 7df3482798 Clean up Unity relaunch warnings (#7078) 2026-06-11 09:40:28 -07:00
adminandGitHub 1f73167221 Trim Bazel 9 compatibility flags (#7075) 2026-06-11 09:40:07 -07:00
adminandGitHub ccc6953cc2 Add Bazel remote cache parity check (#7077) 2026-06-11 08:52:22 -07:00
adminandGitHub 043486f890 Remove unused DungeonMonsters demo scenes (#7076) 2026-06-11 08:51:17 -07:00
adminandGitHub c9105b5eaf Update generated C# project input defines (#7074) 2026-06-11 08:46:22 -07:00
adminandGitHub 03c053372e Remove legacy Unity input backend (#7073) 2026-06-11 08:41:35 -07:00
adminandGitHub 37838350fa Use Input System UI module in shared scene (#7070) 2026-06-11 08:31:04 -07:00
adminandGitHub 0afc0754c1 Load JavaInfo explicitly for Bazel 9 (#7072) 2026-06-11 08:24:25 -07:00
adminandGitHub 4f92065e0f Upgrade Bazel to 9.1.1 (#7071) 2026-06-11 07:57:51 -07:00
adminandGitHub 71f0f47da2 Clean up C++ test warnings (#7065)
* Clean up C++ test warnings

* Keep player setup expected map size readable

* Declare direct Bazel module dependencies (#7069)

* Check Shardok map proto parse results

* Prepare Apple and protobuf module resolution

* Declare direct Bazel module dependencies
2026-06-11 07:39:57 -07:00
adminandGitHub 2c5512cd4a Scope Xcode Bazel env to Apple tools (#7068) 2026-06-11 07:26:38 -07:00
adminandGitHub 55691a0515 Document local Bazel cache hydration (#7064) 2026-06-11 06:48:16 -07:00
adminandGitHub c06a5a9533 Prepare Bazel build files for native load cleanup (#7062) 2026-06-11 06:45:47 -07:00
adminandGitHub c4ad4529d8 Update firstpass csproj for Input System (#7060) 2026-06-11 05:48:00 -07:00
adminandGitHub 44e87fbe49 Route remaining pointer input through GameInput (#7058) 2026-06-11 04:55:20 -07:00
adminandGitHub 4b5897210c Route touch input through GameInput (#7057) 2026-06-10 22:30:30 -07:00
adminandGitHub d1191cf494 Install dmgbuild in a workflow venv (#7056) 2026-06-10 22:21:18 -07:00
adminandGitHub f4250d2b82 Document writable temp file locations (#7055) 2026-06-10 22:18:14 -07:00
adminandGitHub 1a4656992c Route Shardok shortcut input through GameInput (#7054) 2026-06-10 22:17:48 -07:00
adminandGitHub 8b67adbe00 Align scala3 staging version (#7053) 2026-06-10 22:08:51 -07:00
adminandGitHub 6e8c683d9f Gate Shardok grid input until model is ready (#7051) 2026-06-10 22:04:02 -07:00
adminandGitHub fb5ed2365f Update protobuf module (#7052) 2026-06-10 17:50:56 -07:00
adminandGitHub 44ae2f36a7 Update ScalaPB core (#7050) 2026-06-10 17:28:16 -07:00
adminandGitHub 41407d4ad0 Render Shardok bridges below unit labels (#7040) 2026-06-10 16:25:03 -07:00
adminandGitHub 78d92fee37 Update Nimbus JOSE JWT (#7049) 2026-06-10 16:17:35 -07:00
adminandGitHub 4e32bd7c51 Update apple_support (#7047) 2026-06-10 15:50:48 -07:00
adminandGitHub 2227cce8a6 Update Guava (#7046) 2026-06-10 15:40:41 -07:00
adminandGitHub 3e618b1e6f Update OkHttp (#7048) 2026-06-10 15:34:05 -07:00
adminandGitHub c392895e12 Update Error Prone annotations (#7044) 2026-06-10 15:32:55 -07:00
adminandGitHub f86fb86870 Update Gson (#7045) 2026-06-10 15:19:30 -07:00
adminandGitHub 3cacfc5225 Update Scalamock (#7043) 2026-06-10 15:19:12 -07:00
adminandGitHub 6e94c82098 Update AWS Lambda Java libraries (#7042) 2026-06-10 15:14:03 -07:00
adminandGitHub 1a3d0a82a6 Update AWS SDK (#7041) 2026-06-10 15:13:38 -07:00
adminandGitHub ea083c8c6c Update Sentry SDK (#7039) 2026-06-10 15:06:28 -07:00
adminandGitHub dd4ba80a2b Route pointer reads through GameInput (#7030)
* Route pointer reads through GameInput

* Set clicked panel alpha

* Revert accidental scroll view alpha change
2026-06-10 14:56:42 -07:00
adminandGitHub aea5bc614b Update Scalafmt CLI (#7038) 2026-06-10 14:55:11 -07:00
adminandGitHub e006d9e5a0 Update SQLite JDBC driver (#7036) 2026-06-10 14:47:22 -07:00
adminandGitHub 2daf624538 Remove stale LFS hooks before non-LFS checkouts (#7037) 2026-06-10 14:46:50 -07:00
adminandGitHub 7d6d8f3606 Update PostgreSQL JDBC driver (#7034) 2026-06-10 12:52:29 -07:00
adminandGitHub 0bac3ab38f Scope non-LFS checkout config (#7035) 2026-06-10 12:01:13 -07:00
adminandGitHub 7486af58b8 Disable LFS filters before deploy checkouts (#7032) 2026-06-10 10:23:30 -07:00
adminandGitHub 475321a86e Ensure AWS CLI for Addressables upload (#7033) 2026-06-10 10:22:15 -07:00
adminandGitHub 690ae25c88 Document gh PR body-file usage (#7031) 2026-06-10 09:49:37 -07:00
adminandGitHub e1e714a818 Update SLF4J dependencies (#7029) 2026-06-10 09:46:41 -07:00
adminandGitHub 27cd673038 Brighten clicked province panel background (#7027) 2026-06-10 09:44:39 -07:00
adminandGitHub 1da9fe3405 Include Python user bin for Mac DMG deploy (#7028) 2026-06-10 09:44:26 -07:00
adminandGitHub 401fdd26cd Update Netty dependencies (#7026) 2026-06-10 09:39:23 -07:00
adminandGitHub ad142c6f65 Render clicked province panel above map (#7025) 2026-06-10 09:30:43 -07:00
adminandGitHub 96d6d64f77 Give sqlite history test more time (#7024) 2026-06-10 09:22:09 -07:00
adminandGitHub 5e80ef41d6 Add Input System facade for shortcuts (#7023) 2026-06-10 09:09:23 -07:00
adminandGitHub 58553a4de1 Update Clang toolchain patch version (#7021) 2026-06-10 09:05:26 -07:00
adminandGitHub dc237d6e8f Enable Input System compatibility mode (#7022) 2026-06-10 08:36:53 -07:00
adminandGitHub 42fa4831f0 Remove host-specific workflow runner labels (#7018) 2026-06-10 08:00:40 -07:00
adminandGitHub cdab19e920 Update LLVM Bazel toolchain (#7020)
* Update Go Bazel rules

* Update Rust Bazel rules

* Update LLVM Bazel toolchain
2026-06-10 08:00:08 -07:00
adminandGitHub 05de5fd007 Stop caching Shardok game state locally (#7015) 2026-06-10 07:47:32 -07:00
adminandGitHub f1429b9560 Update Rust Bazel rules (#7019)
* Update Go Bazel rules

* Update Rust Bazel rules
2026-06-10 07:27:59 -07:00
adminandGitHub d9452bd763 Update Go Bazel rules (#7017) 2026-06-10 07:19:01 -07:00
adminandGitHub 8f24ef1fa7 Fix Unity Mac module verification path (#7016) 2026-06-10 07:17:49 -07:00
adminandGitHub e9a9475deb Update low-risk Bazel module dependencies (#7014) 2026-06-10 07:01:29 -07:00
adminandGitHub 21b56b8098 Upgrade Unity editor to 6000.4.10f1 (#7013) 2026-06-10 06:49:54 -07:00
adminandGitHub de04afc47f Report Eagle cache load rejection reasons (#7011) 2026-06-09 16:53:44 -07:00
adminandGitHub 1573fc4a82 Log and persist Eagle cache resume counts (#7010) 2026-06-09 16:45:35 -07:00
adminandGitHub e4a6edf7d7 Only warn for Eagle settings references when Eagle is loaded (#7009) 2026-06-09 16:35:49 -07:00
adminandGitHub df781f5643 Cache active Shardok histories with Eagle state (#7008) 2026-06-09 16:05:08 -07:00
adminandGitHub 6401db7043 Cache Eagle game state locally (#7007) 2026-06-09 14:08:32 -07:00
adminandGitHub 9cb3a60501 Remove retired Gameplay scene assets (#7006) 2026-06-09 13:31:13 -07:00
adminandGitHub 0f8880353b Add scene split validation tests (#7004) 2026-06-09 11:32:00 -07:00
adminandGitHub f1b89de5b3 Batch Postgres history replay reads (#7005) 2026-06-09 11:31:26 -07:00
adminandGitHub d99d2f5a53 Bulk read text state during recovery (#7003) 2026-06-09 09:22:37 -07:00
adminandGitHub 72efdff6bc Filter cached client text in storage queries (#7002) 2026-06-09 08:42:20 -07:00
adminandGitHub 9e8a506601 Hide superseded backstory text from clients (#7001) 2026-06-09 08:07:37 -07:00
adminandGitHub 77e1dd41cc Cache completed client text locally (#7000)
* Cache completed client text locally

* Fix completed client text cache scoping

* Avoid Unity API in client text cache writes
2026-06-09 08:06:01 -07:00
adminandGitHub aeb89e265e Staticize TMP fallback font asset (#6998)
* Staticize TMP fallback font asset

* Bake TMP fallback glyph coverage
2026-06-09 07:32:52 -07:00
adminandGitHub 9046e3d7ce Show JFR sidecar errors in admin (#6999) 2026-06-09 07:05:25 -07:00
adminandGitHub fc6b48793e Remove stale Gameplay scene references (#6997) 2026-06-09 06:32:24 -07:00
adminandGitHub 7d2050db5e Update scene split wording (#6996) 2026-06-09 05:11:22 -07:00
adminandGitHub 7e0d8360c4 Rename Eagle scene lighting setup (#6995) 2026-06-08 22:39:45 -07:00
adminandGitHub a0912bbc9e Close Postgres resources on unload (#6994) 2026-06-08 22:16:58 -07:00
adminandGitHub 987390b244 Remove connection readiness marker (#6993) 2026-06-08 22:12:08 -07:00
adminandGitHub e1c7036780 Ignore empty Postgres histories on load (#6992) 2026-06-08 22:07:27 -07:00
adminandGitHub da1a1a64c7 Add bulk SQLite to Postgres migration action (#6991) 2026-06-08 22:05:32 -07:00
adminandGitHub 138c0fcfa9 Remove stale Unity project script entries (#6990) 2026-06-08 22:03:37 -07:00
adminandGitHub 8e7acdfd97 Add admin SQLite to Postgres game migration (#6987) 2026-06-08 18:29:25 -07:00
adminandGitHub e1d8fd7031 Remove legacy Gameplay scene (#6988) 2026-06-08 18:29:00 -07:00
adminandGitHub f56289899b Update Eagle scene setup docs (#6986) 2026-06-08 17:16:55 -07:00
adminandGitHub 9e365b637c Store client text in Postgres for Postgres games (#6985) 2026-06-08 17:14:26 -07:00
adminandGitHub 15d5519634 Remove obsolete scene split meta files (#6984) 2026-06-08 17:12:11 -07:00
adminandGitHub bbf9d23c1f Remove obsolete scene split migration artifacts (#6983) 2026-06-08 17:02:34 -07:00
adminandGitHub 7685f4470c Default Eagle history backend to Postgres (#6982) 2026-06-08 16:58:01 -07:00
adminandGitHub ed47b5769d Remove legacy scene migration tooling (#6980) 2026-06-08 16:34:48 -07:00
adminandGitHub 11d734ce7f Make Postgres history load failures explicit (#6981) 2026-06-08 16:34:10 -07:00
adminandGitHub 275a72256f Bulk read Postgres action results (#6979) 2026-06-08 16:17:46 -07:00
adminandGitHub 13fa2ce8da Cache Postgres history replay reads (#6978) 2026-06-08 15:49:25 -07:00
adminandGitHub 06cd96f921 Retire legacy Gameplay startup fallback (#6973) 2026-06-08 15:47:12 -07:00
adminandGitHub 83ea7ce485 Render Shardok fire below unit labels (#6976) 2026-06-08 15:40:10 -07:00
adminandGitHub 9bdfad34e1 Unregister hero dropdown text listeners on destroy (#6977) 2026-06-08 15:26:17 -07:00
adminandGitHub 14ba4b3933 Restore notification beast animations (#6975)
* Restore notification beast visual layout timing

* Render beast effects on Eagle UI layer

* Wire Eagle canvases to main camera

* Render beast effects as UI overlays

* Add beast renderer diagnostics

* Render Eagle canvases through main camera

* Restore legacy Eagle ambient lighting
2026-06-08 15:21:22 -07:00
adminandGitHub 462bba117e Control history backend from admin settings (#6974) 2026-06-08 14:41:01 -07:00
adminandGitHub 0080f17a19 Wire Unity split scene runtime references (#6968)
* Wire split scene runtime references

* Activate connection scene root on startup

* Hide drop game confirmation on startup

* Preserve stored account UI on split startup

* Initialize OAuth before connection UI startup

* Ensure main queue for split scene callbacks

* Allow lobby rows without Eagle textures loaded

* Resolve Eagle controller from loaded scene

* Wait for Eagle controller after scene load

* Resolve additive scenes from sceneLoaded callback

* Ignore stale Eagle notifications after scene teardown

* Guard deferred Eagle update subscription

* Keep duplicate MainQueue from destroying scene UI

* Provide shared SoundManager for split scenes

* Wire Eagle map audio from shared SoundManager

* Match Main scene camera background to Gameplay

* Move Eagle texture catalog into Shared scene

* Load Shardok assets from Shared scene

* Wire Shardok audio and gate grid input

* Wire Shardok canvases to runtime camera

* Flatten Shardok grid overlay rendering

* Parent Shardok foreground to visible grid

* Let Shardok grid labels pass clicks through

* Allow Shardok clicks through map visuals

* Prefer Shardok grid hits over UI raycasts

* Render Shardok hex meshes through canvas

* Size Shardok canvas hex mesh bounds

* Log Shardok hex mesh canvas state

* Avoid transparent Shardok hex mesh fills

* Revert "Avoid transparent Shardok hex mesh fills"

This reverts commit 34b6ad9301ee1519ab774fa293aa72463c69165a.

* Revert "Log Shardok hex mesh canvas state"

This reverts commit 15516200859584cde46ab54eb1dd5f5a20415e13.

* Revert "Size Shardok canvas hex mesh bounds"

This reverts commit 25c3994f39abacd7ff6bea7376a045e557ca2e36.

* Revert "Render Shardok hex meshes through canvas"

This reverts commit 8cf8f3b78fbfdd904a3e01a2165ac37cb66f0c34.

* Render Shardok canvases through runtime camera

* Layer Shardok labels above hex overlays

* Move Settings UI toward shared scene wiring

* Move persistent UI into Shared scene

* Tighten split scene transition wiring
2026-06-08 13:46:12 -07:00
adminandGitHub aff09aedc3 Add Postgres history smoke check (#6972) 2026-06-08 08:35:53 -07:00
adminandGitHub 47b813fba8 Bind structured action blobs as binary nulls (#6971) 2026-06-08 08:25:52 -07:00
adminandGitHub 5da3a75e7c Add Postgres game history backend (#6970) 2026-06-08 08:20:47 -07:00
adminandGitHub 835d1b7a83 Add Eagle and Shardok scene rebuild tool (#6965)
* Add Eagle and Shardok scene rebuild tool

* Clear copied split-scene external references

* Make split scene rebuild idempotent

* Snapshot scene roots before rebuild deletion

* Generate Eagle and Shardok split scenes
2026-06-08 07:51:08 -07:00
adminandGitHub 7408016628 Add Postgres latency benchmark admin tool (#6967) 2026-06-08 07:48:32 -07:00
adminandGitHub fe2b516e59 Wire Postgres secrets into Eagle deploy (#6966)
* Wire Postgres secrets into Eagle deploy

* Default Postgres SSL mode in compose
2026-06-08 07:00:30 -07:00
adminandGitHub c13ae90c67 Document Postgres history migration plan (#6964) 2026-06-08 06:45:42 -07:00
adminandGitHub eeadacadd4 Render tactical bridges unlit (#6963) 2026-06-08 06:35:51 -07:00
adminandGitHub fa87121ba6 Move error UI into shared scene (#6962) 2026-06-07 22:00:48 -07:00
adminandGitHub b5c775f7ae Release SQLite upload lock before reading snapshots (#6958) 2026-06-07 21:53:38 -07:00
adminandGitHub ca35ed8f6f Wire split connection shared services (#6959) 2026-06-07 21:45:48 -07:00
adminandGitHub f7e47bdc31 Wait longer for warmup follow-up commands (#6961) 2026-06-07 21:45:18 -07:00
adminandGitHub d210cbb820 Add Connection scene migration tool (#6957)
* Add Connection scene migration tool

* Copy connection UI into split scene
2026-06-07 18:59:42 -07:00
adminandGitHub 50948bccae Persist SQLite history with base and deltas (#6956) 2026-06-07 18:52:12 -07:00
adminandGitHub 98a1c2b361 Add Connection scene readiness marker (#6955) 2026-06-07 18:35:47 -07:00
adminandGitHub 38608bd3e4 Keep shared error handler scene-owned (#6954) 2026-06-07 18:24:23 -07:00
adminandGitHub e4e43292df Default Main scene to split startup (#6952) 2026-06-07 18:16:07 -07:00
adminandGitHub 351cc6f658 Use remote persister for SQLite database uploads (#6953) 2026-06-07 18:15:10 -07:00
adminandGitHub 603e7fb071 Stop tracking Unity editor user settings (#6951) 2026-06-07 17:24:13 -07:00
adminandGitHub 0ccf0539ba Centralize connection mode visibility (#6939) 2026-06-07 17:16:34 -07:00
adminandGitHub 033e1f4675 Serialize SQLite history access internally (#6950) 2026-06-07 17:11:11 -07:00
adminandGitHub c9aa6df721 Fix Shardok model lifecycle cleanup (#6948) 2026-06-07 16:54:42 -07:00
adminandGitHub 734ea7c62e Validate SQLite downloads before loading (#6949) 2026-06-07 16:53:59 -07:00
adminandGitHub 23f29249ef Upload SQLite history from snapshots (#6947) 2026-06-07 16:14:31 -07:00
adminandGitHub eeab47e480 Persist Shardok SQLite deltas to S3 (#6944) 2026-06-07 15:48:59 -07:00
adminandGitHub 22b52f498a Use passive SQLite checkpoints for uploads (#6945) 2026-06-07 15:42:47 -07:00
adminandGitHub f403980853 Fallback build warmup binary during deploy (#6946) 2026-06-07 15:39:33 -07:00
adminandGitHub eb6fcb5ce6 Move sqlite history uploads off command path (#6943) 2026-06-07 15:01:26 -07:00
adminandGitHub 299f4625cd Map Shardok clicks through screen space (#6942) 2026-06-07 13:45:11 -07:00
adminandGitHub 84788452b3 Add JFR tracing for SQLite persistence (#6941) 2026-06-07 11:41:24 -07:00
adminandGitHub 5a81a203e8 Bootstrap Unity client from split scenes (#6935) 2026-06-07 09:11:31 -07:00
adminandGitHub 9c60652eaa Instrument Shardok battle latency (#6938) 2026-06-07 09:08:06 -07:00
adminandGitHub 9b384234d8 Clean up AI quest completion docs (#6937) 2026-06-06 18:51:15 -07:00
adminandGitHub ef591539d3 Add prisoner quest invariant coverage (#6936) 2026-06-06 17:10:32 -07:00
adminandGitHub 1db0c6b282 Clarify failed quest notification source hero (#6932) 2026-06-06 05:48:54 -07:00
adminandGitHub 61da5c1e92 Include recon counts in quest prompts (#6934) 2026-06-06 05:11:56 -07:00
adminandGitHub 2d713f4e57 Use release wording for prisoner quests (#6933) 2026-06-06 05:11:14 -07:00
adminandGitHub fab32ae871 Normalize quest LLM request ids (#6931) 2026-06-06 05:10:51 -07:00
adminandGitHub bc762f3b45 Fix quest outcome prompt name grammar (#6930) 2026-06-06 05:10:21 -07:00
adminandGitHub 7e5c5af876 Document Unity scene separation plan (#6929) 2026-06-06 04:43:26 -07:00
adminandGitHub c71c846a81 Allow text listeners to change during updates (#6928) 2026-06-05 05:39:57 -07:00
adminandGitHub cfc218ba30 Regenerate Shardok AI baselines after timeout fix (#6927) 2026-06-04 13:15:08 -07:00
adminandGitHub 29a481f7b4 Show sworn hero faction in Free Heroes panel (#6926) 2026-06-04 10:47:17 -07:00
adminandGitHub fce2a104e8 Fix break alliance imprisonment notification text (#6925) 2026-06-04 10:40:22 -07:00
adminandGitHub 0fb419cbd8 Suppress invited faction destroyed notifications (#6924) 2026-06-04 10:03:13 -07:00
adminandGitHub bf83e7bde3 Preserve Shardok selection during command updates (#6921) 2026-06-04 08:21:30 -07:00
adminandGitHub 54bfea07d4 Fix ID timeout score pollution (#6923) 2026-06-04 06:23:52 -07:00
adminandGitHub ab74172744 Only generate fulfillable prisoner quests (#6922) 2026-06-03 16:15:41 -07:00
adminandGitHub 80cbe1b60f Stream command results before phase advancement (#6920) 2026-06-03 14:17:05 -07:00
adminandGitHub 488ffbf559 Acknowledge commands before AI continuation (#6919) 2026-06-03 09:45:35 -07:00
adminandGitHub 50f9ccb28c Discover dynamic text editor tests (#6918) 2026-06-03 09:44:59 -07:00
adminandGitHub 34cce82cb0 Reject null dynamic render values (#6917) 2026-06-03 09:43:07 -07:00
adminandGitHub f0baa8bdba Require client text provider inputs (#6916) 2026-06-03 09:41:37 -07:00
adminandGitHub 52fe13325b Require generated text listener inputs (#6915) 2026-06-03 09:41:13 -07:00
adminandGitHub de15e2a66a Reject null dynamic placeholder entries (#6914) 2026-06-03 09:33:41 -07:00
adminandGitHub a506abae98 Send human command results before AI continuation (#6912) 2026-06-03 09:26:23 -07:00
adminandGitHub b43406b7c7 Require dynamic text binding inputs (#6913) 2026-06-03 09:25:13 -07:00
adminandGitHub 919990a95c Require parsed dynamic template text (#6911) 2026-06-03 09:18:36 -07:00
adminandGitHub 48162b20c0 Require dynamic placeholder fallback text (#6910) 2026-06-03 09:07:42 -07:00
adminandGitHub 88433e481e Clarify prisoner quest divine prompts (#6909) 2026-06-03 09:07:16 -07:00
adminandGitHub cc5eb01dba Preserve dynamic placeholder discovery order (#6908) 2026-06-03 09:03:11 -07:00
adminandGitHub d5dda77a47 Render dynamic text templates in one pass (#6907) 2026-06-03 08:47:16 -07:00
adminandGitHub 663b2b34f7 Seal dynamic text carrier types (#6906) 2026-06-03 08:17:18 -07:00
1459 changed files with 445008 additions and 127507 deletions
+39 -18
View File
@@ -3,18 +3,9 @@
# for now: filter out annoying TASTY warnings
common --ui_event_filters=-INFO
common --enable_bzlmod
# Use pre-built protoc binary instead of compiling from source
common --incompatible_enable_proto_toolchain_resolution
common --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true
# Don't use toolchains_llvm for the swift app build
common:mactools --ignore_dev_dependency
# Try to speed up sandboxes
common --experimental_reuse_sandbox_directories
common --enable_platform_specific_config
common --strategy=Scalac=worker
@@ -25,10 +16,44 @@ common --jobs=64
common --cxxopt="--std=c++23"
common --cxxopt="-Wno-deprecated-non-prototype"
common --per_file_copt=src/test/cpp/.*@-Wno-character-conversion
common --per_file_copt=src/test/cpp/.*@-Wno-deprecated-enum-compare
common --per_file_copt=src/test/cpp/.*@-Wno-sign-compare
common --per_file_copt=src/test/cpp/.*@-Wno-unknown-warning-option
common --host_cxxopt="--std=c++23"
# C++ sanitizer configs for targeted Shardok safety checks.
# Example: bazel test --config=asan //src/test/cpp/net/eagle0/shardok/library/...
build:asan --compilation_mode=dbg
build:asan --strip=never
build:asan --copt=-fno-omit-frame-pointer
build:asan --copt=-Wno-macro-redefined
build:asan --copt=-fsanitize=address
build:asan --linkopt=-fsanitize=address
test:asan --test_env=ASAN_OPTIONS=detect_leaks=0:strict_init_order=1
build:ubsan --compilation_mode=dbg
build:ubsan --strip=never
build:ubsan --copt=-fno-omit-frame-pointer
build:ubsan --copt=-Wno-macro-redefined
build:ubsan --copt=-fsanitize=undefined
build:ubsan --linkopt=-fsanitize=undefined
test:ubsan --test_env=UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
build:tsan --compilation_mode=dbg
build:tsan --strip=never
build:tsan --copt=-fno-omit-frame-pointer
build:tsan --copt=-Wno-macro-redefined
build:tsan --copt=-fsanitize=thread
build:tsan --linkopt=-fsanitize=thread
test:tsan --test_env=TSAN_OPTIONS=halt_on_error=1
common --javacopt="-Xlint:-options"
# Prefer protobuf's prebuilt protoc toolchain instead of building protoc from
# source when compatible binaries are available.
common --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc
# std::filesystem and other modern C++ deps require macOS 10.15+.
common:macos --macos_minimum_os=10.15
common:macos --host_macos_minimum_os=10.15
@@ -37,13 +62,8 @@ common:macos --host_macos_minimum_os=10.15
# Use host_linkopt for macOS-specific flags to avoid passing them to Linux cross-compilation
common:macos --host_linkopt=-Wl,-no_warn_duplicate_libraries
# Workaround for grpc cf_event_engine build error on macOS with Bazel 7.x
# See: https://github.com/grpc/grpc/issues/37619
common:macos --features=-module_maps
# Pin DEVELOPER_DIR so Apple repo rules use full Xcode instead of Command Line
# Tools. The sync script writes .bazelrc.xcode with an Xcode build-version
# marker so in-place Xcode updates refresh Bazel's generated Apple toolchains.
# Pin default DEVELOPER_DIR so Apple repo rules use full Xcode instead of
# Command Line Tools when they are needed.
common:macos --repo_env=DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
# Local machine or CI overrides. This lets Bazel-only runners use Command Line
@@ -51,8 +71,9 @@ common:macos --repo_env=DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
# generated Xcode settings win when both files exist.
try-import %workspace%/.bazelrc.local
# Xcode config for macOS builds. Generated by scripts/sync_bazel_xcode.sh.
# Bakes the Xcode build version into action cache keys and sets DEVELOPER_DIR.
# Xcode config for Apple tool builds. Generated by scripts/sync_bazel_xcode.sh.
# Bakes the Xcode build version into mactools action cache keys without making
# ordinary macOS Bazel builds depend on the installed Xcode build number.
try-import %workspace%/.bazelrc.xcode
common --java_language_version=25
+1 -1
View File
@@ -1 +1 @@
8.5.1
9.1.1
+32
View File
@@ -0,0 +1,32 @@
Checks: >
-*,
bugprone-*,
performance-*,
readability-*,
modernize-*,
cppcoreguidelines-*,
-bugprone-easily-swappable-parameters,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-modernize-use-trailing-return-type,
-readability-convert-member-functions-to-static,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-magic-numbers
WarningsAsErrors: ''
HeaderFilterRegex: 'src/(main|test)/cpp/net/eagle0/(shardok|common)/.*'
FormatStyle: file
CheckOptions:
readability-braces-around-statements.ShortStatementLines: '1'
readability-function-size.LineThreshold: '160'
readability-function-size.StatementThreshold: '80'
readability-function-size.BranchThreshold: '20'
modernize-use-nullptr.NullMacros: 'NULL'
+9
View File
@@ -0,0 +1,9 @@
name: Setup Bazel
description: Ensure Bazel is installed on a self-hosted runner.
runs:
using: composite
steps:
- name: Ensure Bazel installed
shell: bash
run: ./ci/github_actions/ensure_bazel_installed.sh
@@ -1,76 +0,0 @@
name: Artifact Storage Check
on:
schedule:
# Run every 6 hours
- cron: '0 */6 * * *'
workflow_dispatch:
permissions:
contents: read
actions: write
jobs:
cleanup-expired:
runs-on: ubuntu-latest
steps:
- name: Delete expired artifacts and artifacts older than 3 days
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Fetching all artifacts..."
gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | "\(.id)\t\(.created_at)\t\(.expired)\t\(.name)"' > /tmp/all_artifacts.txt
total=$(wc -l < /tmp/all_artifacts.txt)
echo "Found $total total artifacts"
cutoff=$(date -u -d '3 days ago' '+%Y-%m-%dT%H:%M:%SZ')
echo "Deleting expired artifacts and artifacts created before $cutoff"
deleted=0
while IFS=$'\t' read -r id created_at expired name; do
if [[ "$expired" == "true" || "$created_at" < "$cutoff" ]]; then
gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/$id" 2>/dev/null && deleted=$((deleted + 1))
if [ $((deleted % 50)) -eq 0 ]; then
echo "Deleted $deleted artifacts so far..."
fi
fi
done < /tmp/all_artifacts.txt
echo "Cleanup complete. Deleted $deleted artifacts out of $total total."
rm -f /tmp/all_artifacts.txt
check-storage:
needs: cleanup-expired
runs-on: ubuntu-latest
steps:
- name: Check artifact storage size
env:
GH_TOKEN: ${{ github.token }}
run: |
# Calculate active artifact storage. The artifacts API can list expired
# artifacts until they are explicitly deleted, so do not count them.
total_bytes=$(gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | select(.expired == false) | .size_in_bytes' | awk '{sum+=$1} END {print sum}')
total_mb=$(( ${total_bytes:-0} / 1024 / 1024 ))
echo "Total artifact storage: ${total_mb} MB"
# Fail if over 500MB
if [ "$total_mb" -gt 500 ]; then
echo "::error::Artifact storage is ${total_mb} MB, which exceeds the 500 MB threshold!"
echo ""
echo "Largest artifacts:"
# Save to temp file to avoid SIGPIPE/broken pipe errors with head
gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | select(.expired == false) | "\(.size_in_bytes)\t\(.name)\t\(.created_at)"' > /tmp/artifacts.txt
sort -rn /tmp/artifacts.txt | head -20 | \
awk -F'\t' '{printf "%d MB\t%s\t%s\n", $1/1024/1024, $2, $3}'
rm -f /tmp/artifacts.txt
exit 1
fi
echo "Storage is within acceptable limits."
+50 -4
View File
@@ -6,12 +6,38 @@ on:
paths:
- 'src/main/go/net/eagle0/authservice/**'
- 'src/main/go/net/eagle0/authcli/**'
- 'src/main/go/net/eagle0/common/**'
- 'src/main/go/net/eagle0/util/**'
- 'src/main/protobuf/net/eagle0/eagle/api/auth.proto'
- 'src/main/protobuf/net/eagle0/eagle/api/admin/**'
- 'src/main/protobuf/net/eagle0/eagle/internal/auth_internal.proto'
- 'src/main/resources/net/eagle0/attributions.json'
- 'ci/BUILD.bazel'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- 'go.mod'
- 'go.sum'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- '.bazelrc'
- '.github/workflows/auth_build.yml'
pull_request:
paths:
- 'src/main/go/net/eagle0/authservice/**'
- 'src/main/go/net/eagle0/authcli/**'
- 'src/main/go/net/eagle0/common/**'
- 'src/main/go/net/eagle0/util/**'
- 'src/main/protobuf/net/eagle0/eagle/api/auth.proto'
- 'src/main/protobuf/net/eagle0/eagle/api/admin/**'
- 'src/main/protobuf/net/eagle0/eagle/internal/auth_internal.proto'
- 'src/main/resources/net/eagle0/attributions.json'
- 'ci/BUILD.bazel'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- 'go.mod'
- 'go.sum'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- '.bazelrc'
- '.github/workflows/auth_build.yml'
workflow_dispatch:
@@ -26,23 +52,43 @@ permissions:
contents: read
concurrency:
group: auth-build-deploy
cancel-in-progress: false
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || 'deploy' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build-auth:
runs-on: [self-hosted, bazel, halfdan]
runs-on: [self-hosted, bazel]
outputs:
image_tag: ${{ steps.push-auth.outputs.image_tag }}
steps:
- name: Prepare non-LFS checkout
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
run: |
git config --global --unset-all filter.lfs.process || true
git config --global filter.lfs.smudge "cat"
git config --global filter.lfs.clean "cat"
git config --global filter.lfs.required false
if [ -d ".git" ]; then
git config --local --unset-all filter.lfs.process || true
git config --local filter.lfs.smudge "cat"
git config --local filter.lfs.clean "cat"
git config --local filter.lfs.required false
fi
rm -f .git/hooks/post-checkout .git/hooks/post-merge .git/hooks/pre-push
- name: Checkout repository
uses: actions/checkout@v6
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
uses: ./.github/actions/setup-bazel
- name: Build Auth Server Docker image
id: build-auth
-41
View File
@@ -1,41 +0,0 @@
name: Bazel Cache Cleanup
on:
# Disabled: bazel clean fails when another runner shares the output_base
# on /Volumes/remote_cache (unlinkat "Directory not empty" race).
# See https://github.com/nolen777/eagle0/issues/TBD for details.
# schedule:
# - cron: '0 0 * * 0'
workflow_dispatch: # Allow manual trigger
jobs:
cleanup:
runs-on: [self-hosted, bazel, halfdan]
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Show disk usage before cleanup
run: |
echo "=== Disk usage before cleanup ==="
BAZEL_USER_ROOT="/private/var/tmp/_bazel_$(whoami)"
echo "Bazel user root: $BAZEL_USER_ROOT"
du -sh "$BAZEL_USER_ROOT" 2>/dev/null || echo "Not found"
df -h .
- name: Run bazel clean
run: |
echo "=== Running bazel clean ==="
bazel clean
echo "Clean complete"
- name: Show disk usage after cleanup
run: |
echo "=== Disk usage after cleanup ==="
BAZEL_USER_ROOT="/private/var/tmp/_bazel_$(whoami)"
du -sh "$BAZEL_USER_ROOT" 2>/dev/null || echo "Not found"
df -h .
@@ -0,0 +1,136 @@
name: Bazel Cache Maintenance
on:
workflow_dispatch:
inputs:
target:
description: 'Maintenance target'
required: true
default: 'parity'
type: choice
options:
- parity
- clean
schedule:
- cron: '17 11 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
warm-cache:
if: github.event_name == 'schedule' || github.event.inputs.target == 'parity'
runs-on: [self-hosted, bazel]
steps:
- name: Clean workspace
run: |
git sparse-checkout disable 2>/dev/null || true
git config --local core.sparseCheckout false 2>/dev/null || true
git config --local --unset extensions.worktreeConfig 2>/dev/null || true
rm -f .git/info/sparse-checkout .git/config.worktree 2>/dev/null || true
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
uses: ./.github/actions/setup-bazel
- name: Warm remote cache
run: ./scripts/check_bazel_remote_cache_parity.sh warm
verify-cache:
needs: warm-cache
if: github.event_name == 'schedule' || github.event.inputs.target == 'parity'
runs-on: [self-hosted, bazel]
strategy:
fail-fast: false
matrix:
probe: [a, b]
steps:
- name: Clean workspace
run: |
git sparse-checkout disable 2>/dev/null || true
git config --local core.sparseCheckout false 2>/dev/null || true
git config --local --unset extensions.worktreeConfig 2>/dev/null || true
rm -f .git/info/sparse-checkout .git/config.worktree 2>/dev/null || true
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
uses: ./.github/actions/setup-bazel
- name: Verify remote cache parity
run: ./scripts/check_bazel_remote_cache_parity.sh verify
clean-cache:
if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'clean'
runs-on: [self-hosted, bazel]
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Show disk usage before cleanup
run: |
echo "=== Disk usage before cleanup ==="
BAZEL_USER_ROOT="/private/var/tmp/_bazel_$(whoami)"
echo "Bazel user root: $BAZEL_USER_ROOT"
du -sh "$BAZEL_USER_ROOT" 2>/dev/null || echo "Not found"
df -h .
- name: Run bazel clean
run: |
echo "=== Running bazel clean ==="
bazel clean
echo "Clean complete"
- name: Show disk usage after cleanup
run: |
echo "=== Disk usage after cleanup ==="
BAZEL_USER_ROOT="/private/var/tmp/_bazel_$(whoami)"
du -sh "$BAZEL_USER_ROOT" 2>/dev/null || echo "Not found"
df -h .
+11 -2
View File
@@ -7,9 +7,12 @@ on:
- 'src/**'
- 'WORKSPACE'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- 'BUILD.bazel'
- '.bazelrc'
- 'ci/github_actions/ensure_bazel_installed.sh'
- 'ci/github_actions/summarize_bazel_bep.py'
- '.github/actions/setup-bazel/**'
- '.github/workflows/bazel_test.yml'
- '!src/main/csharp/**'
- '!src/test/csharp/**'
@@ -18,9 +21,12 @@ on:
- 'src/**'
- 'WORKSPACE'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- 'BUILD.bazel'
- '.bazelrc'
- 'ci/github_actions/ensure_bazel_installed.sh'
- 'ci/github_actions/summarize_bazel_bep.py'
- '.github/actions/setup-bazel/**'
- '.github/workflows/bazel_test.yml'
- '!src/main/csharp/**'
- '!src/test/csharp/**'
@@ -104,10 +110,13 @@ jobs:
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
- name: Setup Bazel
uses: ./.github/actions/setup-bazel
- name: Run tests
run: bazel test --build_event_json_file=test.json //src/test/... //src/main/go/...
- name: Summarize Bazel build metrics
if: always()
run: python3 ci/github_actions/summarize_bazel_bep.py test.json
- name: Collect failed test logs
if: always()
run: |
-31
View File
@@ -1,31 +0,0 @@
name: Blob Cleanup
on:
schedule:
# Run daily at 04:00 UTC
- cron: '0 4 * * *'
workflow_dispatch: # Allow manual trigger
permissions:
contents: read
jobs:
cleanup:
runs-on: [self-hosted, bazel, halfdan]
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
clean: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
- name: Clean up unreferenced blobs
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: |
bazel run //src/main/go/net/eagle0/build/blob_cleanup:blob_cleanup -- --min-age=1h
+20 -70
View File
@@ -22,65 +22,13 @@ permissions:
contents: read
jobs:
build-sysroot-amd64:
if: ${{ inputs.architecture == 'amd64' || inputs.architecture == 'both' }}
build-sysroot:
name: Build Linux Sysroot (${{ matrix.architecture }})
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
strategy:
fail-fast: false
matrix: ${{ fromJSON(inputs.architecture == 'both' && '{"include":[{"architecture":"amd64","build_script":"./tools/sysroot/build_sysroot.sh","artifact_name":"ubuntu-noble-sysroot-amd64","archive_name":"ubuntu_noble_amd64_sysroot.tar.xz","checksum_name":"ubuntu_noble_amd64_sysroot.sha256","module_name":"linux_sysroot"},{"architecture":"arm64","build_script":"./tools/sysroot/build_sysroot_arm64.sh","artifact_name":"ubuntu-noble-sysroot-arm64","archive_name":"ubuntu_noble_arm64_sysroot.tar.xz","checksum_name":"ubuntu_noble_arm64_sysroot.sha256","module_name":"linux_sysroot_arm64"}]}' || inputs.architecture == 'arm64' && '{"include":[{"architecture":"arm64","build_script":"./tools/sysroot/build_sysroot_arm64.sh","artifact_name":"ubuntu-noble-sysroot-arm64","archive_name":"ubuntu_noble_arm64_sysroot.tar.xz","checksum_name":"ubuntu_noble_arm64_sysroot.sha256","module_name":"linux_sysroot_arm64"}]}' || '{"include":[{"architecture":"amd64","build_script":"./tools/sysroot/build_sysroot.sh","artifact_name":"ubuntu-noble-sysroot-amd64","archive_name":"ubuntu_noble_amd64_sysroot.tar.xz","checksum_name":"ubuntu_noble_amd64_sysroot.sha256","module_name":"linux_sysroot"}]}') }}
- name: Build sysroot
run: ./tools/sysroot/build_sysroot.sh
- name: Upload sysroot artifact
uses: actions/upload-artifact@v7
with:
name: ubuntu-noble-sysroot-amd64
path: tools/sysroot/output/
retention-days: 1
- name: Install AWS CLI
run: |
if ! command -v aws &> /dev/null; then
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -q awscliv2.zip
sudo ./aws/install
fi
- name: Upload to DigitalOcean Spaces
env:
AWS_ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.SECRET_KEY }}
run: |
# Upload sysroot tarball to DO Spaces
aws s3 cp tools/sysroot/output/ubuntu_noble_amd64_sysroot.tar.xz \
s3://eagle0-sysroot/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
# Upload sha256 file
aws s3 cp tools/sysroot/output/ubuntu_noble_amd64_sysroot.sha256 \
s3://eagle0-sysroot/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.sha256 \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
echo ""
echo "=== AMD64 Sysroot uploaded ==="
echo "URL: https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz"
echo "SHA256: $(cat tools/sysroot/output/ubuntu_noble_amd64_sysroot.sha256)"
echo ""
echo "Update MODULE.bazel with:"
echo "sysroot("
echo " name = \"linux_sysroot\","
echo " sha256 = \"$(cat tools/sysroot/output/ubuntu_noble_amd64_sysroot.sha256)\","
echo " urls = [\"https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz\"],"
echo ")"
build-sysroot-arm64:
if: ${{ inputs.architecture == 'arm64' || inputs.architecture == 'both' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
@@ -88,20 +36,22 @@ jobs:
persist-credentials: false
- name: Set up QEMU for ARM64 emulation
if: ${{ matrix.architecture == 'arm64' }}
uses: docker/setup-qemu-action@v4
with:
platforms: arm64
- name: Set up Docker Buildx
if: ${{ matrix.architecture == 'arm64' }}
uses: docker/setup-buildx-action@v4
- name: Build ARM64 sysroot
run: ./tools/sysroot/build_sysroot_arm64.sh
- name: Build sysroot
run: ${{ matrix.build_script }}
- name: Upload sysroot artifact
uses: actions/upload-artifact@v7
with:
name: ubuntu-noble-sysroot-arm64
name: ${{ matrix.artifact_name }}
path: tools/sysroot/output/
retention-days: 1
@@ -119,25 +69,25 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.SECRET_KEY }}
run: |
# Upload sysroot tarball to DO Spaces
aws s3 cp tools/sysroot/output/ubuntu_noble_arm64_sysroot.tar.xz \
s3://eagle0-sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz \
aws s3 cp "tools/sysroot/output/${{ matrix.archive_name }}" \
"s3://eagle0-sysroot/${{ inputs.version }}/${{ matrix.archive_name }}" \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
# Upload sha256 file
aws s3 cp tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256 \
s3://eagle0-sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.sha256 \
aws s3 cp "tools/sysroot/output/${{ matrix.checksum_name }}" \
"s3://eagle0-sysroot/${{ inputs.version }}/${{ matrix.checksum_name }}" \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
echo ""
echo "=== ARM64 Sysroot uploaded ==="
echo "URL: https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz"
echo "SHA256: $(cat tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256)"
echo "=== ${{ matrix.architecture }} Sysroot uploaded ==="
echo "URL: https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/${{ matrix.archive_name }}"
echo "SHA256: $(cat "tools/sysroot/output/${{ matrix.checksum_name }}")"
echo ""
echo "Update MODULE.bazel with:"
echo "sysroot("
echo " name = \"linux_sysroot_arm64\","
echo " sha256 = \"$(cat tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256)\","
echo " urls = [\"https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz\"],"
echo " name = \"${{ matrix.module_name }}\","
echo " sha256 = \"$(cat "tools/sysroot/output/${{ matrix.checksum_name }}")\","
echo " urls = [\"https://eagle0-sysroot.sfo3.digitaloceanspaces.com/${{ inputs.version }}/${{ matrix.archive_name }}\"],"
echo ")"
+55 -10
View File
@@ -19,7 +19,33 @@ on:
- 'src/main/resources/**'
- 'ci/BUILD.bazel'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- '.bazelrc'
- 'docker-compose.prod.yml'
- 'nginx/**'
- '.github/workflows/docker_build.yml'
pull_request:
paths:
# Note: Auth changes trigger auth_build.yml instead
# Note: Windows installer changes trigger installer_build.yml instead
- 'src/main/go/net/eagle0/admin_server/**'
- 'src/main/go/net/eagle0/common/**'
- 'src/main/go/net/eagle0/util/**'
- 'src/main/scala/**'
- 'src/main/protobuf/**'
- '!src/main/protobuf/net/eagle0/eagle/api/auth.proto'
- '!src/main/protobuf/net/eagle0/eagle/internal/auth_internal.proto'
- 'src/main/resources/**'
- 'ci/BUILD.bazel'
- 'ci/github_actions/ensure_bazel_installed.sh'
- 'ci/github_actions/fetch_lfs.sh'
- '.github/actions/setup-bazel/**'
- 'go.mod'
- 'go.sum'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- '.bazelrc'
- 'docker-compose.prod.yml'
- 'nginx/**'
@@ -36,8 +62,8 @@ on:
# The build-all job checks if it's still the latest commit on main and skips if not,
# so intermediate commits don't waste time building when a newer one is already queued.
concurrency:
group: docker-build-deploy
cancel-in-progress: false
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || 'deploy' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
@@ -48,13 +74,15 @@ jobs:
# This uses 1 runner slot instead of 4, and Bazel parallelizes internally
# NOTE: Must run on a runner with the bazel label to ensure consistent Bazel cache
build-all:
runs-on: [self-hosted, bazel, halfdan]
runs-on: [self-hosted, bazel]
outputs:
eagle_image_tag: ${{ steps.push-images.outputs.eagle_image_tag }}
admin_image_tag: ${{ steps.push-images.outputs.admin_image_tag }}
jfr_sidecar_image_tag: ${{ steps.push-images.outputs.jfr_sidecar_image_tag }}
skip_build: ${{ steps.check-latest.outputs.skip }}
steps:
- name: Skip if superseded
if: github.event_name != 'pull_request'
id: check-latest
run: |
# Check if there's a newer run of this workflow waiting in the queue.
@@ -95,7 +123,7 @@ jobs:
- name: Ensure Bazel installed
if: steps.check-latest.outputs.skip != 'true'
run: ./ci/github_actions/ensure_bazel_installed.sh
uses: ./.github/actions/setup-bazel
- name: Fetch LFS files needed for admin server
if: steps.check-latest.outputs.skip != 'true'
@@ -116,7 +144,7 @@ jobs:
bazel build \
--stamp \
--platforms=//:linux_x86_64 \
--extra_toolchains=@llvm_toolchain_linux//:all \
--extra_toolchains=@llvm_toolchain_linux//:cc-toolchain-x86_64-linux \
//ci:eagle_server_image \
//ci:admin_server_image \
//ci:jfr_sidecar_image \
@@ -141,7 +169,7 @@ jobs:
echo "JFR Sidecar: $JFR_PATH"
- name: Upload warmup binary
if: steps.check-latest.outputs.skip != 'true'
if: steps.check-latest.outputs.skip != 'true' && github.event_name != 'pull_request'
uses: actions/upload-artifact@v7
with:
name: warmup-binary
@@ -161,7 +189,7 @@ jobs:
- name: Push all images to DO registry
id: push-images
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
if: steps.check-latest.outputs.skip != 'true' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true'))
env:
DOCKER_CONFIG: ${{ github.workspace }}/.docker
run: |
@@ -227,9 +255,9 @@ jobs:
echo "=== All images pushed successfully ==="
deploy:
runs-on: [self-hosted, bazel, halfdan]
runs-on: ubuntu-latest
needs: [build-all]
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
if: needs.build-all.outputs.skip_build != 'true' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true'))
environment: production
env:
EAGLE_IMAGE: ${{ needs.build-all.outputs.eagle_image_tag }}
@@ -240,6 +268,7 @@ jobs:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GPT_MODEL_NAME: ${{ secrets.GPT_MODEL_NAME }}
EAGLE_ENABLE_S3: ${{ secrets.EAGLE_ENABLE_S3 }}
DO_SPACES_ENDPOINT: ${{ secrets.DO_SPACES_ENDPOINT }}
DO_SPACES_ACCESS_KEY: ${{ secrets.DO_SPACES_ACCESS_KEY }}
DO_SPACES_SECRET_KEY: ${{ secrets.DO_SPACES_SECRET_KEY }}
# Admin server uses different credentials (for eagle0-assets bucket)
@@ -264,6 +293,12 @@ jobs:
FASTMAIL_API_TOKEN: ${{ secrets.FASTMAIL_API_TOKEN }}
FASTMAIL_FROM_EMAIL: ${{ secrets.FASTMAIL_FROM_EMAIL }}
FASTMAIL_FROM_NAME: ${{ secrets.FASTMAIL_FROM_NAME }}
EAGLE_HISTORY_BACKEND: ${{ secrets.EAGLE_HISTORY_BACKEND }}
EAGLE_POSTGRES_HOST: ${{ secrets.EAGLE_POSTGRES_HOST }}
EAGLE_POSTGRES_PORT: ${{ secrets.EAGLE_POSTGRES_PORT }}
EAGLE_POSTGRES_DATABASE: ${{ secrets.EAGLE_POSTGRES_DATABASE }}
EAGLE_POSTGRES_USER: ${{ secrets.EAGLE_POSTGRES_USER }}
EAGLE_POSTGRES_PASSWORD: ${{ secrets.EAGLE_POSTGRES_PASSWORD }}
DO_DROPLET_IP: ${{ secrets.DO_DROPLET_IP }}
DO_REGISTRY_TOKEN: ${{ secrets.DO_REGISTRY_TOKEN }}
NOTIFY_SECRET: ${{ secrets.EAGLE_NOTIFY_SECRET }}
@@ -360,6 +395,10 @@ jobs:
validate_env "EAGLE_IMAGE" "${EAGLE_IMAGE}" "" || VALIDATION_FAILED=1
validate_env "JWT_PRIVATE_KEY" "${JWT_PRIVATE_KEY}" "" || VALIDATION_FAILED=1
validate_env "DO_REGISTRY_TOKEN" "${DO_REGISTRY_TOKEN}" "" || VALIDATION_FAILED=1
validate_env "EAGLE_POSTGRES_HOST" "${EAGLE_POSTGRES_HOST}" "" || VALIDATION_FAILED=1
validate_env "EAGLE_POSTGRES_DATABASE" "${EAGLE_POSTGRES_DATABASE}" "" || VALIDATION_FAILED=1
validate_env "EAGLE_POSTGRES_USER" "${EAGLE_POSTGRES_USER}" "" || VALIDATION_FAILED=1
validate_env "EAGLE_POSTGRES_PASSWORD" "${EAGLE_POSTGRES_PASSWORD}" "" || VALIDATION_FAILED=1
if [ "\${VALIDATION_FAILED}" -eq 1 ]; then
echo ""
@@ -407,6 +446,12 @@ jobs:
export FASTMAIL_API_TOKEN="${FASTMAIL_API_TOKEN}"
export FASTMAIL_FROM_EMAIL="${FASTMAIL_FROM_EMAIL}"
export FASTMAIL_FROM_NAME="${FASTMAIL_FROM_NAME}"
export EAGLE_HISTORY_BACKEND="${EAGLE_HISTORY_BACKEND:-postgres}"
export EAGLE_POSTGRES_HOST="${EAGLE_POSTGRES_HOST}"
export EAGLE_POSTGRES_PORT="${EAGLE_POSTGRES_PORT}"
export EAGLE_POSTGRES_DATABASE="${EAGLE_POSTGRES_DATABASE}"
export EAGLE_POSTGRES_USER="${EAGLE_POSTGRES_USER}"
export EAGLE_POSTGRES_PASSWORD="${EAGLE_POSTGRES_PASSWORD}"
export DO_REGISTRY_TOKEN="${DO_REGISTRY_TOKEN}"
export NOTIFY_SECRET="${NOTIFY_SECRET}"
export GITHUB_TOKEN="${GITHUB_TOKEN_FOR_ADMIN}"
@@ -516,7 +561,7 @@ jobs:
cleanup:
needs: [build-all, deploy]
if: always()
if: always() && github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Delete warmup-binary artifact
-54
View File
@@ -1,54 +0,0 @@
name: Eagle Build
on:
# Main pushes are covered by docker_build.yml which builds the same target
pull_request:
paths:
- 'src/main/scala/**'
- 'src/main/protobuf/net/eagle0/eagle/**'
- 'src/main/protobuf/net/eagle0/common/**'
- 'WORKSPACE'
- 'MODULE.bazel'
- 'BUILD.bazel'
- '.bazelrc'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/workflows/eagle_build.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
runs-on: [self-hosted, bazel]
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
- name: Build Eagle server
run: bazel build //src/main/scala/net/eagle0/eagle:eagle_server
+6 -2
View File
@@ -5,12 +5,16 @@ on:
branches: [ "main" ]
paths:
- ".github/workflows/installer_build.yml"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".bazelrc"
- "src/main/go/net/eagle0/clients/win/installer/**"
pull_request:
paths:
- ".github/workflows/installer_build.yml"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".bazelrc"
- "src/main/go/net/eagle0/clients/win/installer/**"
workflow_dispatch:
@@ -46,8 +50,8 @@ jobs:
lfs: false
clean: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
- name: Setup Bazel
uses: ./.github/actions/setup-bazel
- name: Build Go installer for Windows
env:
+30 -15
View File
@@ -31,7 +31,7 @@ jobs:
steps:
- name: Check for relevant changes since last successful run
id: check
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
if (context.eventName === 'workflow_dispatch') {
@@ -74,6 +74,7 @@ jobs:
'ci/', // CI scripts & workflows
'.github/workflows/ios_testflight.yml',
'MODULE.bazel',
'MODULE.bazel.lock',
'.bazelrc',
];
@@ -108,7 +109,7 @@ jobs:
build-unity:
needs: check-changes
if: needs.check-changes.outputs.should_build == 'true'
runs-on: [self-hosted, macOS, testflight, halfdan]
runs-on: [self-hosted, macOS, testflight]
steps:
- name: Prune stale PR refs
@@ -165,21 +166,23 @@ jobs:
./ci/github_actions/build_unity_ios.sh "$EAGLE0_BUILD_DIR/eagle0iOS"
- name: Upload Addressables to CDN
if: success()
if: success() && github.event.inputs.skip_upload != 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: ./ci/github_actions/upload_addressables.sh iOS
- name: Purge CDN cache for iOS addressables
if: success()
if: success() && github.event.inputs.skip_upload != 'true'
env:
DO_CDN_PAT: ${{ secrets.DO_CDN_PAT }}
run: |
UNITY_VERSION=$(grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //')
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
curl -s -X DELETE "https://api.digitalocean.com/v2/cdn/endpoints/8c98df29-6f0c-4704-8e82-ca40a2b81aa8/cache" \
-H "Authorization: Bearer $DO_CDN_PAT" \
-H "Content-Type: application/json" \
-d '{"files": ["addressables/iOS/*"]}' \
-d "{\"files\": [\"addressables/iOS/$UNITY_MAJOR_MINOR/*\"]}" \
--fail || echo "Warning: CDN purge failed (non-fatal)"
- name: Archive Build Log
@@ -209,7 +212,7 @@ jobs:
archive-and-upload:
needs: build-unity
runs-on: [self-hosted, macOS, testflight, halfdan]
runs-on: [self-hosted, macOS, testflight]
steps:
- name: Ensure Git LFS available for checkout
@@ -248,16 +251,28 @@ jobs:
tar -xzf "$RUNNER_TEMP/eagle0-ios-project.tar.gz" -C "$EAGLE0_BUILD_DIR"
- name: Delete generated iOS project artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
artifact_id=$(gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate \
--jq '.artifacts[] | select(.name == "eagle0-ios-project-${{ github.run_id }}") | .id')
uses: actions/github-script@v9
with:
script: |
const artifactName = 'eagle0-ios-project-${{ github.run_id }}';
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
if [ -n "$artifact_id" ]; then
gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/$artifact_id"
fi
const artifact = artifacts.find(a => a.name === artifactName);
if (artifact === undefined) {
console.log(`Artifact not found: ${artifactName}`);
return;
}
await github.rest.actions.deleteArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
});
console.log(`Deleted artifact: ${artifactName} (${artifact.id})`);
- name: Install Signing Certificate
env:
+233 -156
View File
@@ -14,6 +14,7 @@ on:
- "src/main/go/net/eagle0/build/mac_build_handler/**"
- "scripts/build_protos.sh"
- "scripts/build_mac_plugin.sh"
- "scripts/build_sparkle_plugin.sh"
- "scripts/inject_sparkle.sh"
- "scripts/codesign_mac_app.sh"
- "scripts/notarize_submit.sh"
@@ -21,10 +22,14 @@ on:
- "ci/github_actions/build_mac.sh"
- "ci/github_actions/build_unity_mac.sh"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/detect_addressables_changes.sh"
- "ci/github_actions/upload_addressables.sh"
- "ci/github_actions/ensure_unity_installed.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
- ".bazelrc"
- "ci/mac/**"
- "src/main/objc/net/eagle0/clients/unity/sparkle/**"
pull_request:
# On PRs, only build Mac when Mac-specific files change. Shared C#/proto
# changes are covered by the Windows build — if it passes, Mac will too.
@@ -33,6 +38,11 @@ on:
- "src/main/go/net/eagle0/build/mac_build_handler/**"
- "scripts/build_mac_plugin.sh"
- "scripts/build_sparkle_plugin.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Editor/BuildScript.cs"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Eagle/SparkleInitializer.cs"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Eagle/SparkleUpdater.cs"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/common/UpdateNotification/UpdateNotificationManager.cs"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/common/WindowFocusManager.cs"
- "src/main/objc/net/eagle0/clients/unity/sparkle/**"
- "scripts/inject_sparkle.sh"
- "scripts/codesign_mac_app.sh"
@@ -40,7 +50,13 @@ on:
- "scripts/notarize_wait.sh"
- "ci/github_actions/build_mac.sh"
- "ci/github_actions/build_unity_mac.sh"
- "ci/github_actions/ensure_unity_installed.sh"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/detect_addressables_changes.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectSettings.asset"
- ".bazelrc"
- "ci/mac/**"
workflow_dispatch:
inputs:
@@ -50,10 +66,6 @@ on:
default: 'false'
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
actions: write # Required to delete artifacts after deploy
@@ -65,11 +77,14 @@ env:
KEYCHAIN_NAME: build-${{ github.run_id }}.keychain
jobs:
build-and-sign:
runs-on: [self-hosted, macOS, unity-mac, halfdan]
build-mac:
runs-on: [self-hosted, macOS, unity-mac]
concurrency:
group: ${{ github.workflow }}-build-${{ github.head_ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
outputs:
submission_id: ${{ steps.notarize-submit.outputs.submission_id }}
should_deploy: ${{ steps.check-deploy.outputs.should_deploy }}
addressables_changed: ${{ steps.addressables.outputs.changed }}
steps:
- name: Prune stale PR refs
@@ -110,11 +125,25 @@ jobs:
- name: Clean stale files
run: |
git clean -ffd
LIBRARY_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library"
VERSION_CACHE="$LIBRARY_DIR/.last_unity_version"
PROJECT_VERSION_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
CURRENT_VERSION=$(grep "m_EditorVersion:" "$PROJECT_VERSION_FILE" | head -1 | sed 's/m_EditorVersion: //')
# Nuke Library/ when Unity version changes to avoid import loops
if [ -f "$VERSION_CACHE" ]; then
CACHED_VERSION=$(cat "$VERSION_CACHE")
if [ "$CACHED_VERSION" != "$CURRENT_VERSION" ]; then
echo "Unity version changed ($CACHED_VERSION -> $CURRENT_VERSION) -- clearing Library/"
rm -rf "$LIBRARY_DIR"
fi
fi
# Only clear Bee/ when C# files were added/deleted/renamed (structural
# changes that stale the DAG). Content-only modifications are handled by
# Bee's incremental compilation. See persist_library.sh for background.
BEE_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library/Bee"
SHA_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_built_sha"
BEE_DIR="$LIBRARY_DIR/Bee"
SHA_FILE="$LIBRARY_DIR/.last_built_sha"
if [ -f "$SHA_FILE" ] && [ -d "$BEE_DIR" ]; then
LAST_SHA=$(cat "$SHA_FILE")
if git diff --diff-filter=ADR --name-only "$LAST_SHA" HEAD -- '*.cs' '*.csproj' '*.asmdef' 2>/dev/null | grep -q .; then
@@ -136,14 +165,34 @@ jobs:
- name: Ensure Unity version installed
run: ./ci/github_actions/ensure_unity_installed.sh mac
- name: Ensure .NET SDK installed
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
uses: ./.github/actions/setup-bazel
- name: Sync Bazel Xcode config
run: ./scripts/sync_bazel_xcode.sh
- name: Detect Addressables changes
id: addressables
if: success()
shell: bash
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
./ci/github_actions/detect_addressables_changes.sh "$BASE_SHA" "${{ github.sha }}"
- name: Build Mac Unity
run: ./ci/github_actions/build_unity_mac.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC"
run: ./ci/github_actions/build_unity_mac.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC" "${{ steps.addressables.outputs.changed }}" "${{ steps.addressables.outputs.changed }}"
- name: Save build SHA for Bee/ cache invalidation
if: success()
@@ -151,20 +200,11 @@ jobs:
mkdir -p src/main/csharp/net/eagle0/clients/unity/eagle0/Library
git rev-parse HEAD > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_built_sha
- name: Upload Addressables to CDN
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: ./ci/github_actions/upload_addressables.sh StandaloneOSX
- name: Inject Sparkle Framework
- name: Save Unity version for Library/ cache invalidation
if: success()
env:
SPARKLE_EDDSA_PUBLIC_KEY: ${{ secrets.SPARKLE_EDDSA_PUBLIC_KEY }}
run: |
chmod +x ./scripts/inject_sparkle.sh
./scripts/inject_sparkle.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app"
mkdir -p src/main/csharp/net/eagle0/clients/unity/eagle0/Library
grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //' > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_unity_version
- name: Check if should deploy
id: check-deploy
@@ -179,8 +219,146 @@ jobs:
echo "should_deploy=false" >> $GITHUB_OUTPUT
fi
- name: Import Code Signing Certificate
- name: Zip unsigned app for deploy
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
ditto -c -k --keepParent eagle0.app eagle0.app.zip
- name: Upload unsigned app
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
uses: actions/upload-artifact@v7
with:
name: unsigned-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app.zip
retention-days: 1
- name: Upload Mac Addressables for deploy
if: success() && steps.check-deploy.outputs.should_deploy == 'true' && steps.addressables.outputs.changed == 'true'
uses: actions/upload-artifact@v7
with:
name: mac-addressables-${{ github.run_id }}
path: src/main/csharp/net/eagle0/clients/unity/eagle0/ServerData/StandaloneOSX
if-no-files-found: ignore
retention-days: 1
- name: Archive Build Log
if: success() || failure()
uses: actions/upload-artifact@v7
with:
name: editor_mac.log
path: ${{ env.EAGLE0_BUILD_DIR }}/editor_mac.log
retention-days: 3
- name: Archive Addressables build reports
if: (success() || failure()) && steps.addressables.outputs.changed == 'true'
uses: actions/upload-artifact@v7
with:
name: mac-addressables-build-reports
path: src/main/csharp/net/eagle0/clients/unity/eagle0/Library/com.unity.addressables/BuildReports
if-no-files-found: ignore
retention-days: 3
- name: Cleanup build directory
if: always()
run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"
deploy-mac:
needs: build-mac
if: needs.build-mac.outputs.should_deploy == 'true'
runs-on: [self-hosted, macOS, notarize]
outputs:
deployed_version: ${{ steps.deploy-mac.outputs.deployed_version }}
deployed_unity_major_minor: ${{ steps.deploy-mac.outputs.deployed_unity_major_minor }}
concurrency:
group: mac-deploy-${{ github.ref }}
cancel-in-progress: false
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
for path in /opt/homebrew/bin /usr/local/bin; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
fi
done
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git-lfs --version
- uses: actions/checkout@v6
env:
GIT_LFS_SKIP_SMUDGE: 1
with:
persist-credentials: false
fetch-depth: 0 # For version numbering
- name: Check if run is still latest main
id: check-latest
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
LATEST_SHA=$(curl -fsSL \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/commits/main" \
| python3 -c 'import json, sys; print(json.load(sys.stdin)["sha"])')
if [[ "$LATEST_SHA" != "${{ github.sha }}" ]]; then
echo "Skipping deploy because ${{ github.sha }} is no longer latest main ($LATEST_SHA is latest)"
echo "should_deploy=false" >> "$GITHUB_OUTPUT"
exit 0
fi
fi
echo "should_deploy=true" >> "$GITHUB_OUTPUT"
- name: Clean download directory
if: steps.check-latest.outputs.should_deploy == 'true'
run: rm -rf ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Download unsigned app
if: steps.check-latest.outputs.should_deploy == 'true'
uses: actions/download-artifact@v8
with:
name: unsigned-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Unzip unsigned app
if: steps.check-latest.outputs.should_deploy == 'true'
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
ditto -x -k eagle0.app.zip .
rm eagle0.app.zip
ls -la ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app/
- name: Clean Addressables directory
if: steps.check-latest.outputs.should_deploy == 'true' && needs.build-mac.outputs.addressables_changed == 'true'
run: rm -rf src/main/csharp/net/eagle0/clients/unity/eagle0/ServerData/StandaloneOSX
- name: Download Mac Addressables
if: steps.check-latest.outputs.should_deploy == 'true' && needs.build-mac.outputs.addressables_changed == 'true'
uses: actions/download-artifact@v8
continue-on-error: true
with:
name: mac-addressables-${{ github.run_id }}
path: src/main/csharp/net/eagle0/clients/unity/eagle0/ServerData/StandaloneOSX
- name: Upload Addressables to CDN
if: steps.check-latest.outputs.should_deploy == 'true' && needs.build-mac.outputs.addressables_changed == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: ./ci/github_actions/upload_addressables.sh StandaloneOSX
- name: Inject Sparkle Framework
if: steps.check-latest.outputs.should_deploy == 'true'
env:
SPARKLE_EDDSA_PUBLIC_KEY: ${{ secrets.SPARKLE_EDDSA_PUBLIC_KEY }}
run: |
chmod +x ./scripts/inject_sparkle.sh
./scripts/inject_sparkle.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app"
- name: Import Code Signing Certificate
if: steps.check-latest.outputs.should_deploy == 'true'
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
@@ -223,7 +401,7 @@ jobs:
rm certificate.p12
- name: Code Sign App
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
if: steps.check-latest.outputs.should_deploy == 'true'
env:
SIGNING_IDENTITY: ${{ secrets.SIGNING_IDENTITY }}
run: |
@@ -232,7 +410,7 @@ jobs:
- name: Submit for Notarization
id: notarize-submit
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
if: steps.check-latest.outputs.should_deploy == 'true'
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
@@ -241,156 +419,47 @@ jobs:
chmod +x ./scripts/notarize_submit.sh
./scripts/notarize_submit.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app" >> $GITHUB_OUTPUT
- name: Cleanup Keychain
if: always()
run: |
security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
- name: Zip signed app for artifact
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
ditto -c -k --keepParent eagle0.app eagle0.app.zip
- name: Upload signed app
if: success() && steps.check-deploy.outputs.should_deploy == 'true'
uses: actions/upload-artifact@v7
with:
name: signed-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app.zip
retention-days: 1
- name: Archive Build Log
if: success() || failure()
uses: actions/upload-artifact@v7
with:
name: editor_mac.log
path: ${{ env.EAGLE0_BUILD_DIR }}/editor_mac.log
retention-days: 3
- name: Cleanup build directory
if: always()
run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"
wait-notarization:
needs: build-and-sign
if: needs.build-and-sign.outputs.should_deploy == 'true'
runs-on: [self-hosted, macOS, notarize, halfdan]
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
for path in /opt/homebrew/bin /usr/local/bin; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
fi
done
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git-lfs --version
- uses: actions/checkout@v6
env:
GIT_LFS_SKIP_SMUDGE: 1
with:
persist-credentials: false
sparse-checkout: scripts
- name: Clean download directory
run: rm -rf ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Download signed app
uses: actions/download-artifact@v8
with:
name: signed-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Unzip signed app
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
ditto -x -k eagle0.app.zip .
rm eagle0.app.zip
ls -la ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app/
- name: Wait for Notarization and Staple
if: steps.check-latest.outputs.should_deploy == 'true'
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
chmod +x ./scripts/notarize_wait.sh
./scripts/notarize_wait.sh "${{ needs.build-and-sign.outputs.submission_id }}" "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app"
./scripts/notarize_wait.sh "${{ steps.notarize-submit.outputs.submission_id }}" "${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app"
- name: Cleanup Keychain
if: always()
run: |
security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
- name: Zip notarized app for artifact
if: steps.check-latest.outputs.should_deploy == 'true'
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
rm -f eagle0.app.zip
ditto -c -k --keepParent eagle0.app eagle0.app.zip
- name: Upload notarized app
if: steps.check-latest.outputs.should_deploy == 'true'
uses: actions/upload-artifact@v7
with:
name: notarized-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC/eagle0.app.zip
retention-days: 1
- name: Cleanup build directory
if: always()
run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"
deploy:
needs: [build-and-sign, wait-notarization]
if: needs.build-and-sign.outputs.should_deploy == 'true'
runs-on: [self-hosted, macOS, notarize, halfdan]
outputs:
deployed_version: ${{ steps.deploy-mac.outputs.deployed_version }}
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
for path in /opt/homebrew/bin /usr/local/bin; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
fi
done
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git-lfs --version
- uses: actions/checkout@v6
env:
GIT_LFS_SKIP_SMUDGE: 1
with:
persist-credentials: false
fetch-depth: 0 # For version numbering
- name: Clean download directory
run: rm -rf ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Download notarized app
uses: actions/download-artifact@v8
with:
name: notarized-mac-app-${{ github.run_id }}
path: ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
- name: Unzip notarized app
run: |
cd ${{ env.EAGLE0_BUILD_DIR }}/eagle0MAC
ditto -x -k eagle0.app.zip .
rm eagle0.app.zip
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
if: steps.check-latest.outputs.should_deploy == 'true'
uses: ./.github/actions/setup-bazel
- name: Sync Bazel Xcode config
if: steps.check-latest.outputs.should_deploy == 'true'
run: ./scripts/sync_bazel_xcode.sh
- name: Deploy Mac Build
id: deploy-mac
if: steps.check-latest.outputs.should_deploy == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
@@ -401,8 +470,13 @@ jobs:
echo "$SPARKLE_EDDSA_PRIVATE_KEY" > "$SPARKLE_PRIVATE_KEY_PATH"
chmod 600 "$SPARKLE_PRIVATE_KEY_PATH"
# Install dmgbuild (creates .DS_Store programmatically, no AppleScript needed)
pip3 install dmgbuild
# Install dmgbuild in an isolated venv. Homebrew Python rejects system
# installs under PEP 668, and mac_build_handler shells out to dmgbuild.
DMGBUILD_VENV="${{ env.EAGLE0_BUILD_DIR }}/dmgbuild-venv"
python3 -m venv "$DMGBUILD_VENV"
"$DMGBUILD_VENV/bin/python" -m pip install --upgrade pip
"$DMGBUILD_VENV/bin/python" -m pip install dmgbuild
export PATH="$DMGBUILD_VENV/bin:$PATH"
# Background image for styled DMG
BACKGROUND_PATH="$(pwd)/ci/mac/dmg/background.png"
@@ -422,6 +496,9 @@ jobs:
rm "$SPARKLE_PRIVATE_KEY_PATH"
echo "deployed_version=$VERSION" >> $GITHUB_OUTPUT
UNITY_VERSION=$(grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //')
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
echo "deployed_unity_major_minor=$UNITY_MAJOR_MINOR" >> $GITHUB_OUTPUT
# Artifact cleanup is handled by the cleanup job on ubuntu-latest
- name: Cleanup build directory
@@ -429,8 +506,8 @@ jobs:
run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"
notify-mac:
needs: deploy
if: needs.deploy.outputs.deployed_version != ''
needs: deploy-mac
if: needs.deploy-mac.outputs.deployed_version != ''
runs-on: ubuntu-latest
steps:
- name: Purge CDN cache and notify clients
@@ -442,16 +519,16 @@ jobs:
curl -s -X DELETE "https://api.digitalocean.com/v2/cdn/endpoints/8c98df29-6f0c-4704-8e82-ca40a2b81aa8/cache" \
-H "Authorization: Bearer $DO_CDN_PAT" \
-H "Content-Type: application/json" \
-d '{"files": ["mac/*", "addressables/StandaloneOSX/*"]}' \
-d '{"files": ["mac/*", "addressables/StandaloneOSX/${{ needs.deploy-mac.outputs.deployed_unity_major_minor }}/*"]}' \
--fail || echo "Warning: CDN purge failed (non-fatal)"
curl -X POST "https://admin.eagle0.net/notify-update?platform=mac&version=${{ needs.deploy.outputs.deployed_version }}&required=false" \
curl -X POST "https://admin.eagle0.net/notify-update?platform=mac&version=${{ needs.deploy-mac.outputs.deployed_version }}&required=false" \
-H "X-Notify-Secret: $NOTIFY_SECRET" \
--fail --silent --show-error || echo "Warning: Failed to notify clients (non-fatal)"
# Cleanup job runs regardless of success/failure to prevent artifact accumulation
cleanup:
needs: [build-and-sign, wait-notarization, deploy, notify-mac]
needs: [build-mac, deploy-mac, notify-mac]
if: always()
runs-on: ubuntu-latest
@@ -461,7 +538,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
# Delete this run's artifacts (names include run ID to avoid conflicts)
for artifact_name in signed-mac-app-${{ github.run_id }} notarized-mac-app-${{ github.run_id }}; do
for artifact_name in unsigned-mac-app-${{ github.run_id }} notarized-mac-app-${{ github.run_id }} mac-addressables-${{ github.run_id }}; do
echo "Deleting artifact: $artifact_name"
artifact_id=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" \
-q ".artifacts[] | select(.name == \"$artifact_name\") | .id")
-107
View File
@@ -1,107 +0,0 @@
name: Cleanup Old Container Images
on:
schedule:
# Run daily at 3am UTC
- cron: '0 3 * * *'
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (show what would be deleted without deleting)'
required: true
default: 'true'
type: boolean
permissions:
contents: read
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DO_REGISTRY_TOKEN }}
- name: Cleanup old images
env:
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true' }}
run: |
set -e
RETENTION_DAYS=5
CUTOFF_DATE=$(date -d "-${RETENTION_DAYS} days" +%s)
REGISTRY="eagle0"
echo "Cleaning up images older than ${RETENTION_DAYS} days"
echo "Cutoff date: $(date -d "@${CUTOFF_DATE}" -Iseconds)"
echo "Dry run: ${DRY_RUN}"
echo ""
# List repositories via JSON. Text output can wrap table rows and make
# headers/tags/digests look like repository names.
REPOS=$(doctl registry repository list-v2 "${REGISTRY}" --output json | jq -r '.[] | .name // .Name // empty')
for REPO in $REPOS; do
echo "=== Processing repository: ${REPO} ==="
# Get all manifests with their tags and dates using JSON output for reliable parsing
MANIFESTS_JSON=$(doctl registry repository list-manifests "${REPO}" --output json 2>/dev/null || echo "[]")
if [ "$MANIFESTS_JSON" = "[]" ] || [ -z "$MANIFESTS_JSON" ]; then
echo " No manifests found"
continue
fi
# Parse JSON and process each manifest
echo "$MANIFESTS_JSON" | jq -r '.[] | "\(.digest) \(.updated_at) \(.tags // [] | join(","))"' | while read -r DIGEST UPDATED_AT TAGS; do
# Skip if no digest or if it doesn't look like a valid digest (sha256:...)
if [ -z "$DIGEST" ] || ! echo "$DIGEST" | grep -q '^sha256:'; then
continue
fi
# Parse the date (ISO 8601 format from JSON)
MANIFEST_DATE=$(date -d "$UPDATED_AT" +%s 2>/dev/null || echo "0")
# Skip protected tags (latest, arm64-latest)
if echo ",$TAGS," | grep -qE ',(latest|arm64-latest),'; then
echo " KEEP: ${DIGEST:0:20}... (protected tag: $TAGS)"
continue
fi
# Check if older than cutoff
if [ "$MANIFEST_DATE" -lt "$CUTOFF_DATE" ]; then
echo " DELETE: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
if [ "$DRY_RUN" != "true" ]; then
doctl registry repository delete-manifest "${REPO}" "$DIGEST" --force
fi
else
echo " KEEP: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
fi
done
echo ""
done
- name: Run garbage collection
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
run: |
echo "Starting garbage collection..."
set +e
GC_OUTPUT=$(doctl registry garbage-collection start --force 2>&1)
GC_STATUS=$?
set -e
echo "$GC_OUTPUT"
if [ "$GC_STATUS" -eq 0 ]; then
echo "Garbage collection started. It may take a few minutes to complete."
exit 0
fi
if echo "$GC_OUTPUT" | grep -q "automated garbage collection is enabled"; then
echo "Automated garbage collection is enabled for this registry; skipping manual garbage collection."
exit 0
fi
exit "$GC_STATUS"
@@ -0,0 +1,219 @@
name: Renovate Dependency Artifacts
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- 'maven_install.json'
- 'renovate.json'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- '.github/workflows/renovate_dependency_artifacts.yml'
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- 'maven_install.json'
- 'go.mod'
- 'go.sum'
- 'renovate.json'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- '.github/workflows/renovate_dependency_artifacts.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.head.ref }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: read
jobs:
changes:
runs-on: ubuntu-latest
outputs:
bazel: ${{ steps.changed-files.outputs.bazel }}
go: ${{ steps.changed-files.outputs.go }}
update_matrix: ${{ steps.changed-files.outputs.update_matrix }}
steps:
- name: Detect changed dependency files
id: changed-files
uses: actions/github-script@v9
with:
script: |
const files = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
},
);
const changed = files.map((file) => file.filename);
const bazelPatterns = [
/^MODULE\.bazel$/,
/^MODULE\.bazel\.lock$/,
/^maven_install\.json$/,
/^renovate\.json$/,
/^ci\/github_actions\/ensure_bazel_installed\.sh$/,
/^\.github\/actions\/setup-bazel\//,
/^\.github\/workflows\/renovate_dependency_artifacts\.yml$/,
];
const goPatterns = [
/^go\.mod$/,
/^go\.sum$/,
/^renovate\.json$/,
/^\.github\/workflows\/renovate_dependency_artifacts\.yml$/,
];
core.setOutput(
'bazel',
changed.some((file) => bazelPatterns.some((pattern) => pattern.test(file))),
);
core.setOutput(
'go',
changed.some((file) => goPatterns.some((pattern) => pattern.test(file))),
);
const include = [];
if (changed.some((file) => bazelPatterns.some((pattern) => pattern.test(file)))) {
include.push({
artifact: 'bazel',
name: 'Bazel lockfiles',
changed_files: 'MODULE.bazel.lock maven_install.json',
commit_message: 'Update generated dependency lockfiles',
});
}
if (changed.some((file) => goPatterns.some((pattern) => pattern.test(file)))) {
include.push({
artifact: 'go',
name: 'Go artifacts',
changed_files: 'go.sum',
commit_message: 'Update generated Go artifacts',
});
}
core.setOutput('update_matrix', JSON.stringify({include}));
check-lockfile:
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.user.login != 'renovate[bot]'
runs-on: [self-hosted, bazel]
permissions:
contents: read
steps:
- name: Checkout PR
uses: actions/checkout@v6
with:
lfs: false
- name: Ensure Bazel installed
uses: ./.github/actions/setup-bazel
- name: Update Maven lockfile
env:
REPIN: "1"
run: bazel run @maven//:pin
- name: Refresh Bazel lockfile
run: bazel build --nobuild //src/main/scala/net/eagle0/eagle:eagle_server //src/main/cpp/net/eagle0/shardok:shardok-server
- name: Verify generated lockfiles are current
run: |
if git diff --quiet -- MODULE.bazel.lock maven_install.json; then
echo "Generated dependency lockfiles are current"
exit 0
fi
echo "Generated dependency lockfiles are stale. Run the lockfile update commands and commit the result:"
echo " REPIN=1 bazel run @maven//:pin"
echo " bazel build --nobuild //src/main/scala/net/eagle0/eagle:eagle_server //src/main/cpp/net/eagle0/shardok:shardok-server"
git diff -- MODULE.bazel.lock maven_install.json
exit 1
update-artifacts:
name: Update ${{ matrix.name }}
needs: changes
if: >-
github.event_name == 'pull_request_target' &&
github.event.pull_request.user.login == 'renovate[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository &&
startsWith(github.event.pull_request.head.ref, 'renovate/') &&
needs.changes.outputs.update_matrix != '{"include":[]}'
runs-on: [self-hosted, bazel]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.changes.outputs.update_matrix) }}
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout Renovate branch
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
token: ${{ secrets.RENOVATE_LOCKFILE_TOKEN || github.token }}
lfs: false
- name: Ensure Bazel installed
if: matrix.artifact == 'bazel'
uses: ./.github/actions/setup-bazel
- name: Update Maven lockfile
if: matrix.artifact == 'bazel'
env:
REPIN: "1"
run: bazel run @maven//:pin
- name: Refresh Bazel lockfile
if: matrix.artifact == 'bazel'
run: bazel build --nobuild //src/main/scala/net/eagle0/eagle:eagle_server //src/main/cpp/net/eagle0/shardok:shardok-server
- name: Set up Go
if: matrix.artifact == 'go'
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: false
- name: Update Go module sums
if: matrix.artifact == 'go'
run: go mod download all
- name: Commit generated artifact updates
run: |
if git diff --quiet -- ${{ matrix.changed_files }}; then
echo "${{ matrix.name }} are already up to date"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add ${{ matrix.changed_files }}
git commit -m "${{ matrix.commit_message }}"
git push origin "HEAD:${{ github.event.pull_request.head.ref }}"
+189
View File
@@ -0,0 +1,189 @@
name: Repository Cleanup
on:
schedule:
# Registry cleanup: run daily at 03:00 UTC
- cron: '0 3 * * *'
# Artifact cleanup and storage check: run every 6 hours
- cron: '0 */6 * * *'
workflow_dispatch:
inputs:
target:
description: 'Cleanup target'
required: true
default: 'all'
type: choice
options:
- all
- artifacts
- registry
registry_dry_run:
description: 'Dry run registry cleanup'
required: true
default: 'true'
type: boolean
permissions:
contents: read
actions: write
jobs:
cleanup-and-check-artifacts:
if: >-
github.event_name == 'workflow_dispatch' &&
(github.event.inputs.target == 'all' || github.event.inputs.target == 'artifacts') ||
github.event_name == 'schedule' &&
github.event.schedule == '0 */6 * * *'
runs-on: ubuntu-latest
steps:
- name: Delete expired artifacts and artifacts older than 3 days
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Fetching all artifacts..."
gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | "\(.id)\t\(.created_at)\t\(.expired)\t\(.name)"' > /tmp/all_artifacts.txt
total=$(wc -l < /tmp/all_artifacts.txt)
echo "Found $total total artifacts"
cutoff=$(date -u -d '3 days ago' '+%Y-%m-%dT%H:%M:%SZ')
echo "Deleting expired artifacts and artifacts created before $cutoff"
deleted=0
while IFS=$'\t' read -r id created_at expired name; do
if [[ "$expired" == "true" || "$created_at" < "$cutoff" ]]; then
gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/$id" 2>/dev/null && deleted=$((deleted + 1))
if [ $((deleted % 50)) -eq 0 ]; then
echo "Deleted $deleted artifacts so far..."
fi
fi
done < /tmp/all_artifacts.txt
echo "Cleanup complete. Deleted $deleted artifacts out of $total total."
rm -f /tmp/all_artifacts.txt
- name: Check artifact storage size
env:
GH_TOKEN: ${{ github.token }}
run: |
# Calculate active artifact storage. The artifacts API can list expired
# artifacts until they are explicitly deleted, so do not count them.
total_bytes=$(gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | select(.expired == false) | .size_in_bytes' | awk '{sum+=$1} END {print sum}')
total_mb=$(( ${total_bytes:-0} / 1024 / 1024 ))
echo "Total artifact storage: ${total_mb} MB"
# Fail if over 500MB
if [ "$total_mb" -gt 500 ]; then
echo "::error::Artifact storage is ${total_mb} MB, which exceeds the 500 MB threshold!"
echo ""
echo "Largest artifacts:"
# Save to temp file to avoid SIGPIPE/broken pipe errors with head
gh api "repos/${{ github.repository }}/actions/artifacts" \
--paginate -q '.artifacts[] | select(.expired == false) | "\(.size_in_bytes)\t\(.name)\t\(.created_at)"' > /tmp/artifacts.txt
sort -rn /tmp/artifacts.txt | head -20 | \
awk -F'\t' '{printf "%d MB\t%s\t%s\n", $1/1024/1024, $2, $3}'
rm -f /tmp/artifacts.txt
exit 1
fi
echo "Storage is within acceptable limits."
cleanup-registry:
if: >-
github.event_name == 'workflow_dispatch' &&
(github.event.inputs.target == 'all' || github.event.inputs.target == 'registry') ||
github.event_name == 'schedule' &&
github.event.schedule == '0 3 * * *'
runs-on: ubuntu-latest
steps:
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DO_REGISTRY_TOKEN }}
- name: Cleanup old images
env:
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.registry_dry_run == 'true' }}
run: |
set -e
RETENTION_DAYS=5
CUTOFF_DATE=$(date -d "-${RETENTION_DAYS} days" +%s)
REGISTRY="eagle0"
echo "Cleaning up images older than ${RETENTION_DAYS} days"
echo "Cutoff date: $(date -d "@${CUTOFF_DATE}" -Iseconds)"
echo "Dry run: ${DRY_RUN}"
echo ""
# List repositories via JSON. Text output can wrap table rows and make
# headers/tags/digests look like repository names.
REPOS=$(doctl registry repository list-v2 "${REGISTRY}" --output json | jq -r '.[] | .name // .Name // empty')
for REPO in $REPOS; do
echo "=== Processing repository: ${REPO} ==="
# Get all manifests with their tags and dates using JSON output for reliable parsing
MANIFESTS_JSON=$(doctl registry repository list-manifests "${REPO}" --output json 2>/dev/null || echo "[]")
if [ "$MANIFESTS_JSON" = "[]" ] || [ -z "$MANIFESTS_JSON" ]; then
echo " No manifests found"
continue
fi
# Parse JSON and process each manifest
echo "$MANIFESTS_JSON" | jq -r '.[] | "\(.digest) \(.updated_at) \(.tags // [] | join(","))"' | while read -r DIGEST UPDATED_AT TAGS; do
# Skip if no digest or if it doesn't look like a valid digest (sha256:...)
if [ -z "$DIGEST" ] || ! echo "$DIGEST" | grep -q '^sha256:'; then
continue
fi
# Parse the date (ISO 8601 format from JSON)
MANIFEST_DATE=$(date -d "$UPDATED_AT" +%s 2>/dev/null || echo "0")
# Skip protected tags (latest, arm64-latest)
if echo ",$TAGS," | grep -qE ',(latest|arm64-latest),'; then
echo " KEEP: ${DIGEST:0:20}... (protected tag: $TAGS)"
continue
fi
# Check if older than cutoff
if [ "$MANIFEST_DATE" -lt "$CUTOFF_DATE" ]; then
echo " DELETE: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
if [ "$DRY_RUN" != "true" ]; then
doctl registry repository delete-manifest "${REPO}" "$DIGEST" --force
fi
else
echo " KEEP: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
fi
done
echo ""
done
- name: Run garbage collection
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.registry_dry_run == 'false')
run: |
echo "Starting garbage collection..."
set +e
GC_OUTPUT=$(doctl registry garbage-collection start --force 2>&1)
GC_STATUS=$?
set -e
echo "$GC_OUTPUT"
if [ "$GC_STATUS" -eq 0 ]; then
echo "Garbage collection started. It may take a few minutes to complete."
exit 0
fi
if echo "$GC_OUTPUT" | grep -q "automated garbage collection is enabled"; then
echo "Automated garbage collection is enabled for this registry; skipping manual garbage collection."
exit 0
fi
exit "$GC_STATUS"
-86
View File
@@ -1,86 +0,0 @@
name: S3 Archive Cleanup
on:
schedule:
# Run weekly on Sunday at 05:00 UTC
- cron: '0 5 * * 0'
workflow_dispatch: # Allow manual trigger
permissions:
contents: read
jobs:
cleanup:
runs-on: [self-hosted, bazel, halfdan]
steps:
- name: Ensure AWS CLI is available
run: |
if ! command -v aws &> /dev/null; then
brew install awscli
fi
- name: Delete old archived game folders
env:
AWS_ACCESS_KEY_ID: ${{ secrets.DO_SPACES_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DO_SPACES_SECRET_KEY }}
run: |
set -euo pipefail
S3_ENDPOINT="https://sfo3.digitaloceanspaces.com"
BUCKET="s3://eagle0/eagle/archived/"
# macOS date syntax
CUTOFF=$(date -v-1m +%s)
DELETED=0
SKIPPED=0
NO_DIR_FILE=0
echo "Cutoff date: $(date -r ${CUTOFF} '+%Y-%m-%dT%H:%M:%S')"
echo ""
FOLDERS=$(aws s3 ls "$BUCKET" --endpoint-url "$S3_ENDPOINT" \
| awk '/PRE/{gsub(/\/$/,"",$2); print $2}')
if [ -z "$FOLDERS" ]; then
echo "No archived folders found."
exit 0
fi
TOTAL=$(echo "$FOLDERS" | wc -l | tr -d ' ')
CURRENT=0
for game_id in $FOLDERS; do
CURRENT=$((CURRENT + 1))
DIR_INFO=$(aws s3 ls "${BUCKET}${game_id}/directory.e0i" \
--endpoint-url "$S3_ENDPOINT" 2>/dev/null || true)
if [ -z "$DIR_INFO" ]; then
echo "[$CURRENT/$TOTAL] DELETE $game_id (no directory.e0i)"
aws s3 rm --recursive "${BUCKET}${game_id}/" \
--endpoint-url "$S3_ENDPOINT" > /dev/null 2>&1
DELETED=$((DELETED + 1))
NO_DIR_FILE=$((NO_DIR_FILE + 1))
continue
fi
FILE_DATE=$(echo "$DIR_INFO" | awk '{print $1 " " $2}')
FILE_EPOCH=$(date -j -f '%Y-%m-%d %H:%M:%S' "$FILE_DATE" +%s 2>/dev/null || echo "0")
if [ "$FILE_EPOCH" -lt "$CUTOFF" ]; then
echo "[$CURRENT/$TOTAL] DELETE $game_id (directory.e0i from $FILE_DATE)"
aws s3 rm --recursive "${BUCKET}${game_id}/" \
--endpoint-url "$S3_ENDPOINT" > /dev/null 2>&1
DELETED=$((DELETED + 1))
else
echo "[$CURRENT/$TOTAL] KEEP $game_id (directory.e0i from $FILE_DATE)"
SKIPPED=$((SKIPPED + 1))
fi
done
echo ""
echo "=== Summary ==="
echo "Total folders: $TOTAL"
echo "Deleted: $DELETED"
echo "Kept (recent): $SKIPPED"
+153
View File
@@ -0,0 +1,153 @@
name: Server Build
on:
# Main pushes are covered by deploy workflows that build these targets.
pull_request:
paths:
- 'src/main/scala/**'
- 'src/main/cpp/**'
- 'src/main/protobuf/net/eagle0/eagle/**'
- 'src/main/protobuf/net/eagle0/shardok/**'
- 'src/main/protobuf/net/eagle0/common/**'
- 'src/main/go/net/eagle0/build/**'
- 'go.mod'
- 'go.sum'
- 'WORKSPACE'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- 'BUILD.bazel'
- '.bazelrc'
- 'ci/github_actions/ensure_bazel_installed.sh'
- 'scripts/build_eagle_ci.sh'
- 'scripts/build_shardok_ci.sh'
- 'scripts/build_shardok_linux_arm64_ci.sh'
- '.github/actions/setup-bazel/**'
- '.github/workflows/server_build.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: read
jobs:
changes:
runs-on: ubuntu-latest
outputs:
eagle: ${{ steps.filter.outputs.eagle }}
shardok: ${{ steps.filter.outputs.shardok }}
build_matrix: ${{ steps.filter.outputs.build_matrix }}
steps:
- name: Detect changed server areas
id: filter
uses: actions/github-script@v9
with:
script: |
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
});
const changed = files.map(file => file.filename);
const matches = patterns => changed.some(file =>
patterns.some(pattern =>
pattern.endsWith('/') ? file.startsWith(pattern) : file === pattern
)
);
const sharedPatterns = [
'src/main/protobuf/net/eagle0/common/',
'src/main/go/net/eagle0/build/',
'go.mod',
'go.sum',
'WORKSPACE',
'MODULE.bazel',
'MODULE.bazel.lock',
'BUILD.bazel',
'.bazelrc',
'ci/github_actions/ensure_bazel_installed.sh',
'.github/actions/setup-bazel/',
'.github/workflows/server_build.yml',
];
const eaglePatterns = [
'src/main/scala/',
'src/main/protobuf/net/eagle0/eagle/',
'scripts/build_eagle_ci.sh',
];
const shardokPatterns = [
'src/main/cpp/',
'src/main/protobuf/net/eagle0/shardok/',
'scripts/build_shardok_ci.sh',
'scripts/build_shardok_linux_arm64_ci.sh',
];
const shared = matches(sharedPatterns);
const eagle = shared || matches(eaglePatterns);
const shardok = shared || matches(shardokPatterns);
core.info(`Changed files:\n${changed.join('\n')}`);
core.info(`Run Eagle build: ${eagle}`);
core.info(`Run Shardok build: ${shardok}`);
core.setOutput('eagle', String(eagle));
core.setOutput('shardok', String(shardok));
const include = [];
if (eagle) {
include.push({
name: 'Eagle server',
command: './scripts/build_eagle_ci.sh',
});
}
if (shardok) {
include.push({
name: 'Shardok server',
command: './scripts/build_shardok_ci.sh',
});
include.push({
name: 'Shardok Linux ARM64 server',
command: './scripts/build_shardok_linux_arm64_ci.sh',
});
}
core.setOutput('build_matrix', JSON.stringify({include}));
server-build:
name: Build ${{ matrix.name }}
needs: changes
if: needs.changes.outputs.build_matrix != '{"include":[]}'
runs-on: [self-hosted, bazel]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.changes.outputs.build_matrix) }}
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Setup Bazel
uses: ./.github/actions/setup-bazel
- name: Build server
run: ${{ matrix.command }}
+33 -5
View File
@@ -7,10 +7,15 @@ on:
- 'src/main/cpp/**'
- 'src/main/protobuf/net/eagle0/shardok/**'
- 'src/main/protobuf/net/eagle0/common/**'
- 'src/main/go/net/eagle0/build/**'
- 'src/main/resources/net/eagle0/shardok/**'
- 'ci/BUILD.bazel'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/actions/setup-bazel/**'
- 'go.mod'
- 'go.sum'
- 'MODULE.bazel'
- 'MODULE.bazel.lock'
- '.bazelrc'
- '.github/workflows/shardok_arm64_build.yml'
workflow_dispatch:
@@ -30,18 +35,38 @@ permissions:
jobs:
build-shardok-arm64:
runs-on: [self-hosted, bazel, halfdan]
runs-on: [self-hosted, bazel]
outputs:
image_tag: ${{ steps.push-shardok.outputs.image_tag }}
steps:
- name: Prepare non-LFS checkout
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
run: |
git config --global --unset-all filter.lfs.process || true
git config --global filter.lfs.smudge "cat"
git config --global filter.lfs.clean "cat"
git config --global filter.lfs.required false
if [ -d ".git" ]; then
git config --local --unset-all filter.lfs.process || true
git config --local filter.lfs.smudge "cat"
git config --local filter.lfs.clean "cat"
git config --local filter.lfs.required false
fi
rm -f .git/hooks/post-checkout .git/hooks/post-merge .git/hooks/pre-push
- name: Checkout repository
uses: actions/checkout@v6
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
uses: ./.github/actions/setup-bazel
- name: Build Shardok ARM64 binary (cross-compile for Linux ARM64)
run: |
@@ -50,7 +75,7 @@ jobs:
echo "=== Building shardok-server binary for linux-aarch64 ==="
bazel build \
--platforms=//:linux_arm64 \
--extra_toolchains=@llvm_toolchain_linux_arm64//:all \
--extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux \
//src/main/cpp/net/eagle0/shardok:shardok-server
LINUX_BIN="bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server"
@@ -91,9 +116,10 @@ jobs:
run: |
set -ex
# Keep this in sync with the standalone binary build above.
bazel build \
--platforms=//:linux_arm64 \
--extra_toolchains=@llvm_toolchain_linux_arm64//:all \
--extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux \
//ci:shardok_server_image_arm64
IMAGE_PATH=$(readlink -f bazel-bin/ci/shardok_server_image_arm64)
@@ -170,7 +196,9 @@ jobs:
echo "Also tagged as: registry.digitalocean.com/eagle0/shardok-server:arm64-latest"
deploy-hetzner:
runs-on: [self-hosted, bazel, halfdan]
# Keep this on a self-hosted Mac runner for IPv6 reachability to Hetzner,
# but do not require a Bazel runner slot for artifact transport/deploy work.
runs-on: [self-hosted, macOS, ARM64]
needs: [build-shardok-arm64]
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
environment: production
-55
View File
@@ -1,55 +0,0 @@
name: Shardok Build
on:
# Main pushes are covered by shardok_arm64_build.yml which builds the same target
pull_request:
paths:
- 'src/main/cpp/**'
- 'src/main/protobuf/net/eagle0/shardok/**'
- 'src/main/protobuf/net/eagle0/common/**'
- 'src/main/go/net/eagle0/build/**'
- 'WORKSPACE'
- 'MODULE.bazel'
- 'BUILD.bazel'
- '.bazelrc'
- 'ci/github_actions/ensure_bazel_installed.sh'
- '.github/workflows/shardok_build.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
runs-on: [self-hosted, bazel]
steps:
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS=(/opt/homebrew/bin /usr/local/bin)
for path in "${COMMON_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
export PATH="$path:$PATH"
fi
done
if command -v git-lfs >/dev/null 2>&1; then
git-lfs version
exit 0
fi
brew install git-lfs
git-lfs version
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
lfs: false
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
- name: Build Shardok server
run: bazel build -c opt //src/main/cpp/net/eagle0/shardok:shardok-server
+74
View File
@@ -0,0 +1,74 @@
name: Storage Cleanup
on:
schedule:
# Blob cleanup: run daily at 04:00 UTC
- cron: '0 4 * * *'
# S3 archive cleanup: run weekly on Sunday at 05:00 UTC
- cron: '0 5 * * 0'
workflow_dispatch:
inputs:
target:
description: 'Cleanup target'
required: true
default: 'all'
type: choice
options:
- all
- blob
- archive
permissions:
contents: read
jobs:
cleanup:
name: ${{ matrix.name }}
runs-on: [self-hosted, bazel]
strategy:
fail-fast: false
matrix: ${{ fromJSON(github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'all' && '{"include":[{"name":"Blob cleanup","command":"bazel run //src/main/go/net/eagle0/build/blob_cleanup:blob_cleanup -- --min-age=1h"},{"name":"S3 archive cleanup","command":"bazel run //src/main/go/net/eagle0/build/s3_archive_cleanup:s3_archive_cleanup -- --min-age=720h"}]}' || github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'archive' && '{"include":[{"name":"S3 archive cleanup","command":"bazel run //src/main/go/net/eagle0/build/s3_archive_cleanup:s3_archive_cleanup -- --min-age=720h"}]}' || github.event_name == 'schedule' && github.event.schedule == '0 5 * * 0' && '{"include":[{"name":"S3 archive cleanup","command":"bazel run //src/main/go/net/eagle0/build/s3_archive_cleanup:s3_archive_cleanup -- --min-age=720h"}]}' || '{"include":[{"name":"Blob cleanup","command":"bazel run //src/main/go/net/eagle0/build/blob_cleanup:blob_cleanup -- --min-age=1h"}]}') }}
steps:
- name: Prepare non-LFS checkout
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
run: |
git config --global --unset-all filter.lfs.process || true
git config --global filter.lfs.smudge "cat"
git config --global filter.lfs.clean "cat"
git config --global filter.lfs.required false
if [ -d ".git" ]; then
git config --local --unset-all filter.lfs.process || true
git config --local filter.lfs.smudge "cat"
git config --local filter.lfs.clean "cat"
git config --local filter.lfs.required false
fi
rm -f .git/hooks/post-checkout .git/hooks/post-merge .git/hooks/pre-push
- uses: actions/checkout@v6
env:
GIT_CONFIG_GLOBAL: ${{ runner.temp }}/gitconfig-no-lfs
GIT_CONFIG_NOSYSTEM: "1"
with:
persist-credentials: false
lfs: false
clean: false
- name: Ensure Bazel installed
uses: ./.github/actions/setup-bazel
- name: Run blob cleanup
if: matrix.name == 'Blob cleanup'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: ${{ matrix.command }}
- name: Run S3 archive cleanup
if: matrix.name == 'S3 archive cleanup'
env:
DO_SPACES_ACCESS_KEY: ${{ secrets.DO_SPACES_ACCESS_KEY }}
DO_SPACES_SECRET_KEY: ${{ secrets.DO_SPACES_SECRET_KEY }}
run: ${{ matrix.command }}
+237 -25
View File
@@ -17,10 +17,14 @@ on:
- "ci/github_actions/build_unity.sh"
- "ci/github_actions/test_unity_editmode.sh"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/detect_addressables_changes.sh"
- "ci/github_actions/upload_addressables.sh"
- "ci/github_actions/ensure_unity_installed.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- ".bazelrc"
- "WORKSPACE"
workflow_dispatch:
pull_request:
@@ -38,28 +42,30 @@ on:
- "ci/github_actions/build_unity.sh"
- "ci/github_actions/test_unity_editmode.sh"
- "ci/github_actions/ensure_bazel_installed.sh"
- ".github/actions/setup-bazel/**"
- "ci/github_actions/detect_addressables_changes.sh"
- "ci/github_actions/upload_addressables.sh"
- "ci/github_actions/ensure_unity_installed.sh"
- "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- ".bazelrc"
- "WORKSPACE"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
actions: write
contents: read
env:
# Runner-specific build directory to allow parallel builds on multiple runners
EAGLE0_BUILD_DIR: /tmp/eagle0-${{ github.run_id }}
EAGLE0_BUILD_DIR: /tmp/eagle0-${{ github.run_id }}-${{ github.job }}
jobs:
windows-unity:
runs-on: [self-hosted, macOS, unity-windows, halfdan]
outputs:
deployed_version: ${{ steps.get-version.outputs.deployed_version }}
unity-editmode-tests:
runs-on: [self-hosted, macOS, unity-windows]
concurrency:
group: ${{ github.workflow }}-tests-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
steps:
- name: Prune stale PR refs
@@ -95,10 +101,17 @@ jobs:
persist-credentials: false
lfs: false # Fetch LFS after checkout to avoid stale ref issues
clean: false # Library/ persists between runs on self-hosted runners
fetch-depth: 2
- name: Clean stale files
run: |
git clean -ffd
UNITY_ROOT="src/main/csharp/net/eagle0/clients/unity/eagle0"
# These ignored proto outputs are mutually exclusive across proto-generation
# layouts. Clean both before regenerating to avoid stale DLL/source duplicates
# when a self-hosted runner switches between main and PR branches.
rm -rf "$UNITY_ROOT/Assets/GeneratedProtos"
rm -rf "$UNITY_ROOT/Assets/Plugins/Eagle0Protos"
LIBRARY_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library"
VERSION_CACHE="$LIBRARY_DIR/.last_unity_version"
PROJECT_VERSION_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
@@ -137,12 +150,144 @@ jobs:
run: ./ci/github_actions/fetch_lfs.sh
- name: Ensure Unity version installed
run: ./ci/github_actions/ensure_unity_installed.sh windows
- name: Ensure .NET SDK installed
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'
- name: Ensure Bazel installed
run: ./ci/github_actions/ensure_bazel_installed.sh
uses: ./.github/actions/setup-bazel
- name: Run Unity EditMode tests
run: ./ci/github_actions/test_unity_editmode.sh
- name: Archive EditMode test artifacts
if: success() || failure()
uses: actions/upload-artifact@v7
with:
name: unity-editmode-tests
path: |
${{ env.EAGLE0_BUILD_DIR }}/editor_editmode_tests.log
${{ env.EAGLE0_BUILD_DIR }}/editmode-test-results.xml
retention-days: 3
- name: Cleanup build directory
if: always()
run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"
windows-unity:
runs-on: [self-hosted, macOS, unity-windows]
outputs:
addressables_changed: ${{ steps.addressables.outputs.changed }}
deployed_version: ${{ steps.get-version.outputs.deployed_version }}
concurrency:
group: ${{ github.workflow }}-build-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
steps:
- name: Prune stale PR refs
run: |
# Self-hosted runners persist .git between runs. When a PR is updated,
# old local refs (refs/remotes/pull/*/merge) may point to commits whose
# objects were never fetched or have been pruned. Remove these stale refs
# before checkout to prevent "missing object" errors.
if [ -d ".git" ]; then
echo "Pruning stale PR refs..."
git for-each-ref --format='%(refname)' refs/remotes/pull/ 2>/dev/null | \
xargs -r git update-ref -d 2>/dev/null || true
fi
- name: Ensure Git LFS available for checkout
run: |
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
for path in /opt/homebrew/bin /usr/local/bin; do
if [ -d "$path" ]; then
echo "$path" >> "$GITHUB_PATH"
fi
done
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git-lfs --version
- uses: actions/checkout@v6
env:
GIT_LFS_SKIP_SMUDGE: 1
with:
persist-credentials: false
lfs: false # Fetch LFS after checkout to avoid stale ref issues
clean: false # Library/ persists between runs on self-hosted runners
fetch-depth: 2 # Keep the previous main commit available for Addressables change detection
- name: Clean stale files
run: |
git clean -ffd
UNITY_ROOT="src/main/csharp/net/eagle0/clients/unity/eagle0"
# These ignored proto outputs are mutually exclusive across proto-generation
# layouts. Clean both before regenerating to avoid stale DLL/source duplicates
# when a self-hosted runner switches between main and PR branches.
rm -rf "$UNITY_ROOT/Assets/GeneratedProtos"
rm -rf "$UNITY_ROOT/Assets/Plugins/Eagle0Protos"
LIBRARY_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library"
VERSION_CACHE="$LIBRARY_DIR/.last_unity_version"
PROJECT_VERSION_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
CURRENT_VERSION=$(grep "m_EditorVersion:" "$PROJECT_VERSION_FILE" | head -1 | sed 's/m_EditorVersion: //')
# Nuke Library/ when Unity version changes to avoid import loops
if [ -f "$VERSION_CACHE" ]; then
CACHED_VERSION=$(cat "$VERSION_CACHE")
if [ "$CACHED_VERSION" != "$CURRENT_VERSION" ]; then
echo "Unity version changed ($CACHED_VERSION -> $CURRENT_VERSION) -- clearing Library/"
rm -rf "$LIBRARY_DIR"
fi
fi
# Only clear Bee/ when C# files were added/deleted/renamed (structural
# changes that stale the DAG). Content-only modifications are handled by
# Bee's incremental compilation. See persist_library.sh for background.
BEE_DIR="$LIBRARY_DIR/Bee"
SHA_FILE="$LIBRARY_DIR/.last_built_sha"
if [ -f "$SHA_FILE" ] && [ -d "$BEE_DIR" ]; then
LAST_SHA=$(cat "$SHA_FILE")
if git diff --diff-filter=ADR --name-only "$LAST_SHA" HEAD -- '*.cs' '*.csproj' '*.asmdef' 2>/dev/null | grep -q .; then
echo "C# files added/deleted/renamed since $LAST_SHA -- clearing Bee/"
rm -rf "$BEE_DIR"
else
echo "No structural C# changes since $LAST_SHA -- keeping Bee/"
fi
else
echo "No previous build SHA or no Bee/ -- clearing Bee/ as safe default"
rm -rf "$BEE_DIR"
fi
- name: Fetch LFS files
env:
GITHUB_TOKEN: ${{ github.token }}
run: ./ci/github_actions/fetch_lfs.sh
- name: Ensure Unity version installed
run: ./ci/github_actions/ensure_unity_installed.sh windows
- name: Ensure .NET SDK installed
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'
- name: Ensure Bazel installed
uses: ./.github/actions/setup-bazel
- name: Detect Addressables changes
id: addressables
if: success()
shell: bash
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
./ci/github_actions/detect_addressables_changes.sh "$BASE_SHA" "${{ github.sha }}"
- name: Build Windows unity
run: ./ci/github_actions/build_unity.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN"
run: ./ci/github_actions/build_unity.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" true "${{ steps.addressables.outputs.changed }}"
- name: Save build SHA for Bee/ cache invalidation
if: success()
@@ -156,21 +301,89 @@ jobs:
mkdir -p src/main/csharp/net/eagle0/clients/unity/eagle0/Library
grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //' > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_unity_version
- name: Upload Addressables to CDN
- name: Check if run is still latest main for Addressables
id: check-addressables-latest
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && steps.addressables.outputs.changed == 'true'
run: |
LATEST_SHA=$(curl -fsSL \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/commits/main" \
| python3 -c 'import json, sys; print(json.load(sys.stdin)["sha"])')
if [[ "$LATEST_SHA" != "${{ github.sha }}" ]]; then
echo "Skipping Addressables upload because ${{ github.sha }} is no longer latest main ($LATEST_SHA is latest)"
echo "should_upload=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "should_upload=true" >> "$GITHUB_OUTPUT"
- name: Wait for EditMode tests before publishing
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
deadline=$((SECONDS + 600))
while [ "$SECONDS" -lt "$deadline" ]; do
result=$(curl -fsSL \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs" \
| python3 -c 'import json, sys; job = next(j for j in json.load(sys.stdin)["jobs"] if j["name"] == "unity-editmode-tests"); print("{} {}".format(job["status"], job["conclusion"] or ""))')
status="${result%% *}"
conclusion="${result#* }"
if [ "$status" = "completed" ]; then
if [ "$conclusion" = "success" ]; then
echo "EditMode tests passed; publishing may continue."
exit 0
fi
echo "EditMode tests completed with conclusion: $conclusion"
exit 1
fi
echo "EditMode tests are still $status; waiting..."
sleep 5
done
echo "Timed out waiting for EditMode tests."
exit 1
- name: Check if run is still latest main for publish
id: check-publish-latest
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
run: |
LATEST_SHA=$(curl -fsSL \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/commits/main" \
| python3 -c 'import json, sys; print(json.load(sys.stdin)["sha"])')
if [[ "$LATEST_SHA" != "${{ github.sha }}" ]]; then
echo "Skipping publish because ${{ github.sha }} is no longer latest main ($LATEST_SHA is latest)"
echo "should_publish=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "should_publish=true" >> "$GITHUB_OUTPUT"
- name: Stage Windows blobs for publish
if: success() && steps.check-publish-latest.outputs.should_publish == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: bazel run //src/main/go/net/eagle0/build/unity3d_windows_build_handler:unity3d_windows_build_handler -- --skip-previous-manifest "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" "${{ env.EAGLE0_BUILD_DIR }}/unity_manifest.txt"
- name: Upload Windows Addressables to CDN
if: success() && steps.check-publish-latest.outputs.should_publish == 'true' && steps.check-addressables-latest.outputs.should_upload == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: ./ci/github_actions/upload_addressables.sh StandaloneWindows64
- name: Deploy Windows unity
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
- name: Publish previous Windows manifest
if: success() && steps.check-publish-latest.outputs.should_publish == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
run: bazel run //src/main/go/net/eagle0/build/unity3d_windows_build_handler:unity3d_windows_build_handler -- "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" "/tmp/unity_manifest.txt"
run: bazel run //src/main/go/net/eagle0/build/unity3d_windows_build_handler:unity3d_windows_build_handler -- --publish-previous-manifest "${{ env.EAGLE0_BUILD_DIR }}/unity_manifest.txt"
- name: Update unified manifest
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
if: success() && steps.check-publish-latest.outputs.should_publish == 'true'
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
@@ -188,16 +401,16 @@ jobs:
fi
# Update the v2 manifest at installer/v2/eagle0_manifest.txt
bazel run //src/main/go/net/eagle0/build/manifest_manager:manifest_manager -- unity3d-v2 /tmp/unity_manifest.txt $SIGNING_ARGS
bazel run //src/main/go/net/eagle0/build/manifest_manager:manifest_manager -- unity3d-v2 "${{ env.EAGLE0_BUILD_DIR }}/unity_manifest.txt" $SIGNING_ARGS
# Cleanup
rm -f /tmp/manifest_signing_key
- name: Export deployed version
id: get-version
if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
if: success() && steps.check-publish-latest.outputs.should_publish == 'true'
run: |
VERSION=$(grep "^version=" /tmp/unity_manifest.txt | cut -d= -f2 || date +%Y.%m.%d)
VERSION=$(grep "^version=" "${{ env.EAGLE0_BUILD_DIR }}/unity_manifest.txt" | cut -d= -f2 || date +%Y.%m.%d)
echo "deployed_version=$VERSION" >> $GITHUB_OUTPUT
- name: Archive build log
@@ -207,14 +420,13 @@ jobs:
name: editor_win.log
path: ${{ env.EAGLE0_BUILD_DIR }}/editor_win.log
retention-days: 3
- name: Archive EditMode test artifacts
if: success() || failure()
- name: Archive Addressables build reports
if: (success() || failure()) && steps.addressables.outputs.changed == 'true'
uses: actions/upload-artifact@v7
with:
name: unity_editmode_tests
path: |
${{ env.EAGLE0_BUILD_DIR }}/editor_editmode_tests.log
${{ env.EAGLE0_BUILD_DIR }}/editmode-test-results.xml
name: windows-addressables-build-reports
path: src/main/csharp/net/eagle0/clients/unity/eagle0/Library/com.unity.addressables/BuildReports
if-no-files-found: ignore
retention-days: 3
- name: Cleanup build directory
if: always()
+1
View File
@@ -40,6 +40,7 @@ scripts/refresh_name_layers/refresh_name_layers.zip
api_keys.txt
src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/GeneratedProtos/
src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Plugins/Eagle0Protos.meta
src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Plugins/Eagle0Protos/
src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/Packages/com.unity.dedicated-server/
node_modules/
+68 -8
View File
@@ -6,6 +6,11 @@
**NEVER prefix a command with `cd`.** Run `git`, `gh`, `bazel`, etc. directly from whatever the cwd already is. The cwd is a subdirectory of the worktree; `git`/`gh` find the repo via `.git` discovery and `bazel` finds the workspace via MODULE.bazel discovery — walking up from a subdir works fine. The shell resets cwd after every command anyway, so a `cd` never persists. (And `cd <dir> && <cmd>` violates the no-chaining rule above and forces an approval prompt every time.) Only `cd` in the rare case a tool genuinely cannot locate its root, and even then as its own Bash call, never a chain.
## CRITICAL UNITY PROCESS RULES (NEVER VIOLATE)
**NEVER kill Unity without asking the user first.** The user might be actively using the Unity Editor. Do not kill,
force-quit, terminate, or otherwise stop Unity processes unless the user explicitly approves that specific action.
## CRITICAL GIT RULES (NEVER VIOLATE)
**NEVER use `git -C`.** Just run `git` directly from the cwd — it finds the repo via `.git` discovery. Do not `cd` to the repo root either (see the no-`cd` bash rule above).
@@ -24,6 +29,16 @@ If you catch yourself about to run `git push origin main` or `git push origin <b
If you catch yourself about to run `gh pr merge`, STOP. Only the user merges PRs.
## Commit History During Review
Prefer new follow-up commits over amending existing commits once a PR is open and the user is actively reviewing or
testing it. This preserves working interim states so the user can inspect, compare, or return to a known-good point
while improvements continue.
Use `git commit --amend` and force-push only when the user explicitly asks for history cleanup, when fixing metadata
before review has started, or when repairing a local commit that has not been pushed. If you believe squashing is the
right choice despite an open review, explain why and ask first.
## Worktree Usage
Prefer using the current worktree for small requested fixes. Create a separate worktree only when the user asks for one,
@@ -49,6 +64,38 @@ When code changes appear correct locally, create the PR promptly even if long-ru
Opening the PR starts CI on the build machines, uploads results to the Bazel remote cache, and can speed up subsequent
local validation. Continue monitoring any already-started local and CI validation after opening the PR.
For PRs expected to trigger substantial Bazel work in CI, run the relevant Bazel builds and tests locally too. This
workstation is fast, and local Bazel runs can help hydrate the remote cache for the CI builders while still giving
earlier signal on failures.
## Cleanup PR Batching
When making mechanical cleanup changes, group 3-5 files per PR when the files are receiving the same kind of change
and can be reviewed as one pattern. Do not open one PR per file for nearly identical few-line cleanups, such as replacing
the same unsafe accessor pattern with the same contextual failure pattern across multiple Scala files.
Keep separate PRs for changes that involve different semantics, materially different risk, unrelated subsystems, or
files whose tests/validation strategy makes them better reviewed independently.
## GitHub CLI PR Bodies
When creating or editing PR descriptions with multiline bodies, write the body to a temporary markdown file and pass it
with `gh pr create --body-file <path>` or `gh pr edit <number> --body-file <path>`. Do not use multiline `$'...'`
shell quoting for PR bodies; Codex's permission matching may treat that as a complex shell command instead of a clean
`gh pr` invocation, causing unnecessary approval prompts.
Write these temporary body files under `/private/tmp/eagle0-pr-bodies/`, creating that directory first when needed. This
directory is the canonical writable scratch location for PR descriptions. Do not write PR body files to protected paths,
repo-tracked paths, or ad hoc locations that might require additional user permission.
Use ordinary Bash file-writing commands for these temporary PR body files. First run a standalone command to create the
directory. Then run a standalone `printf` command that writes to `/private/tmp/eagle0-pr-bodies/<name>.md`. **Do not use
`apply_patch` for temporary PR body files**, because patch tools are for tracked workspace edits and may incorrectly
request user approval for scratch paths outside the repository.
Write other scratch files needed for commands under `/private/tmp/eagle0-scratch/`, creating that directory first when
needed. Do not use protected paths that require additional user permission just to create or edit temporary files.
---
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
@@ -90,14 +137,14 @@ Unity Client ↔ Eagle (gRPC streaming) ↔ Shardok (internal gRPC)
### Building
```bash
# Build Eagle server (Scala strategic layer)
bazel build //src/main/scala/net/eagle0/eagle:eagle_server_deploy.jar
# Build Eagle server using the same target as GitHub Actions
./scripts/build_eagle_ci.sh
# Build Shardok server (C++ tactical layer)
bazel build -c opt //src/main/cpp/net/eagle0/shardok:shardok-server
# Build Shardok server using the same target and flags as GitHub Actions
./scripts/build_shardok_ci.sh
# Shardok server includes both AI algorithms
bazel build //src/main/cpp/net/eagle0/shardok:shardok-server
# Warm the remote cache for the main GitHub Actions Bazel builds
./scripts/hydrate_bazel_remote_cache.sh
# Build Unity/C# client
./scripts/build_protos.sh # Protocol buffer generation for Unity
@@ -150,6 +197,19 @@ bazel run gazelle # Update Go build files
The pre-commit hook runs gazelle but only checks if it succeeds - it does NOT verify the BUILD files are in canonical format. The `gazelle_test` will fail if deps are not alphabetically sorted. **Always run gazelle manually after BUILD file changes.**
### Bazel Cache Hydration
When making changes that will trigger GitHub Actions Bazel builds, run the matching CI script instead of hand-writing
`bazel build` commands. The scripts keep local cache hydration aligned with CI targets, compilation modes, and flags:
```bash
./scripts/build_eagle_ci.sh
./scripts/build_shardok_ci.sh
./scripts/hydrate_bazel_remote_cache.sh
```
Use direct `bazel build` commands only for targeted debugging where CI cache hydration is not the goal.
### Code Formatting
```bash
@@ -210,7 +270,7 @@ ShardokAIClient client(playerId, isDefender, hexMap, settings, AIAlgorithmType::
```bash
# Build the server (includes both AI algorithms)
bazel build //src/main/cpp/net/eagle0/shardok:shardok-server
./scripts/build_shardok_ci.sh
# Test both algorithms
bazel test //src/test/cpp/net/eagle0/shardok/ai:ai_iterative_deepening_test
@@ -253,7 +313,7 @@ to be used for different players or game situations within the same server proce
**C# (Unity Client):**
- Located in `/src/main/csharp/net/eagle0/clients/unity/eagle0/`
- Uses Unity 6 (6000.4.9f1) with comprehensive protobuf integration (100+ .proto files)
- Uses Unity 6.5 (6000.5.0f1) with comprehensive protobuf integration (100+ .proto files)
- Key components: `EagleConnection.cs` (gRPC client), `EagleGameController.cs` (main game logic)
- Real-time bidirectional streaming with server via `PersistentClientConnection.cs`
- Strategic map UI in `Assets/Eagle/`, tactical battle UI in `Assets/Shardok/`
+3
View File
@@ -1,5 +1,6 @@
load("@bazel_gazelle//:def.bzl", "gazelle")
load("@io_bazel_rules_go//go:def.bzl", "nogo")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
package(default_visibility = ["//visibility:public"])
@@ -10,6 +11,7 @@ platform(
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
missing_toolchain_error = "No Linux x86_64 toolchain matched. Docker/Eagle image builds should pass --platforms=//:linux_x86_64 and --extra_toolchains=@llvm_toolchain_linux//:cc-toolchain-x86_64-linux when compiling C++ code.",
)
# Platform for cross-compiling to Linux ARM64
@@ -19,6 +21,7 @@ platform(
"@platforms//os:linux",
"@platforms//cpu:aarch64",
],
missing_toolchain_error = "No Linux ARM64 toolchain matched. Shardok ARM64 builds should pass --platforms=//:linux_arm64 and --extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux.",
)
gazelle(name = "gazelle")
+1 -1
View File
@@ -228,7 +228,7 @@ to be used for different players or game situations within the same server proce
**C# (Unity Client):**
- Located in `/src/main/csharp/net/eagle0/clients/unity/eagle0/`
- Uses Unity 6 (6000.4.9f1) with comprehensive protobuf integration (100+ .proto files)
- Uses Unity 6 (6000.4.10f1) with comprehensive protobuf integration (100+ .proto files)
- Key components: `EagleConnection.cs` (gRPC client), `EagleGameController.cs` (main game logic)
- Real-time bidirectional streaming with server via `PersistentClientConnection.cs`
- Strategic map UI in `Assets/Eagle/`, tactical battle UI in `Assets/Shardok/`
+104 -63
View File
@@ -3,25 +3,37 @@ module(name = "net_eagle0")
# Version constants
SCALA_VERSION = "3.7.4"
NETTY_VERSION = "4.1.127.Final"
NETTY_VERSION = "4.1.135.Final"
SCALAPB_VERSION = "1.0.0-alpha.1"
SCALAPB_VERSION = "1.0.0-alpha.5"
AWS_SDK_VERSION = "2.41.18"
SCALAPB_JSON4S_VERSION = "1.0.0-alpha.1"
AWS_SDK_VERSION = "2.46.9"
SLF4J_VERSION = "2.0.18"
#
# Core Build Tools
#
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "aspect_rules_esbuild", version = "0.26.0")
bazel_dep(name = "aspect_rules_js", version = "3.2.2")
bazel_dep(name = "bazel_jar_jar", version = "0.1.15", repo_name = "bazel_jar_jar")
bazel_dep(name = "platforms", version = "1.1.0")
bazel_dep(name = "rules_dotnet", version = "0.21.5")
bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
bazel_dep(name = "rules_android", version = "0.7.3")
bazel_dep(name = "rules_nodejs", version = "6.7.4")
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_shell", version = "0.8.0")
#
# Language Support - Scala
#
bazel_dep(name = "rules_scala", version = "7.2.1")
bazel_dep(name = "rules_scala", version = "7.2.6")
scala_config = use_extension(
"@rules_scala//scala/extensions:config.bzl",
@@ -41,21 +53,31 @@ scala_deps.scala_proto()
# Language Support - C++
#
bazel_dep(name = "toolchains_llvm", version = "1.6.0")
bazel_dep(name = "toolchains_llvm", version = "1.8.0")
llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm")
LLVM_VERSION = "22.1.7"
LLVM_DISTRIBUTIONS = {
"LLVM-22.1.7-Linux-ARM64.tar.xz": "118ca2d3ad9da34367e05735317854e7977db45dc4c02a32af58da64c23b8789",
"LLVM-22.1.7-Linux-X64.tar.xz": "edb0522b41e261819c06ea437d249f9b8acfa413d3805bc9920eec6fb76ff830",
"LLVM-22.1.7-macOS-ARM64.tar.xz": "4177245188b0a30a6539c96b361dea56f253485756bfd8927a6a59e7301e7806",
}
# Native toolchain (macOS -> macOS, Linux -> Linux)
llvm.toolchain(
name = "llvm_toolchain",
llvm_version = "20.1.4",
extra_llvm_distributions = LLVM_DISTRIBUTIONS,
llvm_version = LLVM_VERSION,
)
# Cross-compilation toolchain (macOS -> Linux x86_64)
# Uses the same LLVM distribution but with a Linux sysroot
llvm.toolchain(
name = "llvm_toolchain_linux",
llvm_version = "20.1.4",
extra_llvm_distributions = LLVM_DISTRIBUTIONS,
llvm_version = LLVM_VERSION,
)
# Linux x86_64 sysroot for cross-compilation
@@ -68,7 +90,8 @@ llvm.sysroot(
# Cross-compilation toolchain (macOS -> Linux ARM64)
llvm.toolchain(
name = "llvm_toolchain_linux_arm64",
llvm_version = "20.1.4",
extra_llvm_distributions = LLVM_DISTRIBUTIONS,
llvm_version = LLVM_VERSION,
)
# Linux ARM64 sysroot for cross-compilation
@@ -77,7 +100,6 @@ llvm.sysroot(
label = "@linux_sysroot_arm64//sysroot",
targets = ["linux-aarch64"],
)
use_repo(llvm, "llvm_toolchain", "llvm_toolchain_linux", "llvm_toolchain_linux_arm64")
# Download the Linux sysroots (Ubuntu 24.04 Noble for C++23 support)
@@ -103,11 +125,11 @@ sysroot(
# Language Support - Go
#
bazel_dep(name = "rules_go", version = "0.59.0", repo_name = "io_bazel_rules_go")
bazel_dep(name = "gazelle", version = "0.47.0", repo_name = "bazel_gazelle")
bazel_dep(name = "rules_go", version = "0.61.1", repo_name = "io_bazel_rules_go")
bazel_dep(name = "gazelle", version = "0.51.3", repo_name = "bazel_gazelle")
go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.23.3")
go_sdk.download(version = "1.25.11")
use_repo(go_sdk, "go_default_sdk")
go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
@@ -130,7 +152,7 @@ use_repo(
# Language Support - Rust
#
bazel_dep(name = "rules_rust", version = "0.68.1")
bazel_dep(name = "rules_rust", version = "0.70.0")
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(edition = "2021")
@@ -150,8 +172,16 @@ use_repo(crate, "map_generator_crates")
# Platform Support - Apple/iOS
#
bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_apple_support")
bazel_dep(name = "rules_apple", version = "4.3.3", repo_name = "build_bazel_rules_apple")
bazel_dep(name = "apple_support", version = "2.6.1", repo_name = "build_bazel_apple_support")
bazel_dep(name = "rules_apple", version = "4.5.3", repo_name = "build_bazel_rules_apple")
multiple_version_override(
module_name = "rules_swift",
versions = [
"2.1.1",
"3.5.0",
],
)
# Register Apple CC toolchain for Objective-C compilation
apple_cc_configure = use_extension(
@@ -164,11 +194,15 @@ use_repo(apple_cc_configure, "local_config_apple_cc", "local_config_apple_cc_too
# Protocol Buffers & RPC
#
bazel_dep(name = "protobuf", version = "33.5", repo_name = "com_google_protobuf")
bazel_dep(name = "protobuf", version = "35.1", repo_name = "com_google_protobuf")
single_version_override(
module_name = "protobuf",
version = "35.1",
)
# Use pre-built protoc binaries instead of compiling from source.
# See: https://protobuf.dev/reference/cpp/cpp-generated/#invocation
prebuilt_protoc = use_extension("@com_google_protobuf//bazel/private:prebuilt_protoc_extension.bzl", "protoc")
prebuilt_protoc = use_extension("@com_google_protobuf//bazel/private/oss/toolchains/prebuilt:protoc_extension.bzl", "protoc")
use_repo(
prebuilt_protoc,
"prebuilt_protoc.linux_aarch_64",
@@ -183,21 +217,18 @@ use_repo(
)
bazel_dep(name = "rules_proto", version = "7.1.0")
bazel_dep(name = "rules_cc", version = "0.2.14")
bazel_dep(name = "grpc", version = "1.78.0")
bazel_dep(name = "rules_cc", version = "0.2.19")
bazel_dep(name = "grpc", version = "1.81.1")
# Patch grpc 1.78.0 to fix rules_python incompatibility: grpc requests Python
# 3.14.0b2 toolchain but rules_python >= 1.6.0 removed it in favor of 3.14.
# Upstream fix: https://github.com/grpc/grpc/commit/459279b
# TODO: Remove this override once a fixed grpc version is published to BCR.
# gRPC 1.81.1's generated upb module map trips Clang's layering check in the
# Linux ARM64 cross-toolchain even though the normal Bazel deps are present.
single_version_override(
module_name = "grpc",
patch_strip = 1,
patches = ["//third_party/patches:grpc_fix_python_version.patch"],
version = "1.78.0",
patches = ["//third_party/grpc/patches:disable_xds_client_layering_check.patch"],
)
bazel_dep(name = "grpc-java", version = "1.78.0")
bazel_dep(name = "grpc-java", version = "1.78.0.bcr.1")
bazel_dep(name = "rules_proto_grpc_csharp", version = "5.8.0")
bazel_dep(name = "flatbuffers", version = "25.12.19")
@@ -205,13 +236,13 @@ bazel_dep(name = "flatbuffers", version = "25.12.19")
# Testing
#
bazel_dep(name = "googletest", version = "1.17.0")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
#
# Container Images (OCI)
#
bazel_dep(name = "rules_oci", version = "2.2.7")
bazel_dep(name = "rules_oci", version = "2.3.0")
bazel_dep(name = "aspect_bazel_lib", version = "2.22.5")
oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")
@@ -222,19 +253,19 @@ oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")
# curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json" "https://registry-1.docker.io/v2/library/eclipse-temurin/manifests/25-jre" | python3 -c "import json,sys; m=json.load(sys.stdin); [print(p['digest']) for p in m.get('manifests',[]) if p['platform']['architecture']=='amd64']"
oci.pull(
name = "eclipse_temurin_25_jre",
digest = "sha256:98236ffdfb61cff3fc4f8e40e5b80460a5a4c35a0f56a5b4182a926844a7db49",
digest = "sha256:0beb258e10ad28d2bae0f74349a170bdfa67da0ef87c028cbf3d95c3e1170120",
image = "docker.io/library/eclipse-temurin",
platforms = ["linux/amd64"],
tag = "25-jre",
tag = "26-jre",
)
# Base image for JFR sidecar (Java 25 JDK - includes jcmd for JFR dumps)
oci.pull(
name = "eclipse_temurin_25_jdk",
digest = "sha256:bc2d562355813350f47bc472b9721b0b84761866671ad95d9a9f1151fa69da6e",
digest = "sha256:41c3a5b8a607d226c73e81538cd94d0e66f746f41f057561447eb47b40a91a65",
image = "docker.io/library/eclipse-temurin",
platforms = ["linux/amd64"],
tag = "25-jdk",
tag = "26-jdk",
)
# Base image for Shardok (Ubuntu 24.04 for C++ runtime)
@@ -245,15 +276,16 @@ oci.pull(
"linux/amd64",
"linux/arm64/v8",
],
tag = "24.04",
tag = "26.04",
)
# Base image for Admin Server (Alpine for lightweight Go binary)
oci.pull(
name = "alpine_linux",
digest = "sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b",
image = "docker.io/library/alpine",
platforms = ["linux/amd64"],
tag = "3.21",
tag = "3.24",
)
use_repo(oci, "alpine_linux", "alpine_linux_linux_amd64", "eclipse_temurin_25_jdk", "eclipse_temurin_25_jdk_linux_amd64", "eclipse_temurin_25_jre", "eclipse_temurin_25_jre_linux_amd64", "ubuntu_24_04", "ubuntu_24_04_linux_amd64", "ubuntu_24_04_linux_arm64_v8")
@@ -261,8 +293,8 @@ use_repo(oci, "alpine_linux", "alpine_linux_linux_amd64", "eclipse_temurin_25_jd
# Java/Scala Dependencies
#
bazel_dep(name = "rules_java", version = "9.3.0")
bazel_dep(name = "rules_jvm_external", version = "6.10")
bazel_dep(name = "rules_java", version = "9.6.1")
bazel_dep(name = "rules_jvm_external", version = "7.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
@@ -281,7 +313,7 @@ maven.install(
# ScalaPB
"com.thesamet.scalapb:lenses_3:%s" % SCALAPB_VERSION,
"com.thesamet.scalapb:scalapb-json4s_3:%s" % SCALAPB_VERSION,
"com.thesamet.scalapb:scalapb-json4s_3:%s" % SCALAPB_JSON4S_VERSION,
"com.thesamet.scalapb:scalapb-runtime_3:%s" % SCALAPB_VERSION,
"com.thesamet.scalapb:scalapb-runtime-grpc_3:%s" % SCALAPB_VERSION,
"com.thesamet.scalapb:compilerplugin_3:%s" % SCALAPB_VERSION,
@@ -293,10 +325,10 @@ maven.install(
"org.json4s:json4s-native_3:4.1.0-M8",
# Testing
"org.scalamock:scalamock_3:7.4.1",
"org.scalamock:scalamock_3:7.5.5",
# Developer tools
"org.scalameta:scalafmt-cli_2.13:3.9.9",
"org.scalameta:scalafmt-cli_2.13:3.11.1",
# AWS SDK
"software.amazon.awssdk:s3-transfer-manager:%s" % AWS_SDK_VERSION,
@@ -308,35 +340,42 @@ maven.install(
"software.amazon.awssdk:http-client-spi:%s" % AWS_SDK_VERSION,
# AWS Lambda
"com.amazonaws:aws-lambda-java-core:1.2.3",
"com.amazonaws:aws-lambda-java-events:3.13.0",
"com.amazonaws:aws-lambda-java-core:1.4.0",
"com.amazonaws:aws-lambda-java-events:3.16.1",
# Logging
"org.slf4j:slf4j-api:2.0.16",
"org.slf4j:slf4j-simple:2.0.16",
"org.slf4j:slf4j-api:%s" % SLF4J_VERSION,
"org.slf4j:slf4j-simple:%s" % SLF4J_VERSION,
# Other
"org.reactivestreams:reactive-streams:1.0.4",
"javax.xml.bind:jaxb-api:2.3.1",
# OkHttp (for SSE with read timeout support, OAuth HTTP calls)
"com.squareup.okhttp3:okhttp:4.12.0",
"com.squareup.okhttp3:okhttp-sse:4.12.0",
"com.squareup.okhttp3:okhttp-jvm:5.4.0",
"com.squareup.okhttp3:okhttp-sse:5.4.0",
# JWT (for OAuth token handling)
"com.nimbusds:nimbus-jose-jwt:9.37.3",
"com.nimbusds:nimbus-jose-jwt:10.9.1",
# Error tracking
"io.sentry:sentry:8.31.0",
"io.sentry:sentry:8.43.2",
# SQLite for client text storage
"org.xerial:sqlite-jdbc:3.46.1.0",
"org.xerial:sqlite-jdbc:3.53.2.0",
# Postgres for production history benchmarking and migration work
"org.postgresql:postgresql:42.7.11",
],
duplicate_version_warning = "error",
fail_if_repin_required = True,
known_contributing_modules = [
"grpc-java",
"protobuf",
],
lock_file = "//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
"https://repo.maven.apache.org/maven2",
],
)
@@ -346,7 +385,7 @@ maven.artifact(
artifact = "scala3-staging_3",
exclusions = ["org.scala-lang:scala3-compiler_3"],
group = "org.scala-lang",
version = "3.3.6",
version = SCALA_VERSION,
)
# Force specific versions for dependencies with conflicts between grpc-java and protobuf
@@ -354,21 +393,20 @@ maven.artifact(
artifact = "gson",
force_version = True,
group = "com.google.code.gson",
version = "2.11.0",
version = "2.14.0",
)
maven.artifact(
artifact = "error_prone_annotations",
force_version = True,
group = "com.google.errorprone",
version = "2.30.0",
version = "2.50.0",
)
maven.artifact(
artifact = "guava",
force_version = True,
group = "com.google.guava",
version = "33.3.1-android",
version = "33.6.0-jre",
)
use_repo(maven, "maven", "unpinned_maven")
#
@@ -376,6 +414,7 @@ use_repo(maven, "maven", "unpinned_maven")
#
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
# GTL (for parallel_hashmap)
@@ -386,6 +425,8 @@ GTL_SHA = "1969c45dd76eac0dd87e9e2b65cffe358617f4fe1bcd203f72f427742537913a"
http_archive(
name = "gtl",
build_file_content = """
load("@rules_cc//cc:defs.bzl", "cc_library")
cc_library(
name = "gtl",
hdrs = glob(["include/gtl/*.hpp"]),
@@ -413,12 +454,12 @@ http_archive(
)
# Sparkle framework for macOS auto-updates
SPARKLE_VERSION = "2.6.4"
SPARKLE_VERSION = "2.9.3"
http_archive(
name = "sparkle",
build_file = "//src/main/objc/net/eagle0/clients/unity/sparkle/external:BUILD.sparkle",
sha256 = "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b",
sha256 = "74a07da821f92b79310009954c0e15f350173374a3abe39095b4fc5096916be6",
strip_prefix = "",
url = "https://github.com/sparkle-project/Sparkle/releases/download/%s/Sparkle-%s.tar.xz" % (SPARKLE_VERSION, SPARKLE_VERSION),
)
@@ -428,24 +469,24 @@ http_archive(
# Fallback: busybox.net (can be unreliable/slow)
http_file(
name = "busybox_x86_64",
downloaded_file_path = "busybox",
executable = True,
sha256 = "6e123e7f3202a8c1e9b1f94d8941580a25135382b99e8d3e34fb858bba311348",
urls = [
"https://eagle0-sysroot.sfo3.digitaloceanspaces.com/busybox/busybox-1.35.0-x86_64-linux-musl",
"https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox",
],
downloaded_file_path = "busybox",
executable = True,
)
http_file(
name = "busybox_aarch64",
downloaded_file_path = "busybox",
executable = True,
sha256 = "141adb1b625a6f44c4b114f76b4387b4ea4f7ab802b88eb40e0d2f6adcccb1c3",
urls = [
# TODO: Upload aarch64 binary to GitHub release when needed
"https://busybox.net/downloads/binaries/1.35.0-aarch64-linux-musl/busybox",
],
downloaded_file_path = "busybox",
executable = True,
)
# LLVM MinGW toolchain for Windows cross-compilation from macOS
@@ -519,7 +560,7 @@ register_toolchains("@local_config_apple_cc_toolchains//:all")
# Set dev_dependency so we can turn this off for swift MacOS builds
register_toolchains(
"@llvm_toolchain//:all",
"@llvm_toolchain_linux//:all",
"@llvm_toolchain_linux_arm64//:all",
"@llvm_toolchain_linux//:cc-toolchain-x86_64-linux",
"@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux",
dev_dependency = True,
)
+1906 -2426
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -212,9 +212,9 @@ oci_push(
)
#
# Shardok Server ARM64 Docker Image (for Hetzner on-demand compute)
# Shardok Server ARM64 Docker Image (for the Hetzner deployment host)
#
# Build: bazel build //ci:shardok_server_image_arm64 --platforms=//:linux_arm64 --extra_toolchains=@llvm_toolchain_linux_arm64//:all
# Build: bazel build //ci:shardok_server_image_arm64 --platforms=//:linux_arm64 --extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux
# Load: bazel run //ci:shardok_server_load_arm64
# Push: bazel run //ci:shardok_server_push_arm64
#
+1 -3
View File
@@ -5,8 +5,7 @@ set -x
COMMAND_LOG="$(bazel info command_log)"
bazel build \
//src/main/cpp/net/eagle0/shardok:shardok-server \
//src/main/cpp/net/eagle0/shardok:shardok-inspector
//src/main/cpp/net/eagle0/shardok:shardok-server
STATUS="$?"
if [ $STATUS -eq 0 ]
@@ -26,7 +25,6 @@ echo "Setting up Shardok files..."
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server ./shardok/
/bin/mkdir -p ./shardok/shardok-server.runfiles/net_eagle0/src/main/
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server.runfiles/net_eagle0/src/main/resources ./shardok/shardok-server.runfiles/net_eagle0/src/main/
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-inspector ./shardok/
echo "tar & zip shardok files..."
/bin/tar cfh - shardok | /usr/bin/pigz > ./deploy/shardok.tar.gz
-13
View File
@@ -40,19 +40,6 @@ if [ -f "$INFO_PLIST" ]; then
/usr/libexec/PlistBuddy -c "Set :ITSAppUsesNonExemptEncryption false" "$INFO_PLIST" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :ITSAppUsesNonExemptEncryption bool false" "$INFO_PLIST"
echo "Set ITSAppUsesNonExemptEncryption=false in Info.plist"
# Remove UIApplicationSceneManifest if present.
# Unity 6 generates this key, which opts the app into the iOS scene-based
# lifecycle. This breaks Application.deepLinkActivated because iOS routes
# deep link URLs through UISceneDelegate instead of UIApplicationDelegate,
# and Unity doesn't implement the scene delegate path.
# See: https://issuetracker.unity3d.com/issues/in-135632
if /usr/libexec/PlistBuddy -c "Print :UIApplicationSceneManifest" "$INFO_PLIST" 2>/dev/null; then
echo "Found UIApplicationSceneManifest in Info.plist — removing to fix deep link handling"
/usr/libexec/PlistBuddy -c "Delete :UIApplicationSceneManifest" "$INFO_PLIST"
else
echo "No UIApplicationSceneManifest in Info.plist (deep links should work)"
fi
fi
# Unity always generates "Unity-iPhone" as the main app scheme
+12
View File
@@ -9,13 +9,23 @@ WORKSPACE=$(pwd)
UNITY_INSTALL_PATH="/Applications/Unity/Hub/Editor"
BUILD_DIR=$1
LOG_PATH=$2
BUILD_ADDRESSABLES=${3:-true}
ADDRESSABLES_BUILD_LAYOUT=${4:-$BUILD_ADDRESSABLES}
echo "Building Mac in $BUILD_DIR"
echo "Build Addressables: $BUILD_ADDRESSABLES"
echo "Generate Addressables build layout: $ADDRESSABLES_BUILD_LAYOUT"
echo "Cleaning up $BUILD_DIR"
/bin/rm -rf "$BUILD_DIR"
/bin/mkdir -p "$BUILD_DIR"
if [ -n "${DOTNET_ROOT:-}" ]; then
export PATH="$DOTNET_ROOT:$PATH"
export DOTNET_HOST_PATH="$DOTNET_ROOT/dotnet"
fi
dotnet --list-sdks
# Use custom build script that builds Addressables before the player
# Capture exit code to show log on failure
set +e
@@ -25,6 +35,8 @@ ${UNITY_INSTALL_PATH}/${UNITY_VERSION}/Unity.app/Contents/MacOS/Unity \
-quit \
-executeMethod BuildScript.BuildMacPlayer \
-buildPath "$BUILD_DIR/eagle0.app" \
-buildAddressables "$BUILD_ADDRESSABLES" \
-addressablesBuildLayout "$ADDRESSABLES_BUILD_LAYOUT" \
-logFile "$LOG_PATH" \
-projectPath "$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
UNITY_EXIT_CODE=$?
+3 -2
View File
@@ -15,6 +15,7 @@ git log -3
/bin/echo "build Windows"
LOG_PATH="${BUILD_BASE}/editor_win.log"
BUILD_DIR=$1
BUILD_ADDRESSABLES=${2:-true}
ADDRESSABLES_BUILD_LAYOUT=${3:-$BUILD_ADDRESSABLES}
./ci/github_actions/build_windows.sh $BUILD_DIR $LOG_PATH
./ci/github_actions/build_windows.sh "$BUILD_DIR" "$LOG_PATH" "$BUILD_ADDRESSABLES" "$ADDRESSABLES_BUILD_LAYOUT"
+20
View File
@@ -12,6 +12,8 @@ BUILD_BASE="${EAGLE0_BUILD_DIR:-/tmp/eagle0}"
WORKSPACE=$(pwd)
BUILD_PATH=${1:-"${BUILD_BASE}/eagle0iOS"}
BUILD_ADDRESSABLES=${2:-true}
ADDRESSABLES_BUILD_LAYOUT=${3:-$BUILD_ADDRESSABLES}
LOG_PATH="${BUILD_BASE}/editor_ios.log"
# Generate unique build number from git commit count
@@ -25,6 +27,8 @@ echo "Building protos"
UNITY_INSTALL_PATH="/Applications/Unity/Hub/Editor"
echo "Building iOS Unity player to: $BUILD_PATH"
echo "Build Addressables: $BUILD_ADDRESSABLES"
echo "Generate Addressables build layout: $ADDRESSABLES_BUILD_LAYOUT"
mkdir -p "$(dirname "$LOG_PATH")"
mkdir -p "$BUILD_PATH"
@@ -39,6 +43,8 @@ ${UNITY_INSTALL_PATH}/${UNITY_VERSION}/Unity.app/Contents/MacOS/Unity \
-executeMethod BuildScript.BuildiOSPlayer \
-buildPath "$BUILD_PATH" \
-buildNumber "$BUILD_NUMBER" \
-buildAddressables "$BUILD_ADDRESSABLES" \
-addressablesBuildLayout "$ADDRESSABLES_BUILD_LAYOUT" \
-logFile "$LOG_PATH" \
-projectPath "$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
UNITY_EXIT_CODE=$?
@@ -53,6 +59,20 @@ if [ $UNITY_EXIT_CODE -ne 0 ]; then
exit $UNITY_EXIT_CODE
fi
if [ ! -f "$LOG_PATH" ]; then
echo ""
echo "ERROR: Unity exited successfully but did not write an editor log at $LOG_PATH"
exit 1
fi
if [ ! -f "$BUILD_PATH/Data/Raw/aa/settings.json" ]; then
echo ""
echo "ERROR: iOS player is missing Addressables runtime data at:"
echo " $BUILD_PATH/Data/Raw/aa/settings.json"
echo "Build Addressables before packaging the player so Addressables can initialize at runtime."
exit 1
fi
# Fail the build if any prefab references are broken — this produces a player
# that launches but has null Inspector fields, which is hard to debug.
if grep -q "Missing Prefab" "$LOG_PATH"; then
+3 -1
View File
@@ -18,5 +18,7 @@ git log -3
/bin/echo "build Mac"
LOG_PATH="${BUILD_BASE}/editor_mac.log"
BUILD_DIR=$1
BUILD_ADDRESSABLES=${2:-true}
ADDRESSABLES_BUILD_LAYOUT=${3:-$BUILD_ADDRESSABLES}
./ci/github_actions/build_mac.sh "$BUILD_DIR" "$LOG_PATH"
./ci/github_actions/build_mac.sh "$BUILD_DIR" "$LOG_PATH" "$BUILD_ADDRESSABLES" "$ADDRESSABLES_BUILD_LAYOUT"
+20
View File
@@ -9,13 +9,23 @@ WORKSPACE=`pwd`
UNITY_INSTALL_PATH="/Applications/Unity/Hub/Editor"
BUILD_DIR=$1
LOG_PATH=$2
BUILD_ADDRESSABLES=${3:-true}
ADDRESSABLES_BUILD_LAYOUT=${4:-$BUILD_ADDRESSABLES}
echo "Building in $1"
echo "Build Addressables: $BUILD_ADDRESSABLES"
echo "Generate Addressables build layout: $ADDRESSABLES_BUILD_LAYOUT"
echo "Cleaning up $1"
/bin/rm -rf $1
/bin/mkdir -p $1
if [ -n "${DOTNET_ROOT:-}" ]; then
export PATH="$DOTNET_ROOT:$PATH"
export DOTNET_HOST_PATH="$DOTNET_ROOT/dotnet"
fi
dotnet --list-sdks
# Use custom build script that builds Addressables before the player
# Capture exit code to show log on failure
set +e
@@ -25,6 +35,8 @@ ${UNITY_INSTALL_PATH}/${UNITY_VERSION}/Unity.app/Contents/MacOS/Unity \
-quit \
-executeMethod BuildScript.BuildWindowsPlayer \
-buildPath "$BUILD_DIR/eagle0.exe" \
-buildAddressables "$BUILD_ADDRESSABLES" \
-addressablesBuildLayout "$ADDRESSABLES_BUILD_LAYOUT" \
-logFile $LOG_PATH \
-projectPath "$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
UNITY_EXIT_CODE=$?
@@ -45,6 +57,14 @@ if [ ! -f "$LOG_PATH" ]; then
exit 1
fi
if [ ! -f "$BUILD_DIR/eagle0_Data/StreamingAssets/aa/settings.json" ]; then
echo ""
echo "ERROR: Windows player is missing Addressables runtime data at:"
echo " $BUILD_DIR/eagle0_Data/StreamingAssets/aa/settings.json"
echo "Build Addressables before packaging the player so Addressables can initialize at runtime."
exit 1
fi
# Fail the build if any prefab references are broken — this produces a player
# that launches but has null Inspector fields, which is hard to debug.
if grep -q "Missing Prefab" "$LOG_PATH"; then
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_SHA=${1:-}
HEAD_SHA=${2:-HEAD}
OUTPUT_FILE=${GITHUB_OUTPUT:-}
if [ -z "$BASE_SHA" ] || [[ "$BASE_SHA" =~ ^0+$ ]]; then
echo "No usable base SHA for this run; building Addressables as a safe default."
changed=true
elif ! git cat-file -e "$BASE_SHA^{commit}"; then
echo "Base SHA $BASE_SHA is not available locally; building Addressables as a safe default."
changed=true
else
UNITY_PROJECT="src/main/csharp/net/eagle0/clients/unity/eagle0"
ADDRESSABLE_PREFIXES=(
"$UNITY_PROJECT/Assets/AddressableAssetsData/"
"$UNITY_PROJECT/Assets/Eagle/Effects/"
"$UNITY_PROJECT/Assets/Hex Tiles/"
"$UNITY_PROJECT/Assets/TileableBridgePack/"
"$UNITY_PROJECT/Assets/Shardok/LargeFlames.prefab"
"$UNITY_PROJECT/Assets/Shardok/soundEffects/"
"$UNITY_PROJECT/Assets/Shardok/Sounds/"
"$UNITY_PROJECT/Assets/Medieval Combat Sounds/"
"$UNITY_PROJECT/Assets/Magic Spells Sound Effects LITE/"
"$UNITY_PROJECT/Assets/Fantasy Interface Sounds/"
"$UNITY_PROJECT/Assets/AustraliaAnimalsPackv1/"
"$UNITY_PROJECT/Assets/Music/"
"$UNITY_PROJECT/Assets/Editor/BuildScript.cs"
)
is_addressables_input() {
local file="$1"
for prefix in "${ADDRESSABLE_PREFIXES[@]}"; do
if [[ "$file" == "$prefix"* ]]; then
return 0
fi
done
return 1
}
changed=false
while IFS= read -r file; do
if is_addressables_input "$file"; then
echo "Addressables-impacting change: $file"
changed=true
break
fi
done < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
fi
if [ "$changed" = "true" ]; then
echo "Building Addressables because their inputs changed."
else
echo "Skipping Addressables build; no Addressables inputs changed."
fi
if [ -n "$OUTPUT_FILE" ]; then
echo "changed=$changed" >> "$OUTPUT_FILE"
else
echo "changed=$changed"
fi
+22 -2
View File
@@ -5,8 +5,26 @@ set -euo pipefail
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
write_developer_dir_override() {
repo_contents_cache_dir() {
if [ "$(uname -s)" = "Darwin" ]; then
printf '%s\n' "${HOME}/Library/Caches/eagle0-bazel-repo-contents"
else
printf '%s\n' "${XDG_CACHE_HOME:-${HOME}/.cache}/eagle0-bazel-repo-contents"
fi
}
write_bazel_local_config() {
local repo_contents_cache
repo_contents_cache="$(repo_contents_cache_dir)"
if [ "$(uname -s)" != "Darwin" ]; then
if [ -n "${CI:-}" ]; then
mkdir -p "$repo_contents_cache"
{
echo "# Generated by ci/github_actions/ensure_bazel_installed.sh; do not edit."
printf 'common --repo_contents_cache=%s\n' "$repo_contents_cache"
} > .bazelrc.local
fi
return
fi
@@ -30,8 +48,10 @@ write_developer_dir_override() {
if [ -f .bazelrc.local ] && [ -z "${CI:-}" ]; then
echo "Keeping existing .bazelrc.local outside CI"
else
mkdir -p "$repo_contents_cache"
{
echo "# Generated by ci/github_actions/ensure_bazel_installed.sh; do not edit."
printf 'common --repo_contents_cache=%s\n' "$repo_contents_cache"
printf 'common:macos --repo_env=DEVELOPER_DIR=%s\n' "$developer_dir"
} > .bazelrc.local
fi
@@ -43,7 +63,7 @@ write_developer_dir_override() {
echo "Using Apple developer directory at $developer_dir"
}
write_developer_dir_override
write_bazel_local_config
if [ -n "${GITHUB_PATH:-}" ]; then
for path in /opt/homebrew/bin /usr/local/bin; do
+32 -11
View File
@@ -34,6 +34,7 @@ echo "Platform: ${PLATFORM}"
# Check if Unity is installed and has required modules
check_modules_installed() {
local unity_path="${UNITY_INSTALL_PATH}/${UNITY_VERSION}"
local unity_app_contents_path="${unity_path}/Unity.app/Contents"
if [ ! -d "$unity_path" ]; then
return 1
@@ -48,15 +49,12 @@ check_modules_installed() {
fi
;;
mac)
# Mac IL2CPP module
if [ ! -d "${unity_path}/PlaybackEngines/MacStandaloneSupport/Variations/macos_development_il2cpp" ] && \
[ ! -d "${unity_path}/PlaybackEngines/MacStandaloneSupport" ]; then
if ! has_mac_il2cpp_support "$unity_path" "$unity_app_contents_path"; then
echo "✗ Mac IL2CPP module not installed for Unity ${UNITY_VERSION}"
return 1
fi
;;
windows)
# Windows mono module
if [ ! -d "${unity_path}/PlaybackEngines/WindowsStandaloneSupport" ]; then
echo "✗ Windows module not installed for Unity ${UNITY_VERSION}"
return 1
@@ -69,8 +67,8 @@ check_modules_installed() {
echo "✗ iOS module missing"
missing=1
fi
if [ ! -d "${unity_path}/PlaybackEngines/MacStandaloneSupport" ]; then
echo "✗ Mac module missing"
if ! has_mac_il2cpp_support "$unity_path" "$unity_app_contents_path"; then
echo "✗ Mac IL2CPP module missing"
missing=1
fi
if [ ! -d "${unity_path}/PlaybackEngines/WindowsStandaloneSupport" ]; then
@@ -86,6 +84,27 @@ check_modules_installed() {
return 0
}
has_mac_il2cpp_support() {
local unity_path="$1"
local unity_app_contents_path="$2"
has_il2cpp_variation "${unity_app_contents_path}/PlaybackEngines/MacStandaloneSupport/Variations" ||
has_il2cpp_variation "${unity_path}/PlaybackEngines/MacStandaloneSupport/Variations"
}
has_il2cpp_variation() {
local variations_path="$1"
local variation
for variation in "${variations_path}"/*il2cpp*; do
if [ -d "$variation" ]; then
return 0
fi
done
return 1
}
# Check if already installed with required modules
if check_modules_installed; then
echo "✓ Unity ${UNITY_VERSION} is already installed with ${PLATFORM} support"
@@ -191,8 +210,10 @@ echo "Running: ${INSTALL_CMD[*]}"
echo ""
# Capture output to check for "already installed" messages
OUTPUT=$("${INSTALL_CMD[@]}" 2>&1) || true
set +e
OUTPUT=$("${INSTALL_CMD[@]}" 2>&1)
EXIT_CODE=$?
set -e
echo "$OUTPUT"
# Check if modules are already installed (Unity Hub returns error but modules are present)
@@ -219,13 +240,13 @@ else
exit 1
fi
# Verify installation
# Verify installation and requested modules
echo ""
if [ -d "${UNITY_INSTALL_PATH}/${UNITY_VERSION}" ]; then
echo "✓ Verified: Unity ${UNITY_VERSION} is now installed"
if check_modules_installed; then
echo "✓ Verified: Unity ${UNITY_VERSION} is now installed with ${PLATFORM} support"
exit 0
else
echo "✗ Unity ${UNITY_VERSION} installation could not be verified"
echo "✗ Unity ${UNITY_VERSION} installation with ${PLATFORM} support could not be verified"
echo " Expected path: ${UNITY_INSTALL_PATH}/${UNITY_VERSION}"
echo ""
echo "The installation may still be in progress, or may require manual intervention."
+18 -1
View File
@@ -38,15 +38,23 @@ fi
echo "Using git-lfs at $(command -v git-lfs)"
git lfs version
git lfs install
git lfs install --local --force
echo "LFS objects before pull:"
git lfs ls-files | wc -l
GIT_LFS_PULL=(git lfs pull)
FALLBACK_GITHUB_LFS_PULL=()
if [ -n "${GITHUB_TOKEN:-}" ]; then
BASIC_AUTH=$(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')
GIT_LFS_PULL=(git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic $BASIC_AUTH" lfs pull)
FALLBACK_GITHUB_LFS_PULL=(
git
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic $BASIC_AUTH"
-c "lfs.url=https://github.com/nolen777/eagle0.git/info/lfs"
lfs
pull
)
fi
for i in $(seq 1 $MAX_ATTEMPTS); do
@@ -61,5 +69,14 @@ for i in $(seq 1 $MAX_ATTEMPTS); do
fi
done
if [ "${#FALLBACK_GITHUB_LFS_PULL[@]}" -gt 0 ]; then
echo "LFS mirror pull failed after $MAX_ATTEMPTS attempts; trying GitHub LFS fallback"
if "${FALLBACK_GITHUB_LFS_PULL[@]}" "$@"; then
echo "LFS objects after GitHub fallback pull:"
git lfs ls-files | wc -l
exit 0
fi
fi
echo "LFS pull failed after $MAX_ATTEMPTS attempts"
exit 1
+235
View File
@@ -0,0 +1,235 @@
#!/usr/bin/env python3
"""Print a compact timing summary from a Bazel build event JSON file."""
from __future__ import annotations
import json
import sys
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any
def duration_to_ms(value: Any) -> float | None:
if value is None:
return None
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, str):
if value.endswith("s"):
try:
return float(value[:-1]) * 1000
except ValueError:
return None
try:
return float(value)
except ValueError:
return None
if isinstance(value, dict):
seconds = value.get("seconds", 0)
nanos = value.get("nanos", 0)
try:
return float(seconds) * 1000 + float(nanos) / 1_000_000
except (TypeError, ValueError):
return None
return None
def format_ms(milliseconds: float | None) -> str | None:
if milliseconds is None:
return None
if milliseconds >= 1000:
return f"{milliseconds / 1000:.2f}s"
return f"{milliseconds:.0f}ms"
def nested_get(data: dict[str, Any], *keys: str) -> Any:
current: Any = data
for key in keys:
if not isinstance(current, dict):
return None
current = current.get(key)
return current
def read_events(path: Path) -> list[dict[str, Any]]:
events = []
with path.open() as file:
for line_number, line in enumerate(file, start=1):
stripped = line.strip()
if not stripped:
continue
try:
event = json.loads(stripped)
except json.JSONDecodeError as error:
print(f"Ignoring malformed BEP line {line_number}: {error}")
continue
if isinstance(event, dict):
events.append(event)
return events
def collect_metric_events(events: list[dict[str, Any]]) -> list[dict[str, Any]]:
metric_events = []
for event in events:
if isinstance(event.get("buildMetrics"), dict):
metric_events.append(event["buildMetrics"])
return metric_events
def first_present(data: dict[str, Any], keys: list[str]) -> Any:
for key in keys:
value = data.get(key)
if value is not None:
return value
return None
def summarize_timing(build_metrics: dict[str, Any]) -> list[str]:
timing = build_metrics.get("timingMetrics")
if not isinstance(timing, dict):
return []
labels = [
("Wall time", ["wallTimeInMs", "wallTime", "elapsedTime"]),
("Loading phase", ["loadingPhaseTimeInMs", "loadingPhaseTime"]),
("Analysis phase", ["analysisPhaseTimeInMs", "analysisPhaseTime"]),
("Execution phase", ["executionPhaseTimeInMs", "executionPhaseTime"]),
("Critical path", ["criticalPathTimeInMs", "criticalPathTime"]),
]
lines = []
for label, keys in labels:
value = format_ms(duration_to_ms(first_present(timing, keys)))
if value is not None:
lines.append(f" {label}: {value}")
return lines
def summarize_counts(build_metrics: dict[str, Any]) -> list[str]:
summaries: list[tuple[str, Any]] = []
action_summary = build_metrics.get("actionSummary")
if isinstance(action_summary, dict):
summaries.extend(
[
("Actions created", action_summary.get("actionsCreated")),
("Actions executed", action_summary.get("actionsExecuted")),
("Action cache hits", action_summary.get("actionCacheHits")),
("Remote cache hits", action_summary.get("remoteCacheHits")),
]
)
target_metrics = build_metrics.get("targetMetrics")
if isinstance(target_metrics, dict):
summaries.extend(
[
("Targets configured", target_metrics.get("targetsConfigured")),
("Targets loaded", target_metrics.get("targetsLoaded")),
]
)
package_metrics = build_metrics.get("packageMetrics")
if isinstance(package_metrics, dict):
summaries.extend(
[
("Packages loaded", package_metrics.get("packagesLoaded")),
("Packages successfully loaded", package_metrics.get("packagesSuccessfullyLoaded")),
]
)
return [f" {label}: {value}" for label, value in summaries if value is not None]
def worker_mnemonic(metric: dict[str, Any]) -> str:
for path in [
("mnemonic",),
("workerKey", "mnemonic"),
("workerKeyInfo", "mnemonic"),
]:
value = nested_get(metric, *path)
if value:
return str(value)
return "unknown"
def worker_actions(metric: dict[str, Any]) -> int | None:
for key in ["actionsExecuted", "executedActionCount", "actionCount", "actions"]:
value = metric.get(key)
if isinstance(value, int):
return value
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
return None
def summarize_workers(build_metrics: dict[str, Any]) -> list[str]:
worker_metrics = build_metrics.get("workerMetrics")
if not isinstance(worker_metrics, list) or not worker_metrics:
return []
counts = Counter()
actions_by_mnemonic: defaultdict[str, int] = defaultdict(int)
saw_action_counts = False
for metric in worker_metrics:
if not isinstance(metric, dict):
continue
mnemonic = worker_mnemonic(metric)
counts[mnemonic] += 1
actions = worker_actions(metric)
if actions is not None:
saw_action_counts = True
actions_by_mnemonic[mnemonic] += actions
if not counts:
return []
lines = [" Worker metric entries:"]
for mnemonic, count in counts.most_common():
suffix = ""
if saw_action_counts:
suffix = f", actions={actions_by_mnemonic[mnemonic]}"
lines.append(f" {mnemonic}: workers={count}{suffix}")
return lines
def main() -> int:
if len(sys.argv) != 2:
print("Usage: summarize_bazel_bep.py <build_event_json_file>")
return 0
path = Path(sys.argv[1])
if not path.exists():
print(f"Bazel BEP summary: {path} does not exist")
return 0
events = read_events(path)
metric_events = collect_metric_events(events)
if not metric_events:
print("Bazel BEP summary: no buildMetrics event found")
return 0
build_metrics = metric_events[-1]
sections = [
summarize_timing(build_metrics),
summarize_counts(build_metrics),
summarize_workers(build_metrics),
]
lines = [line for section in sections for line in section]
if not lines:
print("Bazel BEP summary: buildMetrics event had no recognized metrics")
return 0
print("Bazel BEP summary:")
for line in lines:
print(line)
return 0
if __name__ == "__main__":
sys.exit(main())
+177 -7
View File
@@ -13,11 +13,166 @@ set -euxo pipefail
BUILD_TARGET=$1
WORKSPACE=$(pwd)
UNITY_PROJECT="$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
SERVER_DATA="$UNITY_PROJECT/ServerData/$BUILD_TARGET"
UNITY_VERSION=$(grep "m_EditorVersion:" "$UNITY_PROJECT/ProjectSettings/ProjectVersion.txt" | head -1 | sed 's/m_EditorVersion: //')
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
ADDRESSABLES_PREFIX="addressables/$BUILD_TARGET/$UNITY_MAJOR_MINOR"
SERVER_DATA="$UNITY_PROJECT/ServerData/$BUILD_TARGET/$UNITY_MAJOR_MINOR"
# DigitalOcean Spaces configuration (same region as other eagle0 buckets)
DO_ENDPOINT="https://sfo3.digitaloceanspaces.com"
DO_BUCKET="eagle0-assets"
DEFAULT_MAX_UPLOAD_BYTES=$((1024 * 1024 * 1024))
DEFAULT_MAX_SINGLE_FILE_BYTES=$((512 * 1024 * 1024))
MAX_UPLOAD_BYTES=${ADDRESSABLES_MAX_UPLOAD_BYTES:-$DEFAULT_MAX_UPLOAD_BYTES}
MAX_SINGLE_FILE_BYTES=${ADDRESSABLES_MAX_SINGLE_FILE_BYTES:-$DEFAULT_MAX_SINGLE_FILE_BYTES}
TOP_UPLOAD_COUNT=${ADDRESSABLES_TOP_UPLOAD_COUNT:-20}
COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"
format_bytes() {
local bytes=$1
local unit="B"
local value=$bytes
if [ "$bytes" -ge $((1024 * 1024 * 1024)) ]; then
unit="GiB"
value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 / 1024 / 1024 }")
elif [ "$bytes" -ge $((1024 * 1024)) ]; then
unit="MiB"
value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 / 1024 }")
elif [ "$bytes" -ge 1024 ]; then
unit="KiB"
value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 }")
fi
echo "$value $unit"
}
file_size() {
local path=$1
if stat -f%z "$path" >/dev/null 2>&1; then
stat -f%z "$path"
else
stat -c%s "$path"
fi
}
append_summary() {
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
echo "$1" >> "$GITHUB_STEP_SUMMARY"
fi
}
budget_enabled() {
local value=$1
[ "$value" -gt 0 ]
}
summarize_pending_uploads() {
local sync_args_file
sync_args_file=$(mktemp)
local uploads_file
uploads_file=$(mktemp)
local sorted_uploads_file
sorted_uploads_file=$(mktemp)
trap 'rm -f "$sync_args_file" "$uploads_file" "$sorted_uploads_file"' RETURN
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
--endpoint-url "$DO_ENDPOINT" \
--acl public-read \
--size-only \
--delete \
--dryrun > "$sync_args_file"
while IFS= read -r line; do
case "$line" in
*upload:*)
local source_path=${line#*upload: }
source_path=${source_path% to s3://*}
if [ -f "$source_path" ]; then
local size
size=$(file_size "$source_path")
printf "%s\t%s\n" "$size" "$source_path" >> "$uploads_file"
fi
;;
esac
done < "$sync_args_file"
local upload_count=0
local upload_bytes=0
if [ -s "$uploads_file" ]; then
while IFS=$'\t' read -r size _path; do
upload_count=$((upload_count + 1))
upload_bytes=$((upload_bytes + size))
done < "$uploads_file"
fi
echo "Addressables upload preflight: $upload_count changed files, $(format_bytes "$upload_bytes") to upload."
append_summary "## Addressables upload preflight"
append_summary ""
append_summary "- Target: \`$BUILD_TARGET\`"
append_summary "- Changed files: \`$upload_count\`"
append_summary "- Changed upload bytes: \`$(format_bytes "$upload_bytes")\`"
append_summary "- Upload byte budget: \`$(format_bytes "$MAX_UPLOAD_BYTES")\`"
append_summary "- Single-file budget: \`$(format_bytes "$MAX_SINGLE_FILE_BYTES")\`"
local guardrail_failed=0
if [ -s "$uploads_file" ]; then
echo "Largest pending Addressables uploads:"
append_summary ""
append_summary "### Largest pending uploads"
append_summary ""
append_summary "| Size | Path |"
append_summary "| ---: | --- |"
sort -nr "$uploads_file" > "$sorted_uploads_file"
local listed_uploads=0
while IFS=$'\t' read -r size path; do
if [ "$listed_uploads" -ge "$TOP_UPLOAD_COUNT" ]; then
break
fi
local relative_path=${path#"$SERVER_DATA"/}
echo " $(format_bytes "$size") $relative_path"
append_summary "| $(format_bytes "$size") | \`$relative_path\` |"
listed_uploads=$((listed_uploads + 1))
done < "$sorted_uploads_file"
if budget_enabled "$MAX_SINGLE_FILE_BYTES"; then
while IFS=$'\t' read -r size path; do
if [ "$size" -gt "$MAX_SINGLE_FILE_BYTES" ]; then
local relative_path=${path#"$SERVER_DATA"/}
echo "ERROR: Pending Addressables upload '$relative_path' is $(format_bytes "$size"), exceeding single-file budget $(format_bytes "$MAX_SINGLE_FILE_BYTES")."
append_summary ""
append_summary ":x: \`$relative_path\` exceeds the single-file budget at \`$(format_bytes "$size")\`."
guardrail_failed=1
fi
done < "$uploads_file"
fi
fi
if budget_enabled "$MAX_UPLOAD_BYTES" && [ "$upload_bytes" -gt "$MAX_UPLOAD_BYTES" ]; then
echo "ERROR: Addressables upload is expected to send $(format_bytes "$upload_bytes"), exceeding budget $(format_bytes "$MAX_UPLOAD_BYTES")."
echo "A small asset change may have invalidated an oversized bundle; inspect the largest pending uploads above."
append_summary ""
append_summary ":x: Changed upload bytes exceed budget: \`$(format_bytes "$upload_bytes")\` > \`$(format_bytes "$MAX_UPLOAD_BYTES")\`."
guardrail_failed=1
fi
if [ "$guardrail_failed" -ne 0 ]; then
echo "Refusing to upload Addressables. Raise ADDRESSABLES_MAX_UPLOAD_BYTES or ADDRESSABLES_MAX_SINGLE_FILE_BYTES only after confirming the upload is intentional."
exit 1
fi
}
if ! command -v aws >/dev/null 2>&1; then
if command -v brew >/dev/null 2>&1; then
brew install awscli
else
echo "ERROR: aws CLI is unavailable, and Homebrew is not on PATH"
exit 1
fi
fi
if [ ! -d "$SERVER_DATA" ]; then
echo "No Addressables bundles found at $SERVER_DATA"
@@ -26,19 +181,34 @@ if [ ! -d "$SERVER_DATA" ]; then
fi
echo "Uploading Addressables bundles from $SERVER_DATA"
echo "Target: s3://$DO_BUCKET/addressables/$BUILD_TARGET/"
echo "Target: s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/"
# Configure AWS CLI for DigitalOcean Spaces
export AWS_ACCESS_KEY_ID="$ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$SECRET_KEY"
# Sync bundles to Spaces
# --delete removes files in destination that don't exist in source
# --acl public-read makes files publicly accessible
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/addressables/$BUILD_TARGET/" \
summarize_pending_uploads
# Sync bundles to Spaces. Addressable bundle filenames include content hashes, so a
# same-sized existing bundle is already the same content. Use --size-only to avoid
# re-uploading every rebuilt bundle just because Unity gave it a fresh local mtime.
# Metadata files keep stable names, so sync them again without --size-only below.
#
# --delete removes files in destination that don't exist in source.
# --acl public-read makes files publicly accessible.
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
--endpoint-url "$DO_ENDPOINT" \
--acl public-read \
--size-only \
--delete
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
--endpoint-url "$DO_ENDPOINT" \
--acl public-read \
--exclude "*" \
--include "*.bin" \
--include "*.hash" \
--include "*.json"
echo "Addressables upload complete"
echo "Files available at: https://assets.eagle0.net/addressables/$BUILD_TARGET/"
echo "Files available at: https://assets.eagle0.net/$ADDRESSABLES_PREFIX/"
+2
View File
@@ -5,6 +5,8 @@ lower OCI layer and frequently-changing first-party jars in a small top layer,
so most pushes only re-upload the small layer.
"""
load("@rules_java//java/common:java_info.bzl", "JavaInfo")
def _unique_name(jar):
# short_path is unique per jar and stable across commits (it depends only on
# the jar's own package/coordinate, not on unrelated targets), so the deps
-2
View File
@@ -1,5 +1,3 @@
load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
#pkg_tar(
# name = "eagle_servers",
# strip_prefix = "/src/main/scala/net/eagle0",
+14
View File
@@ -32,6 +32,13 @@ services:
DO_SPACES_ENDPOINT: "${DO_SPACES_ENDPOINT:-https://sfo3.digitaloceanspaces.com}"
DO_SPACES_ACCESS_KEY: "${DO_SPACES_ACCESS_KEY:-}"
DO_SPACES_SECRET_KEY: "${DO_SPACES_SECRET_KEY:-}"
EAGLE_HISTORY_BACKEND: "${EAGLE_HISTORY_BACKEND:-postgres}"
EAGLE_POSTGRES_HOST: "${EAGLE_POSTGRES_HOST:-}"
EAGLE_POSTGRES_PORT: "${EAGLE_POSTGRES_PORT:-}"
EAGLE_POSTGRES_DATABASE: "${EAGLE_POSTGRES_DATABASE:-}"
EAGLE_POSTGRES_USER: "${EAGLE_POSTGRES_USER:-}"
EAGLE_POSTGRES_PASSWORD: "${EAGLE_POSTGRES_PASSWORD:-}"
EAGLE_POSTGRES_SSLMODE: "${EAGLE_POSTGRES_SSLMODE:-require}"
# JWT public key for token validation (auth service handles signing)
# Reads from /etc/eagle0/keys/public.pem via shared volume
# Auth token for Shardok on Hetzner (required)
@@ -81,6 +88,13 @@ services:
DO_SPACES_ENDPOINT: "${DO_SPACES_ENDPOINT:-https://sfo3.digitaloceanspaces.com}"
DO_SPACES_ACCESS_KEY: "${DO_SPACES_ACCESS_KEY:-}"
DO_SPACES_SECRET_KEY: "${DO_SPACES_SECRET_KEY:-}"
EAGLE_HISTORY_BACKEND: "${EAGLE_HISTORY_BACKEND:-postgres}"
EAGLE_POSTGRES_HOST: "${EAGLE_POSTGRES_HOST:-}"
EAGLE_POSTGRES_PORT: "${EAGLE_POSTGRES_PORT:-}"
EAGLE_POSTGRES_DATABASE: "${EAGLE_POSTGRES_DATABASE:-}"
EAGLE_POSTGRES_USER: "${EAGLE_POSTGRES_USER:-}"
EAGLE_POSTGRES_PASSWORD: "${EAGLE_POSTGRES_PASSWORD:-}"
EAGLE_POSTGRES_SSLMODE: "${EAGLE_POSTGRES_SSLMODE:-require}"
# JWT public key for token validation (auth service handles signing)
# Reads from /etc/eagle0/keys/public.pem via shared volume
SHARDOK_AUTH_TOKEN: "${SHARDOK_AUTH_TOKEN:-}"
+1 -1
View File
@@ -68,7 +68,7 @@ private GameObject GetEffectPrefab(string beastName) {
### 4. Wire up in the scene
In `Gameplay.unity`, find the `ProvinceBeastsController` component and assign your new prefab to the new field.
In `Assets/Scenes/Eagle.unity`, find the `ProvinceBeastsController` component on the Eagle map object and assign your new prefab to the new field.
### 5. Test
-159
View File
@@ -1,159 +0,0 @@
# Hetzner Setup Guide
This guide walks through setting up Hetzner Cloud infrastructure for running Shardok on-demand compute.
## Prerequisites
- All code PRs merged (#4990, #4996, #4998, #5001, #5009)
- Access to DigitalOcean Container Registry (for pulling Shardok ARM64 image)
---
## Step 1: Create Hetzner Cloud Account
1. Go to https://console.hetzner.cloud/
2. Sign up and add payment method
3. Create a new project (e.g., "eagle0")
---
## Step 2: Generate Hetzner API Token
1. In Hetzner Console → Security → API Tokens
2. Click "Generate API Token"
3. Give it **Read & Write** permissions
4. Copy the token (you'll only see it once)
---
## Step 3: Generate Shardok Auth Token
Generate a 256-bit random token for Eagle-Shardok authentication:
```bash
openssl rand -hex 32
```
Save this output - it's the shared secret between Eagle and Shardok.
---
## Step 4: Store Secrets in GitHub Actions
Add these secrets in GitHub → Settings → Secrets and variables → Actions:
| Secret Name | Description |
|-------------|-------------|
| `HETZNER_API_TOKEN` | From Step 2 - for Hetzner API calls |
| `SHARDOK_AUTH_TOKEN` | From Step 3 - shared secret for gRPC auth |
Note: `DO_REGISTRY_TOKEN` already exists and will be used for Hetzner to pull container images.
These secrets will be passed to Eagle at runtime via `docker_build.yml`, similar to how `OPENAI_API_KEY` and other secrets are handled.
---
## Step 5: DNS Setup (for Let's Encrypt)
You need a domain pointing to the Shardok instance for TLS certificates.
### Option A: Floating IP (Recommended)
1. In Hetzner Console → Networking → Floating IPs
2. Create a **Floating IPv6** in **Hillsboro, Oregon (hil)** region
- IPv6 costs €1/month vs €3/month for IPv4
- Hillsboro has better latency to DigitalOcean SFO than Ashburn
- Server-to-server communication works fine with IPv6-only
3. Point `shardok.prod.eagle0.net` to this IP via AAAA record
4. The ShardokInstanceManager will attach this IP to instances on spin-up
**Location choice**: Hillsboro, OR (`hil`) is recommended for US West Coast. Same pricing as Ashburn (`ash`).
### Option B: Dynamic DNS
Update DNS programmatically when instance spins up. More complex but avoids floating IP cost.
---
## Step 6: Upload SSH Key to Hetzner
For debugging access to instances:
1. In Hetzner Console → Security → SSH Keys
2. Click "Add SSH Key"
3. Paste your public key (e.g., `~/.ssh/id_rsa.pub`)
4. Give it a name (e.g., "eagle-deploy")
---
## Step 7: Wire Security Config into Eagle
Update Eagle's startup code to use the security config when connecting to remote Shardok:
```scala
val securityConfig = ShardokSecurityConfig(
useTls = true,
authToken = Some(sys.env("SHARDOK_AUTH_TOKEN"))
)
val channel = ServerSetupHelpers.newChannel(
"shardok.prod.eagle0.net",
50051,
securityConfig
)
```
---
## Testing
### Manual Instance Spin-up
Test the Hetzner integration by triggering instance creation:
```scala
val manager = new ShardokInstanceManager(
hetznerApiToken = sys.env("HETZNER_API_TOKEN"),
// ... other config
)
manager.ensureInstanceRunning()
```
### Verify TLS and Auth
1. Instance spins up and gets Let's Encrypt certificate
2. Eagle connects via TLS
3. Auth token is validated on each request
---
## Cost Estimate
| Component | Cost |
|-----------|------|
| CAX41 (16 ARM cores) | ~$0.04/hour |
| Floating IP | ~$4/month |
| Typical usage (20 hrs/week) | ~$3.50/month compute |
**Total: ~$7-8/month** for typical usage.
---
## Troubleshooting
### Instance won't start
- Check Hetzner API token has Read & Write permissions
- Verify you're using the correct region (`hil` for Hillsboro OR, or `ash` for Ashburn VA)
### TLS certificate fails
- Ensure DNS points to the instance IP before certbot runs
- Check port 80 is open for Let's Encrypt HTTP-01 challenge
### Auth failures
- Verify `SHARDOK_AUTH_TOKEN` matches on both Eagle and Shardok
- Check the token file is readable by Shardok container
### Can't pull container image
- Ensure `DO_REGISTRY_TOKEN` is passed to cloud-init
- Verify the ARM64 image exists: `registry.digitalocean.com/eagle0/shardok-server:arm64-latest`
+280 -31
View File
@@ -1,23 +1,121 @@
# New Profession Proposals
This document proposes 5 new hero professions for Eagle0. Each profession has abilities for both the Eagle (strategic) and Shardok (tactical) game layers.
This document proposes new hero professions for Eagle0. Each profession should matter in both the Eagle strategic layer
and the Shardok tactical layer, and should be earned through a prime stat in the same way existing professions are.
## Current Professions Reference
| Profession | Eagle Ability | Shardok Abilities |
|------------|---------------|-------------------|
| **Mage** | Control Weather | Lightning Bolt, Meteor, Freeze Water, Start Fire (enhanced) |
| **Necromancer** | Start Epidemic | Raise Dead, Fear |
| **Engineer** | (general) | Repair, Fortify, Build Bridge, Reduce (siege) |
| **Paladin** | Alms (prioritized) | Holy Wave |
| **Ranger** | Recon | Scout, Hide (enhanced), Brave Water (enhanced) |
| **Champion** | (general) | Challenge Duel |
Professions are currently gained when a no-profession hero crosses the profession stat threshold and wins a profession
roll. The current stat mapping is:
| Prime Stat | Professions |
|------------|-------------|
| **Strength** | Champion |
| **Agility** | Engineer, Ranger |
| **Wisdom** | Mage |
| **Charisma** | Necromancer, Paladin |
| **Constitution** | None |
The current profession capabilities are:
| Profession | Eagle Ability | Shardok Abilities | Current Niche |
|------------|---------------|-------------------|---------------|
| **Mage** | Control Weather: start/end blizzards and droughts in the current or neighboring province | Lightning Bolt, Meteor, Freeze Water, Start Fire access/enhancement | Strategic weather control and high-impact elemental battlefield effects |
| **Necromancer** | Start Epidemic in the current or neighboring province | Raise Dead, Fear | Attrition pressure, undead creation, morale attack |
| **Engineer** | Improve command recommendation and bonus improvement output | Repair, Fortify, Build Bridge, Reduce siege | Infrastructure, battlefield construction, mechanical support |
| **Paladin** | Alms priority and increased support from food | Holy Wave | Public support, holy area effect, anti-undead flavor |
| **Ranger** | Recon against enemy provinces | Scout, Hide access/enhancement, Brave Water enhancement | Information, stealth, terrain crossing |
| **Champion** | Train command recommendation and training bonus | Challenge Duel | Martial excellence, battalion training, direct hero confrontation |
## Recommendation
Add **Warden** first.
Warden is the cleanest next profession because Constitution is the only hero stat that does not currently unlock any
profession. A Constitution profession also creates a distinctive tactical identity: not another caster, scout, or damage
dealer, but a durable hero who keeps important units alive. That fills a real mechanical gap without forcing a large
rewrite of the profession system.
The first version should be intentionally simple:
- **Prime stat:** Constitution
- **Eagle ability:** Garrison, a defensive province action or passive defense bonus when a Warden is present with enough
vigor; or Custody, a prisoner-control ability for captured heroes
- **Shardok ability:** Guard, a command that marks an adjacent friendly unit until the Warden's next turn; the first
attack against that unit is redirected to the Warden's unit or reduced by a fixed amount
- **Battalion suitability:** Optimal with durable melee infantry; suboptimal with fragile archers or stealth-focused
units if we want a stricter identity
- **Balance direction:** Spend action points and/or vigor to protect another unit, rather than adding free passive
prevention every round
This keeps the profession legible: high-Constitution heroes become the people who can hold formation, anchor a province,
and protect fragile specialists.
---
## Proposed New Professions
### 1. HERALD (Morale & Communication Specialist)
### 1. WARDEN (Defense & Protection Specialist)
**Fantasy:** The stalwart defender who holds the line and protects allies.
**Why it fits now:**
- Gives Constitution a profession path
- Adds a defensive/tank identity that no current profession owns
- Creates counterplay to burst damage, duel pressure, and fragile high-value units
**Eagle Ability: "Garrison"**
- A province with a Warden-led unit gets a defense bonus when attacked
- Alternative active version: spend vigor to fortify the province until next round
- Synergy: works well in border provinces, choke points, and provinces recovering after losses
**Alternative Eagle Ability: "Custody"**
- A Warden can handle captured heroes with extra authority: execute them immediately or move them to a neighboring ruled
province before normal prisoner management
- Passive custody version: each new round, prisoners in a Warden-guarded province become more likely to join the ruling
faction
- Prefer implementing this as a small positive addition to the prisoner's existing `factionBiases` entry for the ruling
faction, rather than changing the prisoner `roundsInType` multiplier from `+5/round` to `+10/round`
- Rationale: changing the multiplier would be retroactive. A prisoner held for ten rounds would immediately receive a
large odds jump the moment a Warden arrived. Accumulating a Warden-specific bias only rewards rounds actually spent
under Warden custody, and the existing faction-bias decay makes the effect fade naturally if the Warden leaves
- Stronger version: if the Warden's side captures enemy heroes during battle but still loses the province, the Warden can
evacuate one captured hero to a neighboring ruled province instead of losing custody
- This should be limited to one captured hero per battle or require a high-vigor Warden, because keeping prisoners after
losing the battle is a major strategic swing
- Best flavor: Wardens are not just defenders of walls, but keepers of oaths, chains, and battlefield custody
**Shardok Ability: "Guard"**
- Target adjacent friendly unit is protected until the Warden's next turn
- First incoming attack against the protected unit is redirected to the Warden's unit or reduced
- Costs action points and cannot target self
- Creates a clear positioning puzzle without introducing another long-range damage button
**Implementation Notes:**
- Add `Warden` to the Scala profession enum and common/client profession enums
- Map Constitution to `Vector(Profession.Warden)` in `HeroStatGainAction`
- Add display names, profession-gained notification copy, tutorial trigger mapping, and headshot bucket support
- Add strategic availability and command handling only after deciding whether Warden uses Garrison, Custody, or both
- For Custody, prefer extending the existing captured-hero/prisoner-management flow instead of creating a parallel
prisoner system
- For passive prisoner conversion, update `NewRoundAction` near the existing unaffiliated-hero `roundsInType` and
`factionBiases` maintenance. If the province is ruled, contains a ruling-faction Warden, and the unaffiliated hero is
a prisoner, add the Warden custody bonus to that prisoner's `factionBiases(rulingFactionId)`.
- For Shardok, start with a single-turn guard status before attempting more complex interception chains
**Current Follow-Up Priorities:**
- Teach the Shardok AI when to use the Warden evacuation command. The command is valuable when the Warden's side has
captured enemy heroes and can secure them by leaving the battle, especially when the battle outcome is uncertain or
trending against that side.
- Add the strategic passive custody bonus: each round, prisoners in a province guarded by a ruling-faction Warden should
get a small accumulated positive bias toward joining that faction. Prefer storing this as an addition to the existing
`factionBiases` entry rather than changing the global `roundsInType` multiplier, so the bonus only reflects rounds
actually spent under Warden custody.
- Make tactical AI more protective of heroes at risk of capture when a Warden may be present. Warden evacuation makes
captured heroes harder to recover, so the AI should treat exposing high-value heroes to capture as more dangerous than
before. This matters even when the AI does not have perfect knowledge that a Warden is in the battle.
### 2. HERALD (Morale & Communication Specialist)
**Fantasy:** The inspiring leader who rallies troops and carries messages across the battlefield.
@@ -32,7 +130,7 @@ This document proposes 5 new hero professions for Eagle0. Each profession has ab
---
### 2. ALCHEMIST (Fire & Transformation Specialist)
### 3. ALCHEMIST (Fire & Transformation Specialist)
**Fantasy:** The mad scientist who manipulates the elements through science, not magic.
@@ -47,21 +145,6 @@ This document proposes 5 new hero professions for Eagle0. Each profession has ab
---
### 3. WARDEN (Defensive Specialist)
**Fantasy:** The stalwart defender who holds the line and protects allies.
**Eagle Ability: "Garrison"**
- A province with a Warden-led unit gets +1 to defense when attacked
- Encourages strategic placement of defensive heroes
**Shardok Ability: "Intercept"**
- Once per turn, when an adjacent friendly unit is attacked, the Warden's unit can take the hit instead
- Creates a "bodyguard" mechanic that protects valuable units
- Costs action points to maintain readiness
---
### 4. INQUISITOR (Anti-Magic & Intelligence)
**Fantasy:** The witch-hunter who counters supernatural threats and uncovers secrets.
@@ -96,8 +179,174 @@ This document proposes 5 new hero professions for Eagle0. Each profession has ab
These professions were designed to:
1. **Fill mechanical gaps**: Warden provides defensive depth, Inquisitor counters magic-heavy strategies
2. **Create counterplay**: Inquisitor vs Mage/Necromancer, Beastmaster vs Rangers (nature vs nature)
3. **Avoid overlap**: Each has a unique niche not covered by existing professions
4. **Support both layers**: Each ability is meaningful in its respective game mode
5. **Enable interesting decisions**: Intercept creates bodyguard tactics, Inspire creates action economy choices
1. **Fill mechanical gaps**: Warden gives Constitution a profession and adds defensive depth
2. **Create counterplay**: Inquisitor vs Mage/Necromancer, Warden vs burst damage, Beastmaster vs beast events
3. **Avoid overlap**: Each profession should own a tactical and strategic niche not covered by existing professions
4. **Support both layers**: Each ability should be meaningful in Eagle and Shardok
5. **Enable interesting decisions**: Guard creates positioning choices, Inspire creates action economy choices, Transmute
creates resource tradeoffs
## Unified Candidate Ranking
This ranking considers every candidate together, regardless of where the idea came from. The main criteria are mechanical
fit, overlap with existing professions, implementation clarity, strategic-layer value, Shardok value, and narrative
payoff.
1. **Warden**: Best immediate fit. It gives Constitution a profession path and creates a distinctive custody/protection
identity.
2. **Quartermaster**: Best strategic depth after Warden. Logistics touches resources, armies, travel, and attrition
without adding another damage specialist.
3. **Envoy**: Best diplomacy/prisoner-system extension. Strong narrative payoff and clear Eagle-side value.
4. **Surgeon**: Best loss-mitigation role. Makes battle aftermath less binary and gives non-magical healing a place.
5. **Herald**: Strong morale and action-economy support. Distinct from Paladin if kept secular and battlefield-command
focused.
6. **Marshal**: Strong army identity, especially for formation play and troop organization, but needs careful separation
from Champion.
7. **Inquisitor**: Useful counterplay against Mage and Necromancer, though anti-magic should not become too narrow.
8. **Pathfinder**: Good campaign movement role, but should focus on routes and terrain rather than replacing Ranger
scouting.
9. **Alchemist**: Good resource and terrain manipulation fantasy, but risks crowding Mage and Engineer unless scoped
around risky transformations.
10. **Spymaster**: Strong flavor and intelligence value, but needs slow strategic effects to avoid replacing Recon.
11. **Artificer**: Fun temporary-device fantasy, but overlaps Engineer unless it owns one-shot preparation rather than
construction.
12. **Beastmaster**: Connects nicely to beast events, but overlaps Ranger unless focused on province events and wild
allies.
13. **Harbinger**: Excellent flavor, but probably later because fear/morale already touches Necromancer and Paladin
space.
The best near-term sequence is likely **Warden**, then **Quartermaster** or **Envoy**. Avoid adding another pure damage
caster until defensive, logistical, diplomatic, morale, and aftermath niches are better represented.
## Additional Candidate Details
These ideas come from looking at open mechanical space in Eagle and Shardok: logistics, loyalty, prisoners, province
events, diplomacy, battlefield control, and non-damage support.
### Marshal
**Fantasy:** The army organizer who turns a pile of units into an actual campaign force.
**Best stat fit:** Strength or Charisma
**Eagle Ability: "Muster"**
- Reorganize, reinforce, or prepare battalions more efficiently than a normal hero
- Could reduce vigor cost for Organize Troops or improve training/armament transfer efficiency
**Shardok Ability: "Command Formation"**
- Adjacent friendly units gain a small defensive or morale bonus while holding formation
- Creates a tactical identity around positioning several units together
**Why it is interesting:** Champion currently owns heroic combat and training, but not army-level coordination. Marshal
would be about disciplined formations rather than duels.
### Quartermaster
**Fantasy:** The logistics expert who keeps armies fed, paid, armed, and moving.
**Best stat fit:** Constitution or Wisdom
**Eagle Ability: "Provision"**
- Move food/gold/supplies with less waste or farther than normal
- Reduce attrition or readiness loss for armies operating away from strong provinces
**Shardok Ability: "Resupply"**
- Restore limited ammunition, repair light damage, or grant a one-turn readiness buff to a nearby unit
**Why it is interesting:** The strategic layer already has meaningful resources. A profession that manipulates logistics
would create strong choices without simply adding another combat spell.
### Envoy
**Fantasy:** The negotiator, hostage-broker, and oath-maker.
**Best stat fit:** Charisma
**Eagle Ability: "Parley"**
- Improve diplomacy outcomes, ransom terms, prisoner returns, or truce/alliance offer odds
- Could reduce the risk of ambassadors being imprisoned or create better return-prisoner rewards
**Shardok Ability: "Demand Surrender"**
- Attempt to force a damaged or isolated enemy unit to flee, with odds based on charisma and battlefield state
**Why it is interesting:** Eagle has relationships, ransoms, truces, alliances, and prisoner choices. Envoy would make
diplomacy feel like a profession rather than only a menu action.
### Spymaster
**Fantasy:** The patient handler of informants, rumors, sabotage, and false trails.
**Best stat fit:** Wisdom or Charisma
**Eagle Ability: "Infiltrate"**
- Plant a delayed intelligence effect in an enemy province, revealing troop movements or weakening support
- Could counter or complement Ranger recon without duplicating it
**Shardok Ability: "Sabotage"**
- Before or during battle, reduce one enemy unit's readiness, movement, or first action effectiveness
**Why it is interesting:** Ranger is field reconnaissance. Spymaster can be slower, political, and province-focused.
### Surgeon
**Fantasy:** The healer who saves lives after the dramatic part of the story is over.
**Best stat fit:** Wisdom or Constitution
**Eagle Ability: "Triage"**
- Reduce hero vigor loss, casualty severity, or prisoner death/execution fallout after battles
- Could improve recovery in provinces with many wounded heroes or battered battalions
**Shardok Ability: "Stabilize"**
- Prevent a nearby friendly hero unit from being captured or destroyed once per battle, leaving it routed or exhausted
instead
**Why it is interesting:** Paladin has holy support, but not mundane medical recovery. Surgeon creates a grounded support
role that can make losses less binary.
### Artificer
**Fantasy:** The maker of rare devices, siege tools, lenses, traps, and battlefield instruments.
**Best stat fit:** Wisdom or Agility
**Eagle Ability: "Prototype"**
- Invest gold and vigor to create a temporary province or battalion enhancement
- Examples: better siege readiness, scouting lenses, defensive traps, or weatherproof stores
**Shardok Ability: "Deploy Device"**
- Place a one-use trap, barricade, signal flare, or field tool on a nearby hex
**Why it is interesting:** Engineer currently owns building and repair. Artificer can own temporary inventions and
one-shot tactical preparation.
### Harbinger
**Fantasy:** The terrifying omen-bearer whose arrival changes morale before the first blow lands.
**Best stat fit:** Charisma or Wisdom
**Eagle Ability: "Portent"**
- Lower enemy support, increase unrest, or amplify the psychological effect of victories and executions
- Could be risky: fear-based rule damages diplomacy or loyalty if overused
**Shardok Ability: "Dread Standard"**
- Enemies near the Harbinger suffer morale penalties or worse flee odds
**Why it is interesting:** Necromancer has supernatural fear, but Harbinger could be political and symbolic rather than
undead-focused.
### Pathfinder
**Fantasy:** The guide who knows hidden passes, river crossings, and winter roads.
**Best stat fit:** Agility or Constitution
**Eagle Ability: "Find Passage"**
- Move armies or heroes through difficult terrain, winter, blizzards, or river-heavy borders with lower penalties
- Could create one-turn temporary travel links between neighboring provinces under specific conditions
**Shardok Ability: "Open Route"**
- Let a nearby unit ignore one terrain penalty or cross a difficult hex safely this turn
**Why it is interesting:** Ranger owns stealth and scouting. Pathfinder owns movement and campaign geography.
+149
View File
@@ -0,0 +1,149 @@
# Postgres History Migration Plan
## Goal
Evaluate and, if the measurements support it, migrate production game history persistence from local SQLite plus S3 base
and delta files to a managed Postgres database.
This is not just a mechanical port. The important product requirement is that battle commands, especially Shardok map
clicks, show results to the player as quickly as possible while still keeping the chance of lost results acceptably low.
## Proposed Direction
Use Postgres as the authoritative production store. Local SQLite should remain useful for development, tests, and rollback
during rollout, but production should not have two long-lived authoritative stores for the same game.
Start with a small DigitalOcean Managed Postgres instance in the same region and VPC as Eagle. The $15 single-node plan is
reasonable for early measurement and staging. If the design works, production can move to a more reliable plan when the
availability requirements justify it.
## Phase 1: Measure First
Before porting the full history implementation, add a benchmark or admin utility that runs from the Eagle deployment
environment against the candidate Postgres database.
Measure at least:
- single action-result append transaction latency
- batch append latency for 25 and 100 action results
- latest snapshot lookup
- replay from a snapshot to a target index
- `since` and stream-update query latency
- Shardok battle result append latency
- p50, p95, p99, and max latency
- async queue lag, if an async persistence prototype is included
Useful initial exit criteria:
- Strategic action-result writes should be comfortably below 50 ms p95.
- Shardok synchronous persistence should be below roughly 20-30 ms p95 to stay on the click-to-animation path.
- If Shardok sync writes are above that, use an async Shardok persistence design rather than blocking player feedback.
## Phase 2: Schema Prototype
Add Postgres configuration and a prototype schema that maps the current SQLite history model.
Expected tables:
- `games`, for game-level metadata and backend bookkeeping
- `action_results`, for ordered strategic action results
- per-entity decomposition tables currently written by the SQLite dispatchers
- `state_snapshots`, for replay anchors
- Shardok battle result tables, including battle id, sequence number, command/result payloads, and player metadata
- `schema_migrations`, unless the migration mechanism is handled outside the app
Prefer batched inserts and batched reads. The current SQLite implementation can afford some local-file patterns that will
be too chatty over a network.
## Phase 3: Implement Postgres History
Implement a `FullGameHistory` backend backed by Postgres. Keep the existing SQLite backend available behind a config flag
during rollout.
Core methods to support:
- `count`
- `last`
- `withNewResults`
- `stateAfter`
- `since`
- `sinceDate`
- round-local recent-result queries
- Shardok result writes and reads
The implementation should preserve action ordering and idempotency. If retries are possible, writes should include stable
keys or sequence ranges so replaying a write request cannot duplicate results.
## Phase 4: Decide Shardok Durability Mode
Shardok latency is the place where this migration can most visibly hurt the player. There are two viable modes.
### Option A: Synchronous Shardok Writes
Use this if measured Postgres latency is low enough. The command path writes results to Postgres before telling the client
about them.
This is the simplest durability model, but only acceptable if it keeps click-to-animation latency low.
### Option B: Async Shardok Writes
Use this if synchronous Postgres writes add noticeable latency.
The model:
- apply battle results to authoritative in-memory game state immediately
- send client updates immediately after the ordered in-memory apply
- enqueue ordered persistence batches in the same order they were applied
- persist batches with `battle_id`, sequence range, payload hash, and payload
- track `lastAppliedSeq` and `lastPersistedSeq`
- retry failed writes until they succeed
- alert when persistence lag exceeds a small threshold
- block or degrade new commands if the queue grows too large
- force a synchronous checkpoint before battle end reconciliation or strategic-layer handoff
- drain the queue during graceful shutdown
This creates a small crash-loss window, but only for rare crashes that happen after the player sees a result and before
the async write completes. That tradeoff may be better than adding network persistence latency to every tactical command.
## Phase 5: Rollout
Add a backend flag such as:
```text
EAGLE_HISTORY_BACKEND=sqlite|postgres
```
Suggested rollout:
1. Run benchmarks from the deployed environment.
2. Enable Postgres in staging for new games.
3. Run warmup games and Shardok battle smoke tests.
4. Enable Postgres for new production games.
5. Migrate old games either lazily on first open or through an admin-triggered migration job.
6. Keep SQLite/S3 rollback available until enough new-game production time has passed.
If Postgres is unavailable, commands should not be acknowledged as durable. Either hold/retry them before applying, or
reject them clearly. Do not show a result to the client while pretending it has already been durably stored unless that is
an explicit async-durability path with lag monitoring.
## Phase 6: Cleanup
After production confidence:
- remove production dependence on S3 base/delta history files
- keep object storage for backups, exports, or emergency archives
- keep SQLite for local development and tests if it remains useful
- document restore, migration, and rollback procedures
## Open Questions
- What exact durability guarantee do we want for "the client already saw this result"?
- What Shardok persistence latency is acceptable in real play?
- Should old games migrate lazily or through a bulk/admin process?
- When should the production database move from single-node to HA?
- What should the player experience be if Postgres is reachable but slow?
## Recommended Next PR
The next implementation PR should add a Postgres benchmark/admin utility and configuration plumbing, not the full port.
That gives us real latency numbers from the Eagle deployment environment before committing to the larger migration.
+4 -4
View File
@@ -486,7 +486,7 @@ jobs:
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
lfs: false
@@ -517,7 +517,7 @@ jobs:
runs-on: ubuntu-latest
needs: []
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
lfs: false
@@ -532,7 +532,7 @@ jobs:
uses: actions/cache@v3
with:
path: ~/.cache/bazel
key: bazel-linux-${{ hashFiles('MODULE.bazel', 'WORKSPACE') }}
key: bazel-linux-${{ hashFiles('MODULE.bazel', 'MODULE.bazel.lock', 'WORKSPACE') }}
- name: Build Shardok server
run: bazel build -c opt //src/main/cpp/net/eagle0/shardok:shardok-server
@@ -555,7 +555,7 @@ jobs:
needs: [build-eagle, build-shardok]
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Deploy to DigitalOcean
uses: appleboy/ssh-action@v1.0.0
+1 -1
View File
@@ -136,7 +136,7 @@ Create a tool to programmatically regenerate the Eagle strategic map images with
| `Assets/Eagle/Materials/*.png` | Replaced province masks |
| `Assets/Eagle/map_bw_labels.png` | Either: regenerated, or removed if using dynamic labels |
| `province_map.tsv` | Add centroid columns |
| `Assets/Gameplay.unity` | Add ProvinceLabelsController, label prefab instances |
| `Assets/Scenes/Eagle.unity` | Add ProvinceLabelsController, label prefab instances |
---
+8 -1
View File
@@ -8,6 +8,7 @@ The corrected all-40 run uses:
- `max_rounds = 31`
- `random_seed = 1`
- iterative-deepening timeout propagation fix from PR #6923
- AI setup placement
- forced single-option commands
- defender scoring fixed to `STANDARD` for every row
@@ -33,9 +34,11 @@ An AI-vs-AI smoke check also reruns a subset where neither side used faction `3`
## Current Results
These artifacts are the current baseline for future scoring comparisons.
| Dataset | Valid battles | Invalid rows | Attacker wins | Defender wins | Notes |
| --- | ---: | ---: | ---: | ---: | --- |
| All-40 expected-impact archery baseline | 40 | 0 | 18 | 22 | `standard_scoring_all40_post_bugfix_baseline.*`; regenerated after promoting experiment 34 to STANDARD |
| All-40 timeout-fix STANDARD baseline | 40 | 0 | 18 | 22 | `standard_scoring_all40_post_bugfix_baseline.*`; regenerated after fixing iterative-deepening timeout score propagation |
| All-40 real saved game | 40 known | 0 unknown | 19 | 21 | `standard_scoring_all40_real_vs_sim.*`; sim matches 39/40 known winners |
| AI-vs-AI smoke | 31 | 0 | 10 | 21 | 31/31 winner-side matches |
@@ -73,3 +76,7 @@ Expected-impact archery replaced the old flat archery-position value in STANDARD
shoot now gets half of the expected volley impact; a unit that is merely positioned for a future
volley gets one quarter. Promoting that behavior flips `6248` to match the real AI-vs-AI attacker
win without introducing an AI-vs-AI winner-side miss in the current saved-battle baseline.
The latest baseline refresh was generated after the iterative-deepening evaluator stopped letting
timeout fallback scores compete with completed command evaluations. Future STANDARD-vs-EXPERIMENTAL
comparisons should compare against these checked-in timeout-fix baseline artifacts.
+130 -234
View File
@@ -1,267 +1,163 @@
# Tutorial Battle System
This document describes the tutorial battle system for first-time players. The system provides a scripted introductory battle that teaches combat basics while telling a story.
This document describes the current opening tutorial battle. It is based on the live implementation in `TutorialGameCreation.scala`, `TutorialBattleController.cpp`, `ShardokGamesManager.cpp`, `ResolveBattleAction.scala`, and the Unity dialogue scripts.
For the Unity tutorial UI/dialogue architecture, see `TUTORIAL_SYSTEM.md`. For copy and content notes, see `TUTORIAL_CONTENT.md`.
---
## Overview
## Current Flow
When a new player starts their first game in tutorial mode, they experience:
When a player creates a tutorial game at the default `OpeningBattle` phase:
1. **Narrative Intro** - Story screens introducing the scenario
2. **Immediate Battle** - Skip strategic map, start directly in combat
3. **Scripted Flee** - Enemy flees when player is "on the ropes"
4. **Reinforcements** - Allied heroes arrive mid-battle
5. **Heroes Join** - Reinforcement heroes join player's faction after battle
1. `TutorialGameCreation.createTutorialGame()` creates a `GameType.Tutorial(TutorialPhase.OpeningBattle)` game starting in `RoundPhase.BattleRequest`.
2. Province 14, **Onmaa**, belongs to Sadar Rakon's faction and already has a defending army.
3. Bregos Fyar's faction has a hostile attacking army, led by Ikhaan Tarn, already moving from province 31 to Onmaa.
4. `RequestBattlesAction` immediately creates the Shardok battle and tags tutorial reinforcement hero IDs `100` and `101`.
5. `GamesManager` stores the generated `TutorialBattleConfig` for this game and passes it to Shardok when the battle starts.
6. Shardok creates the battle with the configured timed reinforcement events.
7. Unity plays the active dialogue scripts from `tutorial_strategic.json` and `tutorial_battle.json`.
There is also a tutorial phase-start path. Starting after `OpeningBattle` applies precomputed tutorial setup results from `TutorialPhaseResultsLoader`; this is why the Unity post-battle trigger logic handles a "battle was never observed" auto-resolve path.
---
## Battle Configuration
## Battle Setup
### Defender (Player - John Ranil)
- **Heroes**: John Ranil + 2 random vassals (3 total)
- **Units**:
- 2x Light Infantry (300 troops each, 60 training/armament)
- 1x Longbowmen (200 troops, 60 training/armament)
- **Province**: 14 (Onmaa) with 40 support, 500 gold, 3000 food
- **Restriction**: Cannot flee (must defend)
### Defender: Sadar Rakon's Faction
### Attacker (Ikhaan Tarn)
- **Heroes**: Ikhaan Tarn + 2 sworn brothers (3 total)
- **Units**:
- 1x Heavy Cavalry (600 troops, 80 training/armament)
- 1x Heavy Infantry (500 troops, 80 training/armament)
- 1x Longbowmen (300 troops, 80 training/armament)
- **Origin Province**: 31
Configured in `src/main/resources/net/eagle0/eagle/tutorial_parameters.json`:
### Flee Trigger
Attacker flees when ANY of these conditions are met:
- Player loses 1 unit
- 5 rounds have passed
- **Faction ID:** 3
- **Province:** 14, Onmaa
- **Starting support:** 23
- **Starting resources:** 50 gold, 4000 food
- **Heroes:** Sadar Rakon, Old Marek the Learned, Agamemnon
- **Battalions:**
- Rakon's Loyalists: Light Infantry, 529 size, 80 training, 75 armament
- Onmaa Defenders: Light Infantry, 311 size, 80 training, 60 armament
- Hunters of the Steppe: Longbowmen, 478 size, 80 training, 60 armament
### Reinforcements
When Tarn flees, these heroes arrive as player reinforcements:
- Elena Fyar
- Hedrick
- The Boulder
### Attacker: Bregos Fyar's Faction
The opening attacking army is configured under Bregos Fyar's `attackingArmies`:
- **Faction ID:** 2
- **Origin province:** 31
- **Destination province:** 14, Onmaa
- **Heroes:** Ikhaan Tarn, Tall Edgtheow, Waylaid Julius, Luke the Prank-tricker
- **Battalions:**
- Doomriders: Heavy Cavalry, 592 size, 80 training, 80 armament
- The Shardok's Guard: Heavy Infantry, 507 size, 80 training, 80 armament
- Bowmen of Nikemi: Longbowmen, 291 size, 80 training, 80 armament
- Swift Sabres: Light Cavalry, 450 size, 80 training, 80 armament
Tarn's unit is made visible to the defender from the start through `TutorialBattleConfig.initial_visibilities`.
---
## Implementation Architecture
## Reinforcements
### Phase 1: Narrative Intro System
The current tutorial config has two timed reinforcement events. It does not currently configure a scripted Tarn flee event.
**Proto Changes:**
- `game_state_view.proto`: Added `TutorialNarrativeScreen` message and `pending_narrative_screens` field
- `game_parameters.proto`: Added `TutorialNarrativeScreen` and `tutorial_narrative_screens` field
| Event ID | Trigger | Hero ID | Hero | Battalion |
|----------|---------|---------|------|-----------|
| `elena_reinforcement_round_5` | End of round 5 or later | 101 | Elena Fyar | Fyar's Vanguard, Heavy Infantry, 535 size, 75 training, 75 armament |
| `ranil_reinforcement_round_7` | End of round 7 or later | 100 | John Ranil | Ranil's Riders, Heavy Cavalry, 458 size, 80 training, 80 armament |
**Unity Client:**
- `NarrativeScreenController.cs`: Modal screen with title, body text, optional image
- `EagleGameController.cs`: Checks for pending narrative screens on game start
Shardok creates reinforcement units at battle creation time with `PENDING_REINFORCEMENT` status. `TutorialBattleController` activates them when their event triggers and returns a `TUTORIAL_REINFORCEMENTS_ARRIVED` action result. Unity maps that action to profession-specific dialogue triggers:
**Narrative Content** (defined in `tutorial_game_parameters.json`):
```
Screen 1: "The Engineer of Onmaa" - Player backstory
Screen 2: "The Traitor Arrives" - Tarn's attack
Screen 3: "Defend Your Home" - Call to action
```
- `tutorial_reinforcement_paladin` for Elena Fyar
- `tutorial_reinforcement_engineer` for John Ranil
### Phase 2: Auto-Start Battle
**Configuration:**
- `tutorial_game_parameters.json`: Contains `tutorialBattle` config with attacker, target, flee conditions
- `game_parameters.proto`: `TutorialBattleConfig` message
**Game Creation:**
- `NewGameCreation.scala`: `setupTutorialBattle()` method creates:
- `HostileArmyGroup` with `Attacking` status (bypasses decision phase)
- `Army` for defender
- Both added to target province
**Battle Creation:**
- When round advances to `BattleRequest` phase, `RequestBattlesAction` automatically creates the `ShardokBattle`
### Phase 3: Scripted Flee Mechanism
**C++ Component:**
- `TutorialBattleController.hpp/cpp`: Controller for scripted battle logic
**Key Methods:**
```cpp
// Check all events and execute triggered actions (called at end of each round)
std::vector<ActionResult> CheckAndExecuteEvents(
const GameStateW& state,
const SettingsGetter& settings,
const std::shared_ptr<RandomGenerator>& randomGenerator);
```
**Integration:**
- `ShardokEngine.cpp`: Calls `CheckAndExecuteEvents()` at end of each round in `HandlePlayerTurnEnd()`
- Events fire in config order, each event fires at most once
### Phase 4: Mid-Battle Reinforcements
**Action Types** (in `action_type.proto`):
- `TUTORIAL_ENEMY_FLED = 78`
- `TUTORIAL_REINFORCEMENTS_ARRIVED = 79`
**Implementation (TODO):**
- When scripted flee triggers, create reinforcement units in defender's reserves
- Units have `UnitStatus_RESERVE_UNIT` status
- Reinforcements enter battle on subsequent turns
### Phase 5: Tutorial Popups
**Content Definitions** (in `TutorialContentDefinitions.cs`):
- `tutorial_battle_started`: "Defend your province!"
- `tutorial_enemy_fled`: "The enemy retreats!"
- `tutorial_reinforcements_arrived`: "Allies have arrived!"
- `tutorial_battle_victory`: Victory celebration
**Trigger Integration:**
- `TutorialTriggerRegistry.cs`: Handles tutorial action types from `ActionResultView`
- `ShardokGameController.cs`: Detects tutorial events and triggers popups
### Phase 6: Post-Battle Hero Joining
**Implementation (TODO):**
- `ResolveBattleAction.scala`: Check if battle was tutorial and defender won
- Create `TutorialHeroJoined` action results adding reinforcement heroes to player faction
- Set appropriate loyalty, vigor, location for joined heroes
### Phase 7: Post-Battle Strategic Dialogue
After the battle ends and the player returns to the strategic map, narrative dialogues guide them through the next steps.
**Dialogue 1: Battle Aftermath** (trigger: `tutorial_battle_ended`)
- Old Marek reflects on the close call
- Tarn has vanished — rumors of sorcery or escape, no one knows
- Captured lieutenants: they followed orders, not to blame for Tarn's madness
- Player should try to recruit them or hold them
- No instruction text needed — Handle Captured Heroes UI is self-explanatory
**Dialogue 2: Rebuild Support** (trigger: `tutorial_rebuild_support`)
- Fires when available commands no longer include HandleCapturedHeroCommand
- Marek explains the province needs support rebuilt after the battle
- Engineers (John Ranil) can **Improve** the province — build infrastructure, develop economy
- Paladins (Elena Fyar) can **Give Alms** — distribute food to win hearts
- Instructions highlight **Improve** and **Give Alms** buttons
- Goal: get Support to **40** before January for tax revenue
**Implementation:**
- Dialogues defined in `tutorial_strategic.json`
- `tutorial_battle_ended`: fire when tutorial battle is removed from `RunningShardokGameModels`
- `tutorial_rebuild_support`: fire when available commands no longer include `HandleCapturedHeroCommand`
- Register `ImproveButton` and `AlmsButton` as highlight targets in `TutorialTargetRegistry`
See `TUTORIAL_CONTENT.md` for full dialogue text.
The reinforcement action includes an `attacker_starting_position_index`; Shardok resolves that to currently open map positions and starts a reinforcement placement phase.
---
## File Summary
## Scripted Event Controller
`TutorialBattleController` is a generic event-driven controller, even though the current tutorial battle only uses timed reinforcements.
Supported trigger types:
- `after_round`
- `units_lost`
- `damage_taken`
- `unit_killed`
Supported action types:
- `flee`
- `reinforcements`
Events are checked at the end of each round, evaluated in config order, and each `event_id` fires at most once.
---
## Battle Resolution
`RequestBattlesAction` marks tutorial opening battles with `reinforcementHeroIds = Set(100, 101)`. `ResolveBattleAction` uses that metadata so reinforcement heroes are accepted when battle results return, even though they were not part of the original defending army.
After the opening battle resolves, `ResolveBattleAction` applies `TutorialTarnDisappearsAction` for `GameType.Tutorial(TutorialPhase.OpeningBattle)`. Tarn is removed from captured/unaffiliated province state and from his faction's leaders list, matching the post-battle dialogue that he has vanished.
`TutorialBattleAutoResolve` provides a synthetic defender victory for tutorial setup paths that skip past the opening battle. In that synthetic result:
- The defender wins.
- Reinforcement heroes 100 and 101 survive.
- Tarn is marked outlawed/escaped.
- Other attacker heroes are captured.
- Defender battalions take about 30% casualties.
- Attacker battalions are destroyed.
---
## Unity Dialogue Hooks
Strategic tutorial dialogue:
- `game_started`: opening Onmaa monologue, ending with `FightButton` highlighted.
- `tutorial_battle_ended`: post-battle aftermath.
- `tutorial_rebuild_support`: support rebuilding guidance after captured-hero handling is done.
Battle tutorial dialogue:
- `shardok_placement_started`: placement and unit overview.
- `shardok_battle_running`: first player turn guidance.
- Ability/terrain triggers such as `archery_available`, `melee_available`, `ability_charge_available`, `start_fire_available`, `thunderstorm`, `duel_available`, `hide_available`, and `engineer_near_enemy`.
- Reinforcement triggers `tutorial_reinforcement_paladin` and `tutorial_reinforcement_engineer`.
- Capture triggers `enemy_hero_captured` and `friendly_hero_captured`.
- `shardok_battle_reset`: replay dialogue after a battle reset.
---
## Files To Check First
### New Files
| File | Purpose |
|------|---------|
| `TutorialBattleController.hpp/cpp` | C++ scripted flee and reinforcement logic |
| `NarrativeScreenController.cs` | Unity narrative screen display |
### Modified Files
| File | Changes |
|------|---------|
| `tutorial_game_parameters.json` | Battle configuration and narrative content |
| `game_state_view.proto` | Narrative screens field |
| `game_parameters.proto` | Tutorial battle config |
| `player_info.proto` | Tutorial battle flags for Shardok |
| `action_type.proto` | Tutorial action types |
| `NewGameCreation.scala` | Auto-start battle setup |
| `EagleGameController.cs` | Narrative display integration |
| `TutorialContentDefinitions.cs` | Battle tutorial content |
| `TutorialTriggerRegistry.cs` | Tutorial event triggers |
---
## Remaining Implementation
### Disable Player Flee
In `FleeCommandFactory.cpp`:
- Check if tutorial mode is enabled
- If defender, don't offer flee commands
### Reinforcement Units
In `TutorialBattleController::ExecuteScriptedFlee()`:
- Create `Unit` objects for Elena Fyar, Hedrick, The Boulder
- Add to defender's reserve units
- Generate `TUTORIAL_REINFORCEMENTS_ARRIVED` action
### Post-Battle Hero Joining
In Eagle's battle resolution:
- Detect tutorial battle completion
- Create heroes in player's faction with proper stats
- Generate notification action results
| `src/main/resources/net/eagle0/eagle/tutorial_parameters.json` | Tutorial map, factions, starting provinces, armies |
| `src/main/scala/net/eagle0/eagle/service/new_game_creation/TutorialGameCreation.scala` | Opening battle setup and `TutorialBattleConfig` creation |
| `src/main/protobuf/net/eagle0/common/tutorial_battle_config.proto` | Scripted event config schema |
| `src/main/cpp/net/eagle0/shardok/server/ShardokGamesManager.cpp` | Adds pending reinforcement units and applies initial visibility |
| `src/main/cpp/net/eagle0/shardok/library/tutorial/TutorialBattleController.cpp` | Evaluates scripted events and creates Shardok action results |
| `src/main/scala/net/eagle0/eagle/library/actions/impl/action/RequestBattlesAction.scala` | Creates the opening battle and tags reinforcement hero IDs |
| `src/main/scala/net/eagle0/eagle/library/actions/impl/action/ResolveBattleAction.scala` | Resolves reinforcements and applies Tarn disappearance |
| `src/main/scala/net/eagle0/eagle/service/tutorial/TutorialBattleAutoResolve.scala` | Synthetic result for phase-start paths after the opening battle |
| `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Tutorial/Triggers/TutorialTriggerRegistry.cs` | Converts strategic and tactical events into dialogue triggers |
| `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Resources/Dialogues/tutorial_battle.json` | Active battle dialogue content |
---
## Testing Checklist
- [ ] New user sees narrative screens before battle
- [ ] Battle starts immediately after narratives (no strategic map)
- [ ] Player has correct units (3 heroes, 3 battalions)
- [ ] Attacker has correct units (3 heroes, 3 battalions)
- [ ] Player cannot flee
- [ ] Tarn flees after player loses 1 unit
- [ ] Tarn flees after 5 rounds (if no units lost)
- [ ] Reinforcements appear when Tarn flees
- [ ] Tutorial popups appear at correct moments
- [ ] Reinforcement heroes join faction after battle victory
---
## Configuration Reference
### tutorial_game_parameters.json
```json
{
"tutorialBattle": {
"attackerFactionHead": "Ikhaan Tarn",
"targetProvinceId": 14,
"fleeAfterDefenderUnitsLost": 1,
"fleeAfterRounds": 5,
"reinforcements": ["Elena Fyar", "Hedrick", "The Boulder"]
},
"tutorialNarrativeScreens": [
{ "title": "...", "bodyText": "...", "imagePath": "" }
]
}
```
### TutorialBattleConfig Proto (Shardok)
```protobuf
message TutorialBattleConfig {
bool enabled = 1;
repeated TutorialEvent events = 2;
repeated UnitInitialSize initial_sizes = 3; // For damage tracking
}
message TutorialEvent {
string event_id = 1;
TutorialTrigger trigger = 2;
TutorialAction action = 3;
}
message TutorialTrigger {
oneof trigger_type {
RoundTrigger after_round = 1;
UnitsLostTrigger units_lost = 2;
DamageTakenTrigger damage_taken = 3;
UnitKilledTrigger unit_killed = 4;
}
}
message TutorialAction {
oneof action_type {
FleeAction flee = 1;
ReinforcementsAction reinforcements = 2; // Uses CommonUnit
}
}
```
- [ ] Tutorial game starts in BattleRequest and creates an Onmaa battle immediately.
- [ ] Opening strategic dialogue highlights the Fight button.
- [ ] Tarn's unit is visible to the defender from battle start.
- [ ] Placement dialogue fires during setup.
- [ ] First-turn battle-running dialogue fires on the player's first active turn.
- [ ] Elena Fyar arrives from the configured round-5 event.
- [ ] John Ranil arrives from the configured round-7 event.
- [ ] Reinforcement arrival dialogues fire once per hero.
- [ ] Battle reset clears battle dialogue completion for replay.
- [ ] Post-battle aftermath fires after the battle leaves `RunningShardokGameModels`.
- [ ] Rebuild-support dialogue waits until captured-hero handling and visible notifications are done.
- [ ] Starting a tutorial at a later phase uses precomputed setup and still reaches coherent strategic dialogue state.
+13 -13
View File
@@ -1,6 +1,8 @@
# Tutorial Content Guide
This document defines all tutorial content. Edit this to refine the text, then update `TutorialContentDefinitions.cs` to match.
This document describes tutorial content. The active tutorial experience is driven by the narrative dialogue JSON files under `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Resources/Dialogues/`.
The older step-based modal/overlay content in `TutorialContentDefinitions.cs` is currently dormant: `RegisterAll()` returns before registering any sequences. Keep that in mind when editing this file; changing `TutorialContentDefinitions.cs` will not affect the live tutorial until the early return is removed and the overlap with dialogue triggers is audited.
---
@@ -181,17 +183,16 @@ These fire after the first tutorial battle ends and the player returns to the st
>
> Any of our heroes can develop the land or give alms to the people — nothing wins hearts faster than a full belly. Engineers like John Ranil are especially effective at improving a province, and paladins like Elena Fyar are the best at winning hearts. Either way, we need the people's support before the tax collectors come round in January.
**Instructions:** Use **Improve** and **Give Alms** to raise Support. You must have at least **40** support in the province by January in order to collect gold and food in taxes.
**Instructions:** John Ranil's step highlights `ImproveProvinceButton` and explains **Improve**. Elena Fyar's step highlights `GiveAlmsButton` and explains **Give Alms**. You must have at least **40** support in the province by January in order to collect gold and food in taxes.
**Highlight targets:** `SupportField`
**Highlight targets:** `ImproveProvinceButton`, `GiveAlmsButton`
### Implementation Notes
- Both dialogues go in `tutorial_strategic.json`
- Need new trigger events: `tutorial_battle_ended` and `tutorial_rebuild_support`
- `tutorial_battle_ended` should fire when the tutorial battle is removed from `RunningShardokGameModels` in `EagleGameModel.ApplyGameStateViewDiff`
- `tutorial_rebuild_support` should fire when available commands no longer include `HandleCapturedHeroCommand` (i.e. the captured heroes phase is done and the player has regular commands available)
- The "rebuild" dialogue should highlight the Improve and Give Alms buttons in the command panel (requires registering them as highlight targets)
- Both dialogues are defined in `tutorial_strategic.json`.
- `tutorial_battle_ended` fires from `TutorialTriggerRegistry.CheckTutorialBattleEnded()` when the opening battle is gone and strategic commands are available. It also handles the server-side auto-resolve path where the Unity client never observed a running Shardok model.
- `tutorial_rebuild_support` fires from `TutorialTriggerRegistry.CheckTutorialRebuildSupport()` when available commands no longer include `HandleCapturedHeroCommand` and the notification panel has no visible info.
- `ImproveProvinceButton` and `GiveAlmsButton` are runtime command-button targets registered by `CommandButtonPanelController`.
---
@@ -210,11 +211,10 @@ These fire after the first tutorial battle ends and the player returns to the st
## Adding New Tutorials
1. Add entry to this document
2. Update `TutorialContentDefinitions.cs`:
- For onboarding: add to `CreateOnboardingSequence()`
- For contextual: add to `RegisterStrategicTutorials()` or `RegisterTacticalTutorials()`
3. Ensure trigger event exists in `TutorialTriggerRegistry.cs`
4. Test the flow
2. For active tutorial content, add or edit a script in `Assets/Resources/Dialogues/tutorial_strategic.json` or `tutorial_battle.json`.
3. Ensure the trigger event exists in `TutorialTriggerRegistry.cs` or is fired directly by the relevant controller.
4. If reviving the dormant step-UI system, remove the early return in `TutorialContentDefinitions.RegisterAll()` only after auditing duplicate trigger coverage with the dialogue JSON.
5. Test the flow.
---
+16 -11
View File
@@ -3,7 +3,7 @@
This document describes how the Unity client's tutorial system is wired together: the classes involved, the trigger catalog, the step lifecycle, and the expected first-session flow. It complements two existing docs:
- **`TUTORIAL_CONTENT.md`** — human-facing copy for tutorial steps and dialogues
- **`TUTORIAL_BATTLE_SYSTEM.md`** — the scripted first-battle scenario (Tarn vs. John Ranil)
- **`TUTORIAL_BATTLE_SYSTEM.md`** — the scripted opening battle at Onmaa
It also overlaps slightly with the in-tree `Assets/Tutorial/TUTORIAL_PLAN.md`, which is an older implementation plan.
@@ -20,12 +20,13 @@ There are **two parallel subsystems** that share the same trigger plumbing:
| Step-based tutorial UI (modals, overlays, hints) | **Dormant** | `Tutorial/TutorialManager.cs`, `Tutorial/UI/*`, `Tutorial/Content/*` |
| Narrative dialogue system | **Live** | `Tutorial/Dialogue/*`, `Resources/Dialogues/*.json` |
`TutorialContentDefinitions.RegisterAll()` returns early at line 17 with the comment *"Old tutorial content suppressed — replaced by narrative dialogue system."* As a result:
`TutorialContentDefinitions.RegisterAll()` returns early with the comment *"Old tutorial content suppressed — replaced by narrative dialogue system."* As a result:
- `OnboardingSequence` is never assigned, so `StartOnboarding()` no-ops at the "No onboarding sequence assigned" branch.
- No contextual tutorial sequences are registered with the trigger registry.
- The trigger registry still fires events normally, but nothing on the step-UI side is listening.
- `DialogueManager` consumes those same events and matches them against scripts in `Resources/Dialogues/` (`tutorial_strategic.json`, `tutorial_battle.json`).
- Dialogue completion is persisted per game through `TutorialDialogueProgressStore`, so completed scripts do not replay after a reconnect or scene reload for the same game.
**In practice today, the entire active tutorial experience runs through the dialogue system.** The step-UI machinery is preserved for future use — content definitions still exist below the early return in `TutorialContentDefinitions.cs` for reference.
@@ -37,6 +38,7 @@ There are **two parallel subsystems** that share the same trigger plumbing:
- **`Tutorial/TutorialManager.cs`** — Singleton. Owns `OnboardingSequence`, the active step queue, the trigger registry, and the dialogue manager handle. Initialized by `EagleGameController.SetUpGame()` and `ShardokGameController.SetUpGame()`.
- **`Tutorial/TutorialState.cs`** — PlayerPrefs persistence. Tracks `OnboardingCompleted`, `OnboardingStepReached`, completed sequence IDs (HashSet for O(1) lookup), dismissed hints, and a global "tutorials disabled" preference.
- **`Tutorial/TutorialTargetRegistry.cs`** — Maps string IDs to `RectTransform`s for highlighting. Static targets are Inspector-assigned (`ProvinceInfoPanel`, `SupportField`, `CommitButton`, etc.); dynamic targets register at runtime (e.g., hero rows from `HeroesAndBattalionsPanelController`).
- **`Tutorial/TutorialDialogueCoordinator.cs`** — Recomputes durable dialogue triggers from the current strategic model and sends them directly to `DialogueManager`. This covers reconnects, phase-start tutorial games, and strategic beats that should be recoverable from state.
### Triggers
- **`Tutorial/Triggers/TutorialTriggerRegistry.cs`** (~1100 lines) — Routes game events to both the step UI (when sequences are registered) and `DialogueManager`. Maintains one-shot session flags so the same first-encounter trigger doesn't fire twice.
@@ -94,14 +96,14 @@ Panels can be anchored via `TutorialPanelAnchor` (`Center` / `Left` / `Right` /
## Trigger Catalog
All triggers are raised via `TutorialTriggerRegistry`. Today these flow to `DialogueManager.TriggerDialogue()` and are matched against the dialogue JSON; if step-UI sequences are re-registered, they will also route there. File:line citations are for the registry unless noted; line numbers may drift.
Most event-style triggers are raised via `TutorialTriggerRegistry`. Durable strategic dialogue beats are also recomputed by `TutorialDialogueCoordinator` on each model update and sent directly to `DialogueManager.TriggerDialogue()`. Both paths match against the dialogue JSON; if step-UI sequences are re-registered, only the registry path will route to them. File:line citations are for the registry unless noted; line numbers may drift.
### Bootstrap / first-session
| Trigger | Fires from | When |
|---------|-----------|------|
| `game_started` | `EagleGameController.SetUpGame()` | First time entering a tutorial game |
| `game_started` | `TutorialDialogueCoordinator.EligibleTriggers()` | Any tutorial strategic model update; dialogue progress keeps it from replaying |
| `first_battle_available` | `OnModelUpdated()` ~L204 | A `RunningShardokGameModel` first appears |
| `battle_entered` | `OnBattleEntered()` ~L793 | User clicks **Battle!** |
| `battle_entered` | `OnBattleEntered()` ~L793 | User clicks **Fight!** |
| `tutorial_battle_ended` | `CheckTutorialBattleEnded()` ~L594 | Tutorial battle removed from running models |
| `tutorial_rebuild_support` | `CheckTutorialRebuildSupport()` ~L616 | Captured-heroes phase done |
| `tutorial_taxes_collected` | `CheckTaxesCollected()` ~L680 | New Year action with positive tax delta |
@@ -135,7 +137,7 @@ All triggers are raised via `TutorialTriggerRegistry`. Today these flow to `Dial
| `hero_stat_gained` | ~L281 | `HeroStatGained` action result |
| `hero_profession_gained` | ~L284 | `ProfessionGained` action result |
| `tutorial_faction_appears` | ~L287 | `TutorialFactionAppears` action — the Fracture Covenant landing |
| `tutorial_hero_faction_appears` | ~L290 | `TutorialHeroFactionAppears` action (no dialogue script today) |
| `tutorial_hero_faction_appears` | ~L290 | `TutorialHeroFactionAppears` action; has a four-step strategic dialogue today |
| `tutorial_hero_departed` | ~L299 | First `HeroesDeparted` action against another player (King's hero abandons service) |
| `tutorial_hero_departed_again` | ~L302 | Second `HeroesDeparted` in a *different* month than the first |
@@ -220,8 +222,8 @@ Script shape (see `tutorial_strategic.json`):
"speakerName": "Old Marek the Learned",
"speakerImagePath": "fixed/old_marek_the_learned.png",
"dialogueText": "...",
"instructionText": "Click Battle! to enter tactical combat.",
"highlightTarget": "GoToBattleButton",
"instructionText": "Click <b>Fight!</b> to enter tactical combat.",
"highlightTarget": "FightButton",
"persistHighlight": true,
"highlightProvince": "Onmaa",
"completionEvent": null
@@ -247,10 +249,12 @@ Dialogue panel position defaults sensibly per scene (`top` during combat) and ca
Reset paths:
- `TutorialState.Reset()` clears everything.
- Entering a tutorial game (`GameType.Tutorial`) resets so it can be replayed.
- Entering a tutorial game (`GameType.Tutorial`) resets `TutorialState`; dialogue replay is controlled separately by the per-game dialogue progress key.
- Settings → "Reset Tutorials" calls `TutorialManager.ResetAllProgress()`, which also calls `DialogueManager.ResetCompletedScripts()`.
`DialogueManager` tracks completed scripts in memory only — they reset whenever `TutorialState` does.
`TutorialDialogueProgressStore` separately stores completed dialogue script IDs per game under PlayerPrefs keys named `Eagle0_TutorialDialogueProgress_<gameId>`.
`DialogueManager` keeps the active game's completed script IDs in memory and saves them through `TutorialDialogueProgressStore`. `ResetCompletedScripts()` clears the current game's dialogue progress; `ClearCurrentGameProgress()` deletes the current game's persisted dialogue key.
---
@@ -276,7 +280,7 @@ You play **Sadar Rakon**, formerly Ikhaan Tarn's most trusted lieutenant. Tarn's
| # | Trigger | What happens |
|---|---------|--------------|
| 1 | `game_started` | **Opening monologue** (3 steps): Sadar's backstory, the Reclamation, last stand at Onmaa. Ends highlighting the **Battle!** button (`persistHighlight`) |
| 1 | `game_started` | **Opening monologue** (3 steps): Sadar's backstory, the Reclamation, last stand at Onmaa. Ends highlighting the **Fight!** button (`FightButton`, `persistHighlight`) |
| 2 | `tutorial_battle_ended` | **Aftermath**: Tarn has *vanished*; you've captured his lieutenants. Recruit them or hold them |
| 3 | `tutorial_rebuild_support` | **Rebuild Onmaa** (3 steps): Marek frames it; **John Ranil** introduces *Improve* (engineer bonus); **Elena Fyar** introduces *Give Alms* (paladin bonus). Goal: 40 support by January |
| 4 | `tutorial_loyalty_warning` | November-ish, low-loyalty hero may leave at year end → use *Give Gold* / *Feast* |
@@ -288,6 +292,7 @@ You play **Sadar Rakon**, formerly Ikhaan Tarn's most trusted lieutenant. Tarn's
| 10 | `tutorial_faction_appears` | **The twist**: Fracture Covenant lands at Ingia and Soria. *The Eagle* commands them. Tarn may be with them |
| 11 | `tutorial_hero_departed` | First King's hero abandons service — "something deeper is at work" |
| 12 | `tutorial_hero_departed_again` | Second one in a different month — "something is wrong, I can feel it" (foreshadowing) |
| 13 | `tutorial_hero_faction_appears` | John Ranil and Elena Fyar leave Sadar's service and reappear as independent tutorial factions |
### Tactical battle flow (`tutorial_battle.json`)
+253 -136
View File
@@ -1,173 +1,290 @@
# URP Migration Plan
# URP Migration Completion Notes
## Why
Last refreshed: 2026-06-14
Unity is removing Built-In Render Pipeline (BiRP) support after Unity 6.7. We're currently on Unity 6.4. Each Unity minor version ships roughly quarterly, giving us a few release cycles before BiRP is dropped. This document lays out the migration plan.
## Why This Matters
## Strategy: Long-Lived Feature Branch
Unity is steering projects away from the Built-in Render Pipeline (BiRP) toward the
Universal Render Pipeline (URP). Eagle0 has completed the URP migration and now runs
on URP:
All URP conversion work happens on a **long-lived `urp-migration` branch**. Main stays fully functional on BiRP throughout the migration.
- `ProjectSettings/GraphicsSettings.asset` points at
`Assets/Settings/URP/Eagle0URPPipeline.asset`.
- Every active quality level in `ProjectSettings/QualitySettings.asset` uses the
same URP pipeline asset.
- Legacy Post Processing Stack v2 has been removed from `Packages/manifest.json`;
keep verifying it stays out with the migration inventory.
- **Main branch**: Continues using BiRP. All gameplay, AI, and server development proceeds normally.
- **URP branch**: Accumulates rendering pipeline changes across all phases.
- **Regular rebasing**: Periodically rebase/merge `main` into the URP branch to stay current. Shader and material changes rarely conflict with gameplay code, so merge conflicts should be manageable.
- **Merge to main**: Only when everything renders correctly and visual QA passes.
The migration is no longer an active compatibility project. Keep this document as the
completion record and maintenance checklist for future Unity, URP, shader, material,
camera layering, or render-order changes.
This avoids the "everything is pink" problem of switching the pipeline on main before shaders are converted.
## Current Recommendation
## Current State
Keep URP enabled on main. Do not continue speculative URP cleanup. Use the baseline
checklist and inventory tooling only when a future change touches shaders, materials,
camera layering, render order, pipeline settings, or a visible rendering regression.
| Category | Count | Notes |
Maintenance rules:
1. Keep the visual baseline current for Connection, Eagle, Shardok, and Settings.
2. Refresh the shader/material inventory after meaningful asset or scene changes.
3. Remove or isolate unused rendering dependencies.
4. Keep the existing CI guardrails passing.
5. Treat future pipeline experiments as separate branches with explicit findings.
Known URP regressions found during playtesting have been fixed. Normal gameplay work
can proceed; future rendering PRs should be driven by observable regressions or
intentional rendering changes.
## Maintenance Workflow
### 1. Create Visual Baselines
Capture a small set of known-good views before any future change to URP settings,
custom shaders, render ordering, or third-party visual materials. These should be
reproducible enough that a human can compare screenshots after each rendering change.
Minimum baseline scenes and states:
| Area | Required View |
|---|---|
| Connection | Connect panel, stored account list, running games list |
| Eagle map | Normal province map with borders, faction highlights, selected province tooltip |
| Eagle weather | Drought/heat shimmer, flood, blizzard/rain overlays |
| Eagle beasts | Bird/animal/monster beast effects on map and notification panel |
| Eagle UI | Command panel, notifications, settings panel, generated text |
| Shardok | Terrain, grid lines, labels/icons, bridges, fires, overlays, command buttons |
| Scene transitions | Connection -> Eagle -> Shardok -> Eagle |
Keep the manual checklist and editor-only capture tool working so rendering PRs can
leave screenshots in an ignored directory.
Use `docs/URP_VISUAL_BASELINE_CHECKLIST.md` for the current manual baseline capture
set.
### 2. Refresh The Shader And Material Inventory When Needed
The inventory is useful before significant rendering work. It reports:
- all shaders under `Assets/`
- all materials and the shader each material uses
- all scenes/prefabs that reference custom shaders or Post Processing components
- all usages of Built-in-only shader features:
- `GrabPass`
- `#pragma surface`
- `UnityCG.cginc`
- `UnityUI.cginc`
- tessellation
Run `Eagle0 > Rendering > Generate URP Migration Inventory` in the Unity editor to
generate the current report. The tool writes markdown and CSV files under Unity
`Temp/urp_migration/`, so running it should not dirty project assets.
The generated CSVs include both the full material list and production material
references, which helps separate game-path shader risk from unused package/demo
assets.
`docs/URP_SHADER_DEBT_INVENTORY.md` records the current production-vs-package
classification for remaining Built-in-style shader patterns.
Current inventory snapshot:
- Render pipeline settings are URP for both graphics default and active quality.
- Post Processing Stack reference hits are still zero.
- The all-material inventory is noisy: most material assets are `Standard`, and
many Built-in-only shader hits are in third-party or package/demo assets.
- The production material-reference report is the better starting point for the
probe. Current production shader references are concentrated in TextMeshPro,
Eagle custom map/effect shaders, Shardok grid/fire materials, and a small number
of standard-material beast assets.
- The project-owned Eagle and Shardok production custom shaders have been hardened
for URP and passed Windows Unity builds after merge.
- Project-owned runtime-created Eagle/Shardok materials now use serialized shader or
material references instead of project shader-name fallbacks.
- No project-owned production shader under `Assets/Eagle` or `Assets/Shardok`
currently contains the scanned Built-in-era shader patterns.
- Package/third-party shader debt remains documented but is not on the production
path unless a future inventory run finds new references.
### 3. Verify Legacy Post Processing Usage
The project no longer depends on `com.unity.postprocessing`. The old references were
limited to an unused Orc/Ogre sample scene profile and scene-template type metadata,
not production scenes.
Keep the project free of Post Processing Stack v2 references and use the inventory
report to catch regressions.
If references reappear:
- document the exact scene/camera/profile usage
- either remove the stale dependency or plan the equivalent URP Volume settings
### 4. Keep Drought Effects URP-Friendly
The old `Eagle/HeatShimmer` shader used a Built-in-style `GrabPass`, but shimmer was
already disabled in `DroughtEffect` because it did not work well with the UI Canvas.
Drought now intentionally uses the animated sun overlay as its current visual signal.
Keep drought visuals free of `GrabPass` unless we intentionally add a new
URP-friendly effect.
If stronger drought visuals are needed later, prefer:
- an animated transparent texture overlay clipped like the existing province particles
- a small particle/sprite effect around the sun icon
- a URP renderer feature only if screen-space distortion is truly worth the complexity
### 5. Leave Third-Party Shader Conversion Demand-Driven
The riskiest third-party shader set is Polytope Studio. Do not hand-convert package
shader sets speculatively.
`docs/URP_THIRD_PARTY_ASSET_AUDIT.md` records the current production GUID-reference
scan. The important result is that production assets use Polytope character prefabs
for Eagle beast effects, but the Polytope environment/water assets that still contain
`GrabPass` were not found on the production path.
If a future visible regression or inventory update makes third-party conversion
necessary, record:
- current installed version
- whether a URP-compatible version exists
- whether upgrading is license/account-accessible
- whether the package is still used in production scenes
### 6. Keep Probe Guidance For Future Pipeline Experiments
The original disposable probe has served its main purpose: URP is enabled on main and
the biggest visual regressions were fixed in focused PRs. Keep the probe playbook as
a template for future risky pipeline experiments, not as current migration guidance.
Use `docs/URP_PROBE_PLAYBOOK.md` when we need to measure a future Unity or render
pipeline change cheaply before committing to a mergeable branch.
## Known Maintenance Risks
| Risk | Why It Matters | Right-Now Action |
|---|---|---|
| Shaders | 47 | 7 custom Eagle, 1 hex mesh, 20 Polytope, 13 TextMesh Pro, 6 other third-party |
| Materials | 325 | Mix of Standard shader, custom, and third-party |
| ShaderGraph files | 4 | All TextMesh Pro (URP + HDRP variants already exist) |
| Scenes with baked lighting | 6 | Gameplay, Connection, Eagle, Shardok, Shared, Map Editor |
| Post-processing | 1 profile | PPP_Orc.asset (Post Processing Stack v2, FXAA/TAA, AO) |
| C# rendering scripts | 16 | Material/shader property manipulation only |
| Rendering path | Forward | Matches URP default |
| `UnityUI.cginc` usage in package shaders | URP does not provide the same include path/semantics for hand-authored shaders | Leave package-managed TMP shaders alone unless a visible regression appears. |
| `GrabPass` in unused package shaders | No direct URP equivalent | Ignore until production starts referencing those assets. |
| Surface shaders in third-party packages | URP does not support Built-in surface shader generation | Covered by beast material compatibility on current production paths; convert only for a visible regression. |
| Post Processing Stack v2 | Replaced by URP Volume system | Keep package and runtime/project-setting references from returning. |
| Scene lighting | URP lighting/shadow settings differ from BiRP | Baseline and rebake only for visible regressions or intentional lighting work. |
| Render ordering | Shardok and Eagle overlays depend on careful layering | Include affected overlays in baseline whenever render order changes. |
### Positive Findings
## Follow-Up Strategy
- **No OnRenderImage, Graphics.Blit, CommandBuffer, or GL.\* usage** in C# code
- Forward rendering already in use (matches URP default)
- TextMesh Pro already has URP ShaderGraph variants in the project
- No custom render passes or ScriptableRenderFeatures
- Shader property manipulation (SetTexture, SetFloat, SetColor) is URP-compatible
Main now carries the completed URP switch. Future rendering work should be split by
observable behavior:
### Known Risk Areas
- **Compatibility fixes**: small PRs for specific pink/missing/dark assets.
- **Render ordering fixes**: small PRs for Shardok/Eagle layer order regressions.
- **Inventory/docs updates**: docs-only or editor-tool PRs that do not alter runtime
visuals.
- **Pipeline experiments**: isolated branches using the probe playbook, with findings
copied into a mergeable follow-up.
- **GrabPass shaders** (HeatShimmerShader, PT_Water_Shader) have no direct URP equivalent
- **ProvinceMapShader** is complex (province ID lookup, border rendering, ocean animation, faction highlighting, UI clipping)
- **Surface shaders** (`#pragma surface surf Standard`) must be rewritten as HLSL or ShaderGraph
- **Polytope Studio shaders** (20 shaders) have no vendor-provided URP variants
- **Post Processing Stack v2** must be replaced with URP's integrated Volume system
- **Tessellation** (PT_Water_Shader) is not natively supported in URP
This keeps the project from accumulating another broad, hard-to-revert rendering PR.
## Shader Inventory
## Completed Phases
### Custom Eagle Shaders (7) -- HIGH PRIORITY
### Phase 0: Preparation And Switch
| Shader | Complexity | Key Issues |
|---|---|---|
| ProvinceMapShader | High | Province ID texture lookup, border rendering, ocean animation, faction highlighting, `UnityUI.cginc` dependency, stencil/clipping |
| ProvinceWeatherMapShader | Medium | Weather overlay rendering |
| ProvinceWeatherShader | Medium | Province weather effects |
| ProvinceParticleShader | Low | Custom particle rendering |
| HeatShimmerShader | High | **GrabPass** for screen distortion, province masking |
| ClipRectParticleUnlit | Low | UI-clipped particle shader |
| maskShader | Low | UI masking |
- Visual baseline checklist/screenshots: complete, keep current
- Repeatable shader/material inventory: complete, keep current
- Post Processing Stack usage decision: complete, keep removed
- Drought visual decision: complete, use non-GrabPass visual signal
- Third-party URP availability check: documented
- Disposable URP probe findings: superseded by the merged URP switch
- CI guardrails for pipeline settings, runtime shader lookup, project shader
patterns, and beast-material compatibility: complete
### Hex Mesh Shader (1) -- MODERATE PRIORITY
### Phase 1: Pipeline Setup
Uses `#pragma surface surf Standard` with GPU instancing. Straightforward conversion to URP Lit or ShaderGraph.
- Install/configure URP: complete
- Create URP Pipeline Asset and Renderer Asset: complete
- Configure forward rendering, shadows, HDR, and renderer features: complete enough
for current gameplay paths
- Run Unity's Render Pipeline Converter: complete for the merged switch
- Keep a record of every auto-converted material: use the inventory/audit tools
### Third-Party Shaders (39)
### Phase 2: Runtime Visual QA And Cleanup
| Source | Count | Complexity | Notes |
|---|---|---|---|
| Polytope Studio | 20 | Moderate-High | PBR, Toon, vegetation (custom lighting), water (**GrabPass + tessellation**). No vendor URP pack available. |
| TextMesh Pro | 13 | Low | URP ShaderGraph variants already exist in project |
| RRFreelance | 3 | Low | Standard surface shaders, direct conversion |
| Clown.fat | 2 | Moderate | Custom ToonRamp lighting model |
| GUI Pro Kit | 1 | Low | Hidden particle shader |
Project-owned custom shader conversion, shader-name fallback cleanup, and deletion
cleanup are complete. Current CI coverage keeps the URP pipeline assigned, required
runtime shader references serialized, required player-runtime-only shaders included,
project-owned Eagle/Shardok shaders free of scanned Built-in-only patterns, key
Addressables labels covered, and major Eagle/Shardok visual wiring guarded.
### GrabPass Shaders (Require Special Handling)
Runtime visual QA was completed during playtesting. Known regressions from that pass
were fixed before the migration was called done.
GrabPass does not exist in URP. These must be reimplemented using `ScriptableRenderPass` + `Renderer Features` or Blit-based alternatives:
Follow-up notes:
1. **HeatShimmerShader** -- Screen distortion effect (currently has a shimmer-disabled TODO, may be deprioritized)
2. **PT_Water_Shader** -- Water refraction/transparency (PT_Water_Shader_WebGl exists without GrabPass as a reference)
- Use `docs/URP_VISUAL_BASELINE_CHECKLIST.md` for each future rendering follow-up.
- Keep screenshots in an ignored local directory, not in git.
- Treat package/third-party shader conversions as response work for visible
regressions or new production references, not as speculative cleanup.
## Phased Approach
### Phase 3: GrabPass And Special Effects Maintenance
### Phase 1: Pipeline Setup + Auto-Conversion (Days 1-3)
- Keep drought visuals on the current animated sun overlay unless a new URP-friendly
effect is intentionally added.
- Reimplement or replace the Polytope `PT_Water_Shader` GrabPass shaders if they are
ever become used by production assets.
- Avoid leaving screen-space effects until the end; they are likely to drive renderer
feature requirements.
- Install URP package
- Create URP Pipeline Asset and Renderer Asset
- Configure basic pipeline settings (forward rendering, shadow settings)
- Run Unity's **Render Pipeline Converter** (Edit > Rendering > Render Pipeline Converter)
- Auto-converts Standard shader materials and some built-in shaders
- Handles a significant portion of the 325 materials
- Will NOT touch custom shaders
- Create a test scene to validate basic rendering
- Migrate viewport clipping system (`RendererViewportClipper`, `ViewportClipper`, `PopupClipper`) to use URP-compatible global shader properties
### Phase 4: Third-Party Shaders Maintenance
### Phase 2: Custom Eagle Shaders (Weeks 1-3)
- Upgrade packages where vendor URP shaders exist and a production need appears.
- Convert remaining PBR/toon/vegetation shaders manually only for production
references or visible regressions.
- Prefer replacement or deletion for unused demo-only assets.
This is the critical path. Without these, the game is unplayable.
### Phase 5: Materials, Lighting, And Post Processing Maintenance
**Week 1-2: ProvinceMapShader**
- Convert from BiRP Cg/HLSL to URP HLSL
- Replace `UnityCG.cginc` includes with `Core.hlsl` / `Common.hlsl`
- Replace `UnityUI.cginc` with custom URP-compatible UI clipping
- Preserve province ID texture lookup, border rendering, ocean animation, faction highlighting
- Validate stencil operations and render queue ordering
- Batch-update materials only for production paths or visible regressions.
- Replace Post Processing Stack v2 with URP Volumes only if a production need returns.
- Rebake lighting for production scenes when scene lighting is intentionally changed.
- Tune shadow and HDR settings when visual QA or performance work justifies it.
**Week 2-3: Remaining Eagle shaders**
- ProvinceWeatherMapShader + ProvinceWeatherShader
- ProvinceParticleShader + ClipRectParticleUnlit
- maskShader
- Hex Mesh Shader (convert surface shader to URP Lit)
- HeatShimmerShader (reimplement without GrabPass, or defer if shimmer remains disabled)
### Phase 6: Validation Maintenance
### Phase 3: Third-Party Shaders (Weeks 4-6)
Required visual QA for future rendering changes:
**Polytope Studio (20 shaders)**
- Convert PBR shaders (Armors, NPC, Weapons, Props, Rock) from surface shaders to URP Lit / ShaderGraph
- Convert Toon shaders to custom URP shader or ShaderGraph with custom lighting
- Convert vegetation shaders (custom `StandardCustom` lighting) to ShaderGraph
- PT_Water_Shader: Reimplement without GrabPass and tessellation (use PT_Water_Shader_WebGl as reference for non-GrabPass approach)
- Connection lobby
- Eagle map and command flows
- Eagle weather/beasts/notifications
- Shardok battle map overlays, unit text/icons, bridges, fires
- Scene transitions
- Mac and Windows builds
- Performance sanity check
**Other third-party**
- Clown.fat ToonRamp shaders: Convert custom lighting model to ShaderGraph
- RRFreelance shaders: Direct Standard-to-URP-Lit conversion
- GUI Pro Kit particle shader: Convert to URP particle shader
## Rough Effort
### Phase 4: Materials, Lighting & Post-Processing (Weeks 7-8)
**Materials**
- Batch-update any remaining materials not handled by the auto-converter
- Verify all 325 materials render correctly
- Fix any visual differences from lighting model changes
**Lighting**
- Rebake lightmaps for all 6 scenes (Gameplay, Connection, Eagle, Shardok, Shared, Map Editor)
- Configure URP shadow cascade settings to match current quality levels
- Verify HDR rendering
**Post-Processing**
- Remove Post Processing Stack v2 dependency (`com.unity.postprocessing: 3.5.1`)
- Replace with URP integrated Volume system
- Recreate PPP_Orc effects (FXAA/TAA, Ambient Occlusion) using URP Volume overrides
### Phase 5: Testing & Validation (Weeks 9-11)
- Visual QA across all 6 scenes
- Verify beast/character rendering (vertex colors, animations)
- Verify weather effects (blizzard, drought, flood)
- Verify particle systems (fire & explosion effects, UI particles)
- Verify UI rendering (GUI Pro Kit, Modern UI Pack, TextMesh Pro)
- Performance profiling (URP has different performance characteristics)
- Test on target platforms
- Bug fixes and visual polish
## Effort Estimates
| Phase | Best Case | Realistic | Worst Case |
|---|---|---|---|
| 1. Pipeline Setup | 1-2 days | 2-3 days | 3-5 days |
| 2. Custom Eagle Shaders | 1.5 weeks | 2-3 weeks | 3-4 weeks |
| 3. Third-Party Shaders | 1.5 weeks | 2-3 weeks | 3-4 weeks |
| 4. Materials & Lighting | 3-5 days | 1-1.5 weeks | 2 weeks |
| 5. Testing & Validation | 1 week | 1.5-2 weeks | 2-3 weeks |
| **Total** | **6-8 weeks** | **8-12 weeks** | **12-16 weeks** |
The biggest variable is Polytope Studio shader conversion. If vendor URP packs become available, Phase 3 shrinks significantly.
| Work | Status |
|---|---|
| Preparation/probe | Complete |
| Pipeline setup | Complete |
| Core custom shaders | Complete |
| Runtime shader/build guardrails | Complete; project-owned runtime paths use serialized shader/material references |
| GrabPass/special effects | Deferred until a production reference or new effect need appears |
| Third-party shaders/materials | Covered by runtime compatibility and visual QA unless a visible regression appears |
| Lighting/post-processing | No current Post Processing Stack dependency; rebake/tune only for visible regressions |
| QA/polish | Completed for the migration; repeat for future rendering changes |
## Key Reminders
- **Don't leave GrabPass rewrites and ProvinceMapShader for the end** -- they're the riskiest pieces and should be tackled early.
- The migration window (~1 year) is comfortable. Spreading the work across this window is fine, but front-load the hard shader work.
- C# code changes should be minimal -- the codebase avoids direct rendering API usage.
- `UnityUI.cginc` has no direct URP equivalent. Custom implementation will be needed for UI clipping in shaders.
- URP is already enabled on main; keep follow-up work narrowly scoped.
- Project-owned shader-name fallbacks should stay out of runtime-created materials;
use serialized shader/material references instead.
- `Shader.Find` remains acceptable for Unity/URP-provided shader names inside
third-party compatibility shims, where there is no project asset to serialize.
- Do not hand-convert package shaders unless runtime visual QA exposes a concrete
regression or a new production reference appears.
- Keep C# changes minimal; current rendering scripts mostly set material properties.
- Preserve recent Shardok render-order behavior while changing pipelines.
- Treat screenshots as migration tests; compilation alone is not meaningful here.
+133
View File
@@ -0,0 +1,133 @@
# URP Probe Playbook
Last refreshed: 2026-06-14
This playbook is historical reference for the disposable URP probe described in
`docs/URP_MIGRATION_PLAN.md`. The URP migration is complete; keep this only as a
template for future risky render-pipeline experiments. A probe branch is not a
production branch and should not be merged as-is.
## Goals
- Use the already-installed URP package in an isolated branch.
- Create the minimum URP pipeline/renderer assets needed to enter Play Mode.
- Run Unity's Render Pipeline Converter.
- Record compile errors, pink materials, broken shaders, and visual regressions.
- Preserve findings in docs, then discard or reset the probe branch.
## Branch Shape
Use two branches:
| Branch | Purpose | Mergeable |
|---|---|---|
| `codex/urp-probe` | Disposable package/settings/material conversion experiment | No |
| `codex/urp-probe-findings` | Small docs-only summary copied from the probe | Yes |
Do not open a PR from `codex/urp-probe` unless it is clearly marked draft and
experimental. Prefer opening the PR from `codex/urp-probe-findings`.
## Before Starting
1. Confirm `origin/main` is fresh.
2. Confirm the worktree is clean.
3. Capture or review the baseline checklist in
`docs/URP_VISUAL_BASELINE_CHECKLIST.md`.
4. Generate a current inventory with
`Eagle0 > Rendering > Generate URP Migration Inventory`.
5. Keep the inventory output under Unity `Temp/urp_migration/`; do not commit
generated reports unless they are intentionally summarized.
## Probe Steps
1. Create `codex/urp-probe` from `origin/main`.
2. Confirm `com.unity.render-pipelines.universal` is installed in
`Packages/manifest.json`.
3. If testing new pipeline settings, create alternate URP Pipeline/Renderer assets
under a clearly named temporary folder.
4. Assign the alternate URP pipeline asset in:
- `ProjectSettings/GraphicsSettings.asset`
- `ProjectSettings/QualitySettings.asset`
5. Run Unity's Render Pipeline Converter only if the experiment intentionally tests
material conversion.
6. Enter Play Mode from `Assets/Scenes/Main.unity`.
7. Exercise the baseline views:
- Connection lobby
- Eagle map
- Eagle weather and beast effects
- Shardok terrain, grid, labels, bridges, fires, overlays
- Connection -> Eagle -> Shardok -> Eagle transitions
8. Record the exact failure mode for every broken visual:
- compile error
- pink material
- invisible mesh or sprite
- wrong render order
- lighting/shadow difference
- input or scene-load regression
## Expected High-Risk Areas
- Eagle map rendering:
- province colors, borders, and ocean animation
- weather overlays and province particles
- beast effects on the map and in notifications
- Shardok map rendering:
- terrain, bridges, fire overlays, and grid overlays
- foreground UI/effect overlays
- unit labels/icons over fires, bridges, and one-shot animations
- Third-party 3D materials:
- Polytope Studio character prefabs used by Eagle human-type beast effects
- RRFreelance Orc/Ogre materials used by `OgreEffect`
- Render ordering in Shardok:
- unit text/icons versus fires
- unit text/icons versus bridges
- selection/command overlays versus labels
## Findings Template
Copy this template into the mergeable findings branch after the probe:
```markdown
# URP Probe Findings
Date:
Unity version:
URP package version:
Probe branch commit:
## Summary
- Entered Play Mode:
- Connection usable:
- Eagle usable:
- Shardok usable:
- Build attempted:
## Converter Results
- Materials converted:
- Materials left pink:
- Shader compile errors:
## Visual Regressions
| Area | Expected | Actual | Likely Cause | Next Action |
|---|---|---|---|---|
## Required Migration PRs
1.
2.
3.
## Notes
-
```
## After The Probe
1. Copy findings into a docs-only branch.
2. Delete or abandon `codex/urp-probe`.
3. Do not merge probe-generated package/settings/material churn into main until the
findings have been reviewed and broken visuals have owner PRs.
+145
View File
@@ -0,0 +1,145 @@
# URP Shader Debt Inventory
Last refreshed: 2026-06-14
This inventory classifies remaining Built-in Render Pipeline shader patterns after
the completed URP switch. The project is running URP, but some package shader source
files still use Built-in-era helpers or features. That is package/vendor debt, not
evidence that the old pipeline is active and not an active migration blocker.
## Scan Method
The scan looked for these patterns in Unity shader-like assets:
- `GrabPass`
- `#pragma surface`
- `UnityCG.cginc`
- `UnityUI.cginc`
- `UNITY_MATRIX_`
- `sampler2D _GrabTexture`
- `tessell`
- `Tessellation`
It then mapped every matching shader asset to its Unity GUID and scanned production
serialized assets under:
- `Assets/Scenes/`
- `Assets/Eagle/`
- `Assets/Shardok/`
- `Assets/ConnectionHandler/`
- `Assets/UI/`
- `Assets/common/`
- `Assets/Tutorial/`
- `Assets/Resources/`
- `Assets/Materials/`
This catches serialized production references. It does not prove runtime code never
loads an asset by path, but it separates game-path shader debt from package/demo
noise well enough to decide whether future rendering work is needed.
## Summary
| Bucket | Shader Assets With Built-in Patterns | Serialized Production References |
|---|---:|---:|
| Project-owned production shaders | 0 | 0 referenced |
| Project-owned runtime-loaded shaders | 0 | 0 serialized refs |
| Project-owned unreferenced shaders | 0 | 0 referenced |
| TextMeshPro package shaders | 17 | 1 referenced |
| Third-party/package shaders | 25 | 0 referenced |
Project-owned runtime-created materials now use serialized shader or material
references for Eagle weather, Eagle fireworks, Shardok foreground UI/effects, and
Shardok foreground text. Remaining production `Shader.Find` usage is limited to
Unity/URP-provided shader names inside `BeastMaterialCompatibility`, plus
third-party package code outside our ownership.
## Completed Project-Owned Shader Hardening
These production shaders have been converted to URP-compatible HLSL and passed the
Windows Unity build after merge.
| Shader | Production References | Completion Notes |
|---|---|---|
| `Assets/Eagle/Shaders/ProvinceMapShader.shader` | `Assets/Eagle/Materials/ProvinceMapMaterial.mat` | Converted with province color, border, ocean, and UI clipping behavior preserved. |
| `Assets/Eagle/Shaders/ProvinceWeatherMapShader.shader` | `Assets/Eagle/Materials/ProvinceWeatherMapMaterial.mat` | Converted with weather lookup, overlay alignment, clipping, and initialized effect/tint values. |
| `Assets/Eagle/Shaders/ProvinceWeatherShader.shader` | `Assets/Eagle/Weather/BlizzardEffect.mat`, `Assets/Eagle/Weather/DroughtEffect.mat`, `Assets/Eagle/Weather/FloodEffect.mat` | Converted for the live drought/flood/blizzard material path. |
| `Assets/Eagle/Shaders/ProvinceParticleShader.shader` | `Assets/Eagle/Effects/ProvinceParticleMaterial.mat` | Converted for Eagle map province particles. Weather effect prefabs serialize the shared particle material instead of relying on shader-name fallback. |
| `Assets/Eagle/Shaders/ClipRectParticleUnlit.shader` | `Assets/Eagle/Effects/Fireworks.prefab`, `Assets/Eagle/Effects/ParticleAlphaBlendMaterial.mat`, `Assets/Eagle/Effects/ParticleStandardUnlitMaterial.mat` | Converted for Eagle one-shot effects and clipped particle UI paths; Fireworks serializes this shader instead of using shader-name fallback. |
| `Assets/Eagle/maskShader.shader` | Formerly `Assets/Eagle/Materials/map_color_nolabels.mat`, `Assets/Eagle/Materials/map_color_whitened.mat` | Deleted after confirming the serialized material path was stale. |
| `Assets/Hex Mesh Shader.shader` | None | Deleted after confirming no serialized or code references to the asset, shader name, or GUID. |
| `Assets/Shardok/ShardokFireOverlay.shader` | `Assets/Shardok/ShardokFireOverlay.mat` | Converted with the Shardok fire overlay layering path preserved. |
| `Assets/Shardok/Shaders/ForegroundUIOverlay.shader` | Serialized Shardok foreground UI/effect shader references | URP pass is serialized on the Shardok scene/container and included in `ProjectSettings/GraphicsSettings.asset`; the stale Built-in fallback pass has been removed. |
| `Assets/TextMesh Pro/Resources/Shaders/TMP_SDF Overlay.shader` | Serialized Shardok foreground text shader references | Serialized on the Shardok scene/container and included in `ProjectSettings/GraphicsSettings.asset` for required foreground text overlay material. |
## CI Guardrails
- `URPPipelineSettingsTests` keeps Graphics and Quality settings on the same URP
pipeline asset.
- `RuntimeShaderValidationTests` keeps required runtime shader assets present,
serialized Shardok foreground shader references assigned, and player-runtime-only
shaders listed in Always Included Shaders.
- `WeatherEffectMaterialTests` keeps Eagle flood/blizzard prefabs wired to the
shared URP province particle material and prevents shader-name fallbacks from
returning to those runtime paths.
- `EpidemicEffectMaterialTests` keeps Eagle epidemic particles wired to the shared
clipped particle material.
- `EagleMapMaterialWiringTests` keeps Eagle map/weather controllers wired to the
project map materials and expected URP shaders.
- `ProjectShaderPatternTests` fails if project-owned Eagle/Shardok shaders
reintroduce the scanned Built-in-only patterns.
- `RuntimeShaderFindUsageTests` fails if project-owned Eagle/Shardok runtime
scripts reintroduce shader-name lookups outside documented compatibility shims.
- `BeastMaterialCompatibilityTests` keeps runtime third-party beast material
conversion covered.
- `ProvinceBeastsControllerTests` keeps Eagle beast content names routed to
specific animated effect prefabs instead of silently falling back to the generic
circling-bird effect.
- `FireworksEffectTests` keeps the fireworks prefab wired to the project-owned
clipped particle shader and prevents shader-name fallback from returning.
- `TutorialSpriteAssetTests` keeps tutorial TMP sprite assets and the shared dialogue
panel instruction sprite asset renderable.
- `ProvinceActionAnimatorSpriteTests` keeps Eagle action animation sprite fields
assigned in the Eagle scene.
- `PostProcessingStackRemovalTests` keeps legacy Post Processing Stack v2 package
and runtime/project-setting references from returning.
- `ProductionShaderReferenceTests` keeps shaders with Built-in-only patterns, and
materials that use them, off the production serialized asset path except for the
package-managed TextMeshPro shader family.
## Highest Priority: Referenced Project-Owned Shader Debt
No project-owned production shader under `Assets/Eagle` or `Assets/Shardok`
currently contains the scanned Built-in-era shader patterns.
| Shader | Patterns | Production References | Recommended Action |
|---|---|---|---|
| None | None | None | No active migration work. Re-run inventory after future rendering changes. |
## Referenced Package Shader
| Shader | Patterns | Production References | Recommended Action |
|---|---|---|---|
| `Assets/TextMesh Pro/Resources/Shaders/TMP_Sprite.shader` | `UnityCG.cginc`, `UnityUI.cginc`, `UNITY_MATRIX_` | 16 tutorial sprite assets under `Assets/Tutorial/Sprite Assets/` | Leave alone unless Unity/URP surfaces a visible TextMeshPro sprite regression. Prefer package/vendor updates over hand-editing TMP package shaders. |
## Third-Party And Package Hits Without Serialized Production References
These files still contain Built-in-only shader patterns, but the current GUID scan
did not find serialized production references. Treat them as package/demo cleanup,
not as urgent game-path blockers.
| Package Area | Examples | Patterns | Recommended Action |
|---|---|---|---|
| Polytope Studio characters/weapons/props | Modular NPC, armor, weapons, props shaders | `#pragma surface`, `UnityCG.cginc` | Keep runtime beast material compatibility in place; only import/replace vendor URP materials if we intentionally remove the compatibility shim. |
| Polytope Studio environment/water | Water, vegetation, rock shaders | `GrabPass`, `#pragma surface`, `UnityCG.cginc`, tessellation | Ignore unless production starts referencing these assets. Water shaders are the only remaining `GrabPass` hits. |
| RRFreelance Orc/Ogre | Ogre, armor, weapon shaders | `#pragma surface`, `UnityCG.cginc` | Current production references are handled by compatibility/material fixes; do not hand-convert unless a visible ogre regression returns. |
| TextMeshPro package shaders | TMP SDF/bitmap/surface shaders | `UnityCG.cginc`, `UnityUI.cginc`, `UNITY_MATRIX_`, surface shader variants | Prefer package updates. Avoid editing package shaders unless a concrete TMP visual regression exists. |
| Clown.fat and TileableBridgePack | Toony shaders, bridge cutout shader | `#pragma surface`, `UnityCG.cginc`, `UNITY_MATRIX_` | Leave as low priority unless a production reference appears. |
## Maintenance Guidance
Runtime visual QA for the URP migration was completed during playtesting and known
regressions from that pass were fixed. Leave unreferenced third-party/package shaders
alone until they have a visible regression or production reference.
Future rendering PRs should use `docs/URP_VISUAL_BASELINE_CHECKLIST.md` and include
affected screenshots in an ignored local directory, not in git.
+122
View File
@@ -0,0 +1,122 @@
# URP Third-Party Asset Audit
Last refreshed: 2026-06-14
This audit scopes third-party visual packages after the completed URP migration. It
is based on a GUID reference scan from production Unity assets under:
- `Assets/Scenes`
- `Assets/Eagle`
- `Assets/Shardok`
- `Assets/ConnectionHandler`
- `Assets/UI`
- `Assets/common`
- `Assets/Tutorial`
- `Assets/Resources`
- `Assets/Materials`
The scan maps GUIDs from each package folder, then finds production scenes,
prefabs, materials, controllers, assets, and scripts that reference those GUIDs.
It does not prove runtime code never loads assets by path, but it catches the
serialized references that matter most for future render-pipeline maintenance.
## Summary
| Package | Production Use | URP Risk | Notes |
|---|---:|---|---|
| Polytope Studio | 20 refs / 18 assets | Medium | Used by Eagle human-type beast effects. Runtime beast-material compatibility converts active renderers to URP materials; production refs are character prefabs, not environment/water assets. |
| GUI Pro Kit Fantasy RPG | 138 refs / 58 assets | Low | Production use is UI sprites/icons. No package shader dependency found in production refs. |
| Modern UI Pack v4.2.0 | 23 refs / 6 assets | Low | Production use is UI textures/icons. |
| RRFreelance Orc/Ogre | 2 refs / 2 assets | Medium | Ogre effect uses custom Built-in surface shaders in source assets, but active renderers are covered by runtime beast-material compatibility under URP. |
| DungeonMonsters2D | 12 refs / 12 assets | Medium | 2D monster prefabs for Eagle beast effects. Expected to be mostly SpriteRenderer/Animator; keep covered in beast-effect visual checks. |
| Animal pack deluxe v2 | 7 refs / 7 assets | Medium | Elephant effect and controller refs. Verify lit-material conversion. |
| Animal pack deluxe | 24 refs / 24 assets | Medium | Multiple Eagle animal beast effects. Verify lit-material conversion. |
| AfricaAnimalsPackLowPoly V2 | 10 refs / 10 assets | Medium | Eagle beast effects and controllers. Verify lit-material conversion. |
| AfricaAnimalsPackv1 | 8 refs / 8 assets | Medium | Eagle beast effects and controllers. Verify lit-material conversion. |
| AustraliaAnimalsPackv1 | 6 refs / 6 assets | Medium | Eagle beast effects and controllers. Verify lit-material conversion. |
| DinoPackLowPolyV1 | 3 refs / 3 assets | Medium | Velociraptor effect and controller. Verify lit-material conversion. |
| HEROIC FANTASY CREATURES FULL PACK Vol 2 | 12 refs / 12 assets | Medium | Hippogryph effect and controller. Verify lit-material conversion. |
| TileableBridgePack | 0 refs | Low | No production serialized refs found. Shardok bridge visuals appear to use project-owned/runtime assets instead. |
| Stylize Water Texture | 1 ref / 1 asset | Low | Eagle province map material uses one water texture, not a shader. |
| Terrain Hexes | 2 refs / 1 asset | Low | Shardok scene/container use one rock texture. |
| Hex Tiles | 0 refs | Low | No production serialized refs found. |
| 4000_Fantasy_Icons | 273 refs / 117 assets | Low | Sprite/icon assets only. |
| StrategyGameIcons | 21 refs / 12 assets | Low | Sprite/icon assets only. |
| Clown.fat | 14 refs / 10 assets | Medium | Clown effects and animation clips; also retargeted by Polytope human effects. |
| Dragon | 2 refs / 1 asset | Medium | Dragon effect mesh/animation. Verify material conversion. |
| HoneyBadger | 2 refs / 1 asset | Medium | Honey badger effect mesh/animation. Verify material conversion. |
| Raccoon | 2 refs / 1 asset | Medium | Raccoon effect mesh/animation. Verify material conversion. |
## Runtime Beast Findings
### Polytope Studio
Production assets reference Polytope character prefabs for Eagle map effects:
- `Assets/Eagle/Effects/ArcherEffect.prefab`
- `Assets/Eagle/Effects/GiantEffect.prefab`
- `Assets/Eagle/Effects/KnightEffect.prefab`
- `Assets/Eagle/Effects/MilitiaEffect.prefab`
- `Assets/Eagle/Effects/PeasantEffect.prefab`
- `Assets/Eagle/Effects/SoldierEffect.prefab`
These prefabs ultimately use `PT_NPC_Mat`, which references
`Polytope Studio/Lowpoly_Characters/Sources/Modular_NPC/Shaders/PT_Modular_NPC_Shader_PBR.shader`.
That shader uses Built-in surface shader generation and is not URP-ready as-is.
At runtime, Eagle beast effects pass their renderers through
`BeastMaterialCompatibility.ConfigureRenderer`, which replaces non-URP materials
with URP/Lit materials and bakes Polytope palette textures when needed. Treat this
as covered by the compatibility path unless a future visual regression appears.
Polytope environment and water shaders still exist in the project and include
Built-in-only features such as `GrabPass`, `UnityCG.cginc`, surface shaders, and
tessellation. However, this scan found no production serialized references to the
Polytope environment or water assets. Treat them as demo/package risk, not current
runtime risk, unless a later inventory run finds production references.
### RRFreelance Orc/Ogre
Production assets reference the Orc/Ogre package from:
- `Assets/Eagle/Effects/OgreEffect.prefab`
- `Assets/Eagle/Effects/OgreMapAnims.controller`
The custom materials reference package shaders that use Built-in surface shader
generation in the source assets:
- `RRFreelance-Characters/Orc-Ogre/Shaders/OgreShader.shader`
- `RRFreelance-Characters/Orc-Ogre/Shaders/ArmorShader2sided.shader`
- `RRFreelance-Characters/Orc-Ogre/Shaders/WeaponShader.shader`
At runtime, the Eagle monster-effect path also uses
`BeastMaterialCompatibility.ConfigureRenderer`, so these renderers should be
left on the compatibility path unless the Ogre effect becomes pink, dark, or
otherwise visibly broken.
## Maintenance Guidance
The URP migration is complete; do not convert third-party packages speculatively.
1. Keep the runtime beast-effect compatibility path covered by existing tests and
future visual checks when beast rendering changes.
2. If compatibility leaves a beast pink, dark, or visibly broken, replace the affected
materials with URP/Lit materials before touching unused demo-only shader packs.
3. Leave Polytope water/vegetation/environment conversion until a production
reference appears. They are noisy in the shader inventory but not currently on
the game path.
4. Include Eagle beast effects in the visual baseline. They cover most third-party
3D package risk in one place.
5. Treat UI sprite packs as low pipeline risk. Their risk is texture import/render
ordering, not shader conversion.
## Vendor Version / Access Notes
Known versions from existing repo docs:
- Modern UI Pack: `v4.2.0`
Most other package versions are not recorded in repo-local metadata. Before hand
converting large third-party shader sets, check the Unity Asset Store or vendor
account for URP-compatible updates. This especially matters for Polytope Studio,
because the package contains many Built-in surface shaders even though only a
small character subset is currently used by production assets.
+61
View File
@@ -0,0 +1,61 @@
# URP Visual Baseline Checklist
Use this checklist before any future branch changes render-pipeline settings, custom
shaders, materials, lighting, camera layering, or render order. The URP migration is
complete; this checklist is now a maintenance tool. The goal is not exhaustive
gameplay QA, but a stable set of visual states that can be compared by eye after
rendering changes.
Save screenshots under:
`src/main/csharp/net/eagle0/clients/unity/eagle0/Temp/urp-baselines/`
That directory is ignored by git.
Use `Eagle0 > Rendering > Capture URP Baseline Screenshot` in the Unity editor to
write a timestamped Game view capture to that directory.
## Capture Setup
- Use the same Unity version as the branch under test.
- Start from `Assets/Scenes/Main.unity`.
- Use the same Game view resolution for every capture in a comparison set.
- Capture the full Game view, not cropped panels.
- Name files with a stable prefix and short state name, for example
`main_01_connection_accounts.png` and `branch_01_connection_accounts.png`.
- If a view depends on server data or an account, write a short note next to the
screenshot naming the game/account/state used.
## Required Captures
| ID | Area | State | Must Verify |
|---|---|---|---|
| 01 | Connection | Connect panel with stored accounts visible | Background color, panel layout, text contrast, profession icons |
| 02 | Connection | Running games list visible | Row icons, timestamps, buttons, selected/hover states if practical |
| 03 | Eagle map | Normal province map after joining a game | Province colors, borders, ocean animation frame, faction highlights |
| 04 | Eagle map | Province hovered/selected | On-map province tooltip, selected outline, side-panel readability |
| 05 | Eagle weather | Drought province | Heat shimmer/drought signal remains visible and does not obscure UI |
| 06 | Eagle weather | Flood/rain or blizzard province | Weather overlay color, particle ordering, map readability |
| 07 | Eagle beasts | Beast effect on map | Beast animation visible in Game view and positioned over province |
| 08 | Eagle beasts | Beast notification open | Animated beast visible in notification and not hidden behind panel art |
| 09 | Eagle UI | Command panel with generated text | Text glyphs, fallback glyphs, panel clipping, button states |
| 10 | Eagle UI | Settings panel open | Top-right settings button, Escape behavior, modal layering |
| 11 | Shardok setup | Initial placement | Terrain tiles, grid lines, unit labels/icons, placement overlays |
| 12 | Shardok battle | Unit selected with destination overlays | Overlay labels/icons draw above terrain, bridges, fires, and highlights |
| 13 | Shardok battle | Bridge and fire visible near units | Bridge/fire render order does not cover unit labels/icons |
| 14 | Shardok battle | Command buttons visible | Right-side UX, disabled/enabled button states, tooltip readability |
| 15 | Scene transition | Eagle to Shardok to Eagle | No black/blank frame persists, returning Eagle map is visually intact |
## Acceptance Notes
For each rendering follow-up, record:
- Unity version and branch name.
- Screenshot directory.
- Any pink/missing materials.
- Any invisible UI, labels, particle effects, bridges, fires, or weather effects.
- Any render-order differences, especially Shardok overlays and Eagle beast effects.
- Whether the difference is acceptable, needs shader work, or needs asset/material work.
Do not merge a rendering branch until the affected Connection, Eagle, Shardok, or
Settings baseline captures are visually acceptable.
+20 -10
View File
@@ -4,11 +4,18 @@ This document describes which quests the AI attempts to complete proactively to
## Overview
The AI attempts to complete quests via `FulfillQuestsCommandSelector`, which is invoked by `MidGameAIClient.chosenFulfillEasyQuestsCommand`. The AI only considers quests from unaffiliated heroes in provinces ruled by a faction leader.
The AI attempts to complete most quests via `FulfillQuestsCommandSelector`, which is invoked by `MidGameAIClient.chosenFulfillEasyQuestsCommand`.
Important scope limits:
- The selector only considers quests from unaffiliated heroes in provinces owned by the acting faction.
- It further filters to provinces ruled by that faction's leader.
- It only emits a command when the corresponding command is currently available.
- Handler order is fixed. The first handler that produces a valid command wins.
## Quests the AI Actively Completes
The AI processes quest handlers in priority order. The first handler that produces a valid command wins.
This section lists quests with direct `QuestCommandChooser` handlers in `FulfillQuestsCommandSelector`.
### Diplomacy Quests
@@ -36,7 +43,8 @@ The AI processes quest handlers in priority order. The first handler that produc
| `AlmsAcrossRealmQuest` | `AlmsAcrossRealmQuestCommandChooser` | Gives food from the province with the largest surplus |
| `GiveToHeroesInProvinceQuest` | `GiveToHeroesInProvinceQuestCommandChooser` | Gives gold to the hero with the lowest loyalty in the specified province |
| `GiveToHeroesAcrossRealmQuest` | `GiveToHeroesAcrossRealmQuestCommandChooser` | Gives gold from the province with the most gold available |
| `SpendOnFeastsQuest` | `SpendOnFeastsQuestCommandChooser` | Spend gold on feasts |
| `SpendOnFeastsInProvinceQuest` | `SpendOnFeastsQuestCommandChooser` | Spend gold on feasts in the quest holder's province |
| `SpendOnFeastsAcrossRealmQuest` | `SpendOnFeastsQuestCommandChooser` | Spend gold on the most expensive currently available feast |
| `SendSuppliesQuest` | `SendSuppliesQuestCommandChooser` | Send food to the target province |
### Prisoner Quests
@@ -72,12 +80,6 @@ The AI processes quest handlers in priority order. The first handler that produc
| `BattalionDiversityQuest` | `BattalionDiversityQuestCommandChooser` | Hires one battalion of a missing type to reach 3+ distinct types. Only attempts if the province already has 2+ existing types, has sufficient development (`meetsRequirements`), and gold/food surplus. |
| `UpgradeBattalionQuest` | `UpgradeBattalionQuestCommandChooser` | Walks a priority tree per turn: arm-completes (with optional travel and/or food sale setup) → train-completes → march in a pre-qualified neighbor battalion → organize/top-off of the target type → march in an unqualified neighbor battalion → develop Economy/Agriculture → train progress → develop Infrastructure → arm progress. Only toggles Travel when it directly enables an arm-completes finisher. |
### Riot Quests
| Quest | Handler | Notes |
|-------|---------|-------|
| `SuppressRiotByForceQuest` | `CommandChoiceHelpers.handleRiotSelectedCommand` | When this quest exists and the faction has battalions, the AI prefers CrackDown over Give when handling riots. Not a quest command chooser — modifies existing riot handling priority. |
### Reconnaissance Quests
| Quest | Handler | Notes |
@@ -105,6 +107,14 @@ The AI processes quest handlers in priority order. The first handler that produc
| `SwearBrotherhoodWithHeroQuest` | `SwearBrotherhoodQuestCommandChooser` | Swear brotherhood with a specific hero |
| `DismissSpecificVassalQuest` | `DismissSpecificVassalCommandChooser` | Only if province has more than 2 heroes AND the unaffiliated hero's power >= target hero's power * `RequiredPowerMultiplierForDismiss` |
## Quests the AI Handles Indirectly
These are not in `FulfillQuestsCommandSelector`, but other AI command-selection code can still bias toward completing them.
| Quest | Handler | Notes |
|-------|---------|-------|
| `SuppressRiotByForceQuest` | `CommandChoiceHelpers.handleRiotSelectedCommand` | When this quest exists and the faction has battalions, the AI prefers CrackDown over Give when handling riots. |
## Quests the AI Does Not Yet Attempt to Complete
The following quests have no handler and must be completed naturally through gameplay. They are listed in rough priority order for future implementation.
@@ -153,7 +163,7 @@ The AI prioritizes quests in the order they appear in `FulfillQuestsCommandSelec
13. ReturnPrisoner
14. ReleaseAllPrisoners
15. ApprehendOutlaw
16. SpendOnFeasts
16. SpendOnFeastsInProvince / SpendOnFeastsAcrossRealm
17. RestProvince
18. DevelopProvinces
19. MobilizeProvinces
+1 -1
View File
@@ -87,7 +87,7 @@ Create these prefabs in `Assets/Eagle/Effects/`:
### Scene Setup
Wire up in `Gameplay.unity`:
Wire up in `Assets/Scenes/Eagle.unity`:
1. Add controller components to the Map GameObject
2. Assign `mapContainer`, `centroidsJson` (shared with other controllers)
3. Assign the effect prefab for each controller
Binary file not shown.
+29 -25
View File
@@ -1,36 +1,40 @@
module github.com/nolen777/eagle0
go 1.23.0
go 1.25.0
toolchain go1.23.3
toolchain go1.26.4
require (
github.com/aws/aws-sdk-go-v2 v1.32.8
github.com/aws/aws-sdk-go-v2/config v1.28.10
github.com/aws/aws-sdk-go-v2/credentials v1.17.51
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/aws/aws-sdk-go-v2 v1.42.0
github.com/aws/aws-sdk-go-v2/config v1.32.25
github.com/aws/aws-sdk-go-v2/credentials v1.19.24
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.3
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6
golang.org/x/sys v0.30.0
google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.36.3
golang.org/x/sys v0.46.0
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
)
require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
golang.org/x/text v0.25.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect
github.com/aws/smithy-go v1.27.2 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/text v0.38.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260610212136-7ab31c22f7ad // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect
)
+128
View File
@@ -1,56 +1,184 @@
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo=
github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls=
github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg=
github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg=
github.com/aws/aws-sdk-go-v2/config v1.32.25 h1:ACCejvStYoilgwrfegSt5ZntCbPrk52qfwyNcnl3omM=
github.com/aws/aws-sdk-go-v2/config v1.32.25/go.mod h1:LJyU8sDRbXUxFn8xMJIGP+v9QYYwveNLI8a/giAOiAs=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk=
github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ=
github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I=
github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27 h1:AmB5QxnD+fBFrg9LcqzkgF/CaYvMyU/BTlejG4t1S7Q=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.27/go.mod h1:Sai7P3xTiyv9ZUYO3IFxMnmiIP759/67iQbU4kdmkyU=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8 h1:iwYS40JnrBeA9e9aI5S6KKN4EB2zR4iUVYN0nwVivz4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.8/go.mod h1:Fm9Mi+ApqmFiknZtGpohVcBGvpTu542VC4XO9YudRi0=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 h1:V51LGlOq/1VsDsHUdoklAQi7rMmx4qQubvFYAlP2254=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22/go.mod h1:4Pzhyz8hJOm2bepgl+NjvRx8vlUFAIIvJnZ/MkcNPpU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8 h1:/Mn7gTedG86nbpjT4QEKsN1D/fThiYe1qvq7WsBGNHg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.8/go.mod h1:Ae3va9LPmvjj231ukHB6UeT8nS7wTPfC3tMZSZMwNYg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29 h1:hiME6pBzC7OTl9LMtlyTWBuEl1f4QBcUmFDKC7MLXtc=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.29/go.mod h1:G7RP+uhagpKtKhd1BM9N6JQqjCcGEU47K5lBVZQyRQw=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2 h1:a7aQ3RW+ug4IbhoQp29NZdc7vqrzKZZfWZSaQAXOZvQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.2/go.mod h1:xMekrnhmJ5aqmyxtmALs7mlvXw5xRh+eYjOjvrIIFJ4=
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.3 h1:JRseEu/vIDMaWis4bSw0QbXL+cvIGc1XnX076H5ZXLE=
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.3/go.mod h1:77ZAgynvx1txMvDG8gGWoWkO1augYDxkp9JElWFgjQU=
github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 h1:3nXpRcFwRCW8n7HgO2QGy0Dc20eQNfBuUemGQhpF8m8=
github.com/aws/aws-sdk-go-v2/service/signin v1.2.0/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY=
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo=
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI=
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8=
github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk=
github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 h1:VQpB2SpK88C6B5lPHTuSZKb2Qee1QWwiFlC5CKY4AW0=
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260610212136-7ab31c22f7ad h1:3iLyITS/sySRwbUKoC7ogfj2Yr1Cjs0pfaRKj5U5HEw=
google.golang.org/genproto/googleapis/api v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:KdNqO+rCIWgFumrNBSEDlDNrkrQnpkax7Tv1WxNY8V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad h1:45WmJvIV6C2+O/jjLkPUH+F3aOj/1miDoU2DD0+NWbg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+1304 -697
View File
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"dependencyDashboard": true,
"enabledManagers": [
"bazel-module",
"cargo",
"github-actions",
"gomod",
"swift"
],
"labels": [
"dependencies"
],
"packageRules": [
{
"description": "Bazel module artifact updates run in GitHub Actions so hosted Renovate does not need unsafe bazel mod deps execution",
"matchManagers": [
"bazel-module"
],
"skipArtifactsUpdate": true
},
{
"description": "Go packages import Bazel-generated protobuf packages, so Renovate's generic Go artifact updater cannot run ./...",
"matchManagers": [
"gomod"
],
"skipArtifactsUpdate": true
}
],
"lockFileMaintenance": {
"enabled": true,
"schedule": [
"before 6am on monday"
]
},
"prConcurrentLimit": 3,
"prHourlyLimit": 1,
"rebaseWhen": "behind-base-branch",
"schedule": [
"before 6am on monday"
],
"separateMajorMinor": true,
"timezone": "America/Los_Angeles"
}
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
bazel_cmd=(bazel)
if [ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]; then
bazel_cmd+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}")
fi
if [ -n "${BAZEL_OUTPUT_BASE:-}" ]; then
bazel_cmd+=("--output_base=${BAZEL_OUTPUT_BASE}")
fi
"${bazel_cmd[@]}" build "$@" //src/main/scala/net/eagle0/eagle:eagle_server
+15 -53
View File
@@ -1,68 +1,30 @@
#!/bin/bash
set -euxo pipefail
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
UNITY_ROOT="$REPO_ROOT/src/main/csharp/net/eagle0/clients/unity/eagle0"
OUTPUT_DIR="$UNITY_ROOT/Assets/GeneratedProtos"
PLUGIN_DIR="$UNITY_ROOT/Assets/Plugins/Eagle0Protos"
GENERATED_SOURCES_DIR="$UNITY_ROOT/Assets/GeneratedProtos"
PROTO_DLL_TARGET="//src/main/csharp/net/eagle0/clients/unity/eagle0:eagle0_protos"
PROTO_DLL_OUTPUT="$REPO_ROOT/bazel-bin/src/main/csharp/net/eagle0/clients/unity/eagle0/eagle0_protos/netstandard2.1"
# All C# protobuf generation targets
TARGETS=(
"//src/main/protobuf/net/eagle0/common:common_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/shardok/storage:storage_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/shardok/common:common_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/shardok/api:api_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/eagle/common:common_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/eagle/views:views_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/eagle/api:api_csharp_proto_srcs"
"//src/main/protobuf/net/eagle0/eagle/api:api_csharp_grpc_srcs"
"//src/main/protobuf/net/eagle0/eagle/api/command/util:util_csharp_proto_srcs"
)
/bin/echo "Building C# protobuf DLL..."
bazel build "$PROTO_DLL_TARGET"
# Subdirectory for each target (avoids filename collisions across packages)
SUBDIRS=(
"net/eagle0/common"
"net/eagle0/shardok/storage"
"net/eagle0/shardok/common"
"net/eagle0/shardok/api"
"net/eagle0/eagle/common"
"net/eagle0/eagle/views"
"net/eagle0/eagle/api"
"net/eagle0/eagle/api"
"net/eagle0/eagle/api/command/util"
)
/bin/echo "Syncing generated protobuf DLL to $PLUGIN_DIR..."
/bin/echo "Building C# protobuf sources..."
bazel build "${TARGETS[@]}"
/bin/echo "Syncing generated .cs files to $OUTPUT_DIR..."
# Stage new files in a temp directory, then rsync to preserve timestamps
# on unchanged files. This avoids triggering Unity reimport when protos
# haven't changed, saving ~7 minutes of script recompilation.
STAGING_DIR=$(mktemp -d)
trap "rm -rf '$STAGING_DIR'" EXIT
for i in "${!TARGETS[@]}"; do
target="${TARGETS[$i]}"
subdir="${SUBDIRS[$i]}"
# Convert target label to bazel-bin path
# //src/main/protobuf/net/eagle0/common:common_csharp_proto_srcs
# -> bazel-bin/src/main/protobuf/net/eagle0/common/common_csharp_proto_srcs
target_path="${target#//}"
pkg="${target_path%%:*}"
name="${target_path##*:}"
bin_dir="$REPO_ROOT/bazel-bin/$pkg/$name"
dest_dir="$STAGING_DIR/$subdir"
mkdir -p "$dest_dir"
find "$bin_dir" -name "*.cs" -exec cp {} "$dest_dir/" \;
done
cp "$PROTO_DLL_OUTPUT/Eagle0Protos.dll" "$STAGING_DIR/"
cp "$PROTO_DLL_OUTPUT/Eagle0Protos.xml" "$STAGING_DIR/"
# Sync only changed files and delete removed ones; --checksum compares
# content not timestamps so unchanged files keep their original mtime.
mkdir -p "$OUTPUT_DIR"
rsync -rc --delete "$STAGING_DIR/" "$OUTPUT_DIR/"
mkdir -p "$PLUGIN_DIR"
rsync -rc --delete --exclude "*.meta" "$STAGING_DIR/" "$PLUGIN_DIR/"
/bin/echo "Done. Generated C# proto sources in $OUTPUT_DIR"
rm -rf "$GENERATED_SOURCES_DIR"
/bin/echo "Done. Generated C# proto DLL in $PLUGIN_DIR"
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
bazel_cmd=(bazel)
if [ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]; then
bazel_cmd+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}")
fi
if [ -n "${BAZEL_OUTPUT_BASE:-}" ]; then
bazel_cmd+=("--output_base=${BAZEL_OUTPUT_BASE}")
fi
"${bazel_cmd[@]}" build -c opt "$@" //src/main/cpp/net/eagle0/shardok:shardok-server
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
bazel_cmd=(bazel)
if [ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]; then
bazel_cmd+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}")
fi
if [ -n "${BAZEL_OUTPUT_BASE:-}" ]; then
bazel_cmd+=("--output_base=${BAZEL_OUTPUT_BASE}")
fi
"${bazel_cmd[@]}" build \
--platforms=//:linux_arm64 \
--extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux \
"$@" \
//src/main/cpp/net/eagle0/shardok:shardok-server
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $0 <warm|verify> [bazel targets...]"
echo
echo "warm: build targets normally so local results can upload to the remote cache"
echo "verify: require all target actions to be available from remote cache"
}
if [ "$#" -lt 1 ]; then
usage
exit 2
fi
mode="$1"
shift
case "$mode" in
warm | verify)
;;
*)
usage
exit 2
;;
esac
tmp_root="${RUNNER_TEMP:-/private/tmp}"
run_id="${GITHUB_RUN_ID:-local}-$$"
output_user_root="${tmp_root%/}/eagle0-bazel-user-root-${run_id}"
output_base="${tmp_root%/}/eagle0-cache-parity-${mode}-${run_id}"
echo "Mode: $mode"
echo "Output user root: $output_user_root"
echo "Output base: $output_base"
run_ci_build_script() {
local script="$1"
shift
echo "Build script: $script"
echo "Build args:"
printf ' %s\n' "$@"
BAZEL_OUTPUT_USER_ROOT="$output_user_root" BAZEL_OUTPUT_BASE="$output_base" "$script" "$@"
}
cache_check_args() {
local bazel_args=(
"--remote_download_all"
)
if [ "$mode" = "verify" ]; then
bazel_args+=(
"--remote_upload_local_results=false"
"--experimental_remote_require_cached"
"--disk_cache="
)
fi
printf '%s\n' "${bazel_args[@]}"
}
run_bazel_targets() {
bazel_args=()
while IFS= read -r arg; do
bazel_args+=("$arg")
done < <(cache_check_args)
echo "Bazel args:"
printf ' %s\n' "${bazel_args[@]}" "$@"
bazel "--output_user_root=$output_user_root" "--output_base=$output_base" build "${bazel_args[@]}" "$@"
}
if [ "$#" -gt 0 ]; then
run_bazel_targets "$@"
exit 0
fi
bazel_args=()
while IFS= read -r arg; do
bazel_args+=("$arg")
done < <(cache_check_args)
run_ci_build_script scripts/build_eagle_ci.sh "${bazel_args[@]}"
run_ci_build_script scripts/build_shardok_ci.sh "${bazel_args[@]}"
+352
View File
@@ -0,0 +1,352 @@
#!/usr/bin/env python3
import argparse
import base64
from concurrent.futures import ThreadPoolExecutor, as_completed
import hashlib
import json
import os
from pathlib import Path
import random
import time
import urllib.error
import urllib.request
API_KEY_FILE = Path("src/main/scala/net/eagle0/common/llm_integration/api_keys.txt")
IMAGE_ENDPOINT = "https://api.openai.com/v1/images/generations"
APPEARANCE_PROFILES = [
"West African features, very dark brown skin",
"East African features, deep brown skin",
"North African features, olive-brown skin",
"Arabian Peninsula features, warm medium-brown skin",
"Persian features, olive skin",
"Turkic Central Asian features, tan skin",
"Mongolian features, golden-tan skin",
"North Indian features, medium-brown skin",
"South Indian features, dark brown skin",
"Bengali features, warm brown skin",
"Southeast Asian features, medium tan skin",
"Vietnamese features, light golden skin",
"Chinese features, light olive skin",
"Korean features, fair skin",
"Japanese features, fair-to-light olive skin",
"Indigenous Andean features, copper-brown skin",
"Mesoamerican features, medium brown skin",
"Native North American features, warm brown skin",
"Maori or Polynesian features, medium brown skin",
"Aboriginal Australian features, dark brown skin",
"Afro-Caribbean features, dark brown skin",
"Afro-Latin features, medium-dark brown skin",
"Brazilian mixed-heritage features, medium brown skin",
"Mexican mestizo features, tan skin",
"Mediterranean European features, olive skin",
"Iberian features, light olive skin",
"Italian features, olive skin",
"Greek features, olive skin",
"French features, light skin",
"Irish features, fair skin with freckles",
"Scottish features, ruddy fair skin",
"Nordic features, very fair skin",
"Slavic features, fair-to-light olive skin",
"Ashkenazi Jewish features, light olive skin",
"Armenian features, olive skin",
"Ethiopian highland features, medium-dark brown skin",
"Moroccan Amazigh features, tan olive skin",
"Egyptian features, warm tan skin",
"Filipino features, medium golden-brown skin",
"Pacific Islander features, warm brown skin",
]
AGE_PROFILES = [
"young adult",
"adult",
"seasoned middle-aged",
"older veteran",
]
FACE_PROFILES = [
"broad, sturdy face",
"narrow, severe face",
"round, watchful face",
"angular, weathered face",
"scarred but composed face",
"plain, practical face",
"commanding, heavy-browed face",
"tired but alert face",
]
HAIR_PROFILES = [
"cropped dark hair",
"close-cropped gray hair",
"braided black hair",
"shaved head",
"curly dark hair pulled back",
"straight black hair tied back",
"auburn hair streaked with gray",
"white hair cut short",
"dark beard and close hair",
"neat graying beard",
"coiled natural hair",
"long dark hair bound under a cap",
]
def api_key():
env_value = os.environ.get("OPENAI_API_KEY")
if env_value:
return env_value
if API_KEY_FILE.exists():
for line in API_KEY_FILE.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
if key.strip() == "openai_api_key":
value = value.strip()
if value and not value.startswith("your_"):
return value
raise RuntimeError(
"OpenAI API key not found. Set OPENAI_API_KEY or add openai_api_key to "
f"{API_KEY_FILE}."
)
def parse_args():
parser = argparse.ArgumentParser(
description="Generate Warden hero headshots and update generated_heroes.tsv image paths."
)
parser.add_argument(
"--hero-tsv",
default="src/main/resources/net/eagle0/eagle/generated_heroes.tsv",
help="Generated heroes TSV to read and update.",
)
parser.add_argument(
"--base-image-dir",
default=str(Path.home() / "Documents/headshots"),
help="Local headshot root that sync_headshots.sh uploads to S3.",
)
parser.add_argument(
"--model",
default="gpt-image-2",
help="OpenAI image generation model.",
)
parser.add_argument(
"--quality",
default="low",
choices=("low", "medium", "high", "auto"),
help="Image generation quality.",
)
parser.add_argument(
"--size",
default="1024x1024",
help="Generated image size.",
)
parser.add_argument(
"--limit",
type=int,
default=0,
help="Generate at most this many missing images; 0 means all missing images.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print planned paths without calling the API or writing files.",
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Regenerate images even if the target PNG already exists.",
)
parser.add_argument(
"--concurrency",
type=int,
default=4,
help="Number of parallel image generation requests.",
)
return parser.parse_args()
def read_rows(path):
lines = path.read_text().splitlines()
fieldnames = lines[0].split("\t")
rows = []
for line in lines[1:]:
fields = line.split("\t")
row = dict(zip(fieldnames, fields))
row["_fields"] = fields
rows.append(row)
return fieldnames, rows
def write_rows(path, fieldnames, rows):
image_path_index = fieldnames.index("image_path")
lines = ["\t".join(fieldnames)]
for row in rows:
fields = list(row["_fields"])
fields[image_path_index] = row["image_path"]
lines.append("\t".join(fields))
path.write_text("\n".join(lines) + "\n")
def image_gender(row, warden_index):
gender = row["gender"]
if gender in ("male", "female"):
return gender
digest = hashlib.sha256(row["name"].encode("utf-8")).digest()
return "male" if (digest[0] + warden_index) % 2 == 0 else "female"
def image_filename(row, warden_index):
digest = hashlib.sha256(row["name"].encode("utf-8")).hexdigest()[:16]
return f"{warden_index:04d}_{digest}.png"
def choose_profile(items, row, warden_index, salt):
digest = hashlib.sha256(f"{salt}:{warden_index}:{row['name']}".encode("utf-8")).digest()
return items[int.from_bytes(digest[:4], "big") % len(items)]
def prompt_for(row, warden_index):
prime_notes = [
f"strength {row['strength']}",
f"agility {row['agility']}",
f"constitution {row['constitution']}",
f"wisdom {row['wisdom']}",
f"charisma {row['charisma']}",
]
appearance = choose_profile(APPEARANCE_PROFILES, row, warden_index, "appearance")
age = choose_profile(AGE_PROFILES, row, warden_index, "age")
face = choose_profile(FACE_PROFILES, row, warden_index, "face")
hair = choose_profile(HAIR_PROFILES, row, warden_index, "hair")
return (
"Fantasy strategy game hero portrait, square headshot composition. "
f"Create a distinctive medieval Warden named {row['name']}. "
f"Gender presentation: {row['gender']}. "
f"Appearance: {age}, {appearance}, {face}, {hair}. "
f"Personality traits: {row['personality']}. "
"The character is a jailer, keeper of prisoners, and battlefield custodian: "
"stern keys, austere authority, prison-watch uniform, no modern clothing. "
"Show a resilient, high-constitution figure with weathered endurance and moral judgment. "
f"Stat cues: {', '.join(prime_notes)}. "
"Painted realistic fantasy style, expressive face, shoulders visible, neutral background, "
"soft dramatic lighting, no text, no logo, no frame, no watermark."
)
def request_image(key, model, quality, size, prompt):
body = {
"model": model,
"prompt": prompt,
"size": size,
"quality": quality,
"n": 1,
"output_format": "png",
}
request = urllib.request.Request(
IMAGE_ENDPOINT,
data=json.dumps(body).encode("utf-8"),
headers={
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=300) as response:
response_body = json.loads(response.read().decode("utf-8"))
data = response_body["data"][0]
if "b64_json" in data:
return base64.b64decode(data["b64_json"])
if "url" in data:
with urllib.request.urlopen(data["url"], timeout=300) as response:
return response.read()
raise RuntimeError("Image response did not include b64_json or url")
def generate_with_retries(key, args, prompt, attempts=5):
for attempt in range(1, attempts + 1):
try:
return request_image(key, args.model, args.quality, args.size, prompt)
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
retryable = error.code in (408, 409, 429, 500, 502, 503, 504)
if not retryable or attempt == attempts:
raise RuntimeError(f"OpenAI image request failed: {error.code} {body}") from error
except (TimeoutError, urllib.error.URLError):
if attempt == attempts:
raise
time.sleep(min(60, 2**attempt) + random.random())
def main():
args = parse_args()
hero_tsv = Path(args.hero_tsv)
base_image_dir = Path(args.base_image_dir).expanduser()
fieldnames, rows = read_rows(hero_tsv)
key = None if args.dry_run else api_key()
warden_index = 0
generation_tasks = []
for row in rows:
if row["profession"] != "warden":
continue
warden_index += 1
gender = image_gender(row, warden_index)
rel_path = f"warden/{gender}/{image_filename(row, warden_index)}"
target_path = base_image_dir / rel_path
row["image_path"] = rel_path
if target_path.exists() and not args.overwrite:
print(f"exists {rel_path}", flush=True)
continue
generation_tasks.append(
(row["name"], rel_path, target_path, prompt_for(row, warden_index))
)
if args.dry_run:
print(f"Dry run planned {warden_index} Warden image paths", flush=True)
else:
if args.limit:
generation_tasks = generation_tasks[: args.limit]
for _, _, target_path, _ in generation_tasks:
target_path.parent.mkdir(parents=True, exist_ok=True)
def generate_task(task):
name, rel_path, target_path, prompt = task
print(f"generate {rel_path} for {name}", flush=True)
image_bytes = generate_with_retries(key, args, prompt)
if not image_bytes.startswith(b"\x89PNG\r\n\x1a\n"):
raise RuntimeError(f"Generated image for {name} is not a PNG")
target_path.write_bytes(image_bytes)
return rel_path
generated_count = 0
if generation_tasks:
with ThreadPoolExecutor(max_workers=max(1, args.concurrency)) as executor:
futures = [executor.submit(generate_task, task) for task in generation_tasks]
for future in as_completed(futures):
rel_path = future.result()
generated_count += 1
print(
f"wrote {rel_path} ({generated_count}/{len(generation_tasks)})",
flush=True,
)
write_rows(hero_tsv, fieldnames, rows)
print(f"Updated {hero_tsv}; generated {generated_count} missing images", flush=True)
if __name__ == "__main__":
main()
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
scripts/build_eagle_ci.sh
scripts/build_shardok_ci.sh
+2 -2
View File
@@ -8,12 +8,12 @@
#
# Optional environment variables:
# SPARKLE_FEED_URL - Appcast URL (default: https://assets.eagle0.net/mac/appcast.xml)
# SPARKLE_VERSION - Sparkle version to use (default: 2.6.4)
# SPARKLE_VERSION - Sparkle version to use (default: 2.9.3)
set -euxo pipefail
APP_PATH="$1"
SPARKLE_VERSION="${SPARKLE_VERSION:-2.6.4}"
SPARKLE_VERSION="${SPARKLE_VERSION:-2.9.3}"
SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://assets.eagle0.net/mac/appcast.xml}"
SPARKLE_CACHE_DIR="/tmp/sparkle-cache"
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# Runs the repository-pinned clang-tidy binary from the workspace root.
#
# Bazel's `run` command executes clang-tidy from its runfiles tree, which makes
# workspace-relative source paths and .clang-tidy discovery fail. This wrapper
# builds the tool and then invokes the produced binary directly.
set -e
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
repo_root="$(git rev-parse --show-toplevel)"
if [ "$#" -eq 0 ]; then
echo "Usage: scripts/run-clang-tidy.sh [clang-tidy args] <files> [-- <compiler args>]"
echo ""
echo "Examples:"
echo " scripts/run-clang-tidy.sh --verify-config"
echo " scripts/run-clang-tidy.sh src/main/cpp/net/eagle0/shardok/library/CombatDamage.hpp -- -I$repo_root -std=c++23"
exit 2
fi
bazel build @llvm_toolchain//:clang-tidy
bazel_bin="$(bazel info bazel-bin)"
clang_tidy="$bazel_bin/external/toolchains_llvm++llvm+llvm_toolchain/clang-tidy"
if [ ! -x "$clang_tidy" ]; then
echo "Unable to find clang-tidy binary at $clang_tidy"
exit 1
fi
"$clang_tidy" --config-file="$repo_root/.clang-tidy" "$@"
+9 -9
View File
@@ -6,9 +6,9 @@
# Xcode version. If Xcode is upgraded in place, the DEVELOPER_DIR path stays the
# same, so Bazel can otherwise keep using stale Xcode metadata.
#
# 1. Writes .bazelrc.xcode with macOS-scoped flags:
# - action_env for remote cache key invalidation
# - DEVELOPER_DIR pointing to the active Xcode
# 1. Writes .bazelrc.xcode with mactools-scoped flags:
# - action_env for Apple build remote cache key invalidation
# - DEVELOPER_DIR pointing to the active Xcode for Apple builds
#
# 2. Runs `bazel clean --expunge` when the version actually changes, because
# local_config_apple_cc (a cached repository rule) bakes in the old version
@@ -35,7 +35,7 @@ if [ -z "$XCODE_BUILD_VERSION" ]; then
fi
BAZELRC_XCODE=".bazelrc.xcode"
EXPECTED_LINE="common:macos --action_env=XCODE_BUILD_VERSION=${XCODE_BUILD_VERSION}"
EXPECTED_LINE="common:mactools --action_env=XCODE_BUILD_VERSION=${XCODE_BUILD_VERSION}"
# Check if the file already has the right version (first line is the version marker)
if [ -f "$BAZELRC_XCODE" ]; then
@@ -45,19 +45,19 @@ if [ -f "$BAZELRC_XCODE" ]; then
fi
fi
# Xcode version changed (or first run) — expunge local cache to clear
# Xcode Bazel config changed (or first run) — expunge local cache to clear
# stale local_config_apple_cc, then write the new config
OLD_VERSION="unknown"
OLD_CONFIG="missing"
if [ -f "$BAZELRC_XCODE" ]; then
OLD_VERSION=$(sed -n 's/.*XCODE_BUILD_VERSION=//p' "$BAZELRC_XCODE" | head -1)
OLD_CONFIG=$(head -1 "$BAZELRC_XCODE")
fi
echo "Xcode build version changed: ${OLD_VERSION} -> ${XCODE_BUILD_VERSION}"
echo "Xcode Bazel config changed: ${OLD_CONFIG} -> ${EXPECTED_LINE}"
echo "Running bazel clean --expunge to clear stale toolchain config..."
bazel clean --expunge 2>/dev/null || true
cat > "$BAZELRC_XCODE" << EOF
${EXPECTED_LINE}
common:macos --repo_env=DEVELOPER_DIR=${DEVELOPER_DIR}
common:mactools --repo_env=DEVELOPER_DIR=${DEVELOPER_DIR}
EOF
if [ -n "${GITHUB_ENV:-}" ]; then
+1
View File
@@ -1,6 +1,7 @@
#!/bin/zsh
/opt/homebrew/bin/s3cmd sync \
--acl-public \
--exclude="*dream_log.txt" \
--exclude="*.DS_Store" \
--delete-removed \
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env bash
echo -ne "\033[0;32m"
echo 'Updating bazel dependencies. This will take about five minutes.'
echo -ne "\033[0m"
set -e
if [ "$(uname -s)" == "Linux" ]; then
BAZEL_DEPS_URL=https://github.com/johnynek/bazel-deps/releases/download/v0.1-9/bazel-deps-linux
BAZEL_DEPS_SHA256=034ce4d8674a200d73d97aa60774786a3771c0f3871197858bf55fd930677b15
elif [ "$(uname -s)" == "Darwin" ]; then
BAZEL_DEPS_URL=https://github.com/johnynek/bazel-deps/releases/download/v0.1-9/bazel-deps-macos
BAZEL_DEPS_SHA256=97e1b932cccc60e30475fc0ef56a538c6b8a732adbdf0eaec1988f0b4f1dc5dc
else
echo "Your platform '$(uname -s)' is unsupported, sorry"
exit 1
fi
# This is some bash snippet designed to find the location of the script.
# we operate under the presumption this script is checked into the repo being operated on
# so we goto the script location, then use git to find the repo root.
SCRIPT_LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $SCRIPT_LOCATION
REPO_ROOT=$(git rev-parse --show-toplevel)
BAZEL_DEPS_DIR="$HOME/.bazel-deps-cache"
BAZEL_DEPS_PATH="${BAZEL_DEPS_DIR}/v0.1-9"
if [ ! -f ${BAZEL_DEPS_PATH} ]; then
( # Opens a subshell
set -e
echo "Fetching bazel deps."
curl -L -o /tmp/bazel-deps-bin $BAZEL_DEPS_URL
GENERATED_SHA_256=$(shasum -a 256 /tmp/bazel-deps-bin | awk '{print $1}')
if [ "$GENERATED_SHA_256" != "$BAZEL_DEPS_SHA256" ]; then
echo "Sha 256 does not match, expected: $BAZEL_DEPS_SHA256"
echo "But found $GENERATED_SHA_256"
echo "You may need to update the sha in this script, or the download was corrupted."
exit 1
fi
chmod +x /tmp/bazel-deps-bin
mkdir -p ${BAZEL_DEPS_DIR}
mv /tmp/bazel-deps-bin ${BAZEL_DEPS_PATH}
)
fi
cd $REPO_ROOT
set +e
$BAZEL_DEPS_PATH generate -r $REPO_ROOT -s 3rdparty/workspace.bzl -d dependencies.yaml --target-file 3rdparty/target_file.bzl --disable-3rdparty-in-repo
RET_CODE=$?
set -e
if [ $RET_CODE == 0 ]; then
echo "Success, going to format files"
else
echo "Failure, checking out 3rdparty/jvm"
cd $REPO_ROOT
git checkout 3rdparty/jvm 3rdparty/workspace.bzl
exit $RET_CODE
fi
$BAZEL_DEPS_PATH format-deps -d $REPO_ROOT/dependencies.yaml -o
@@ -1,3 +1,4 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("//tools:copts.bzl", "COPTS")
cc_library(
@@ -66,6 +67,14 @@ cc_library(
],
)
cc_library(
name = "shardok_latency_trace",
srcs = ["ShardokLatencyTrace.cpp"],
hdrs = ["ShardokLatencyTrace.hpp"],
copts = COPTS,
visibility = ["//visibility:public"],
)
cc_library(
name = "repeated_proto_utils",
srcs = [],
@@ -14,6 +14,7 @@
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include "tools/cpp/runfiles/runfiles.h"
@@ -37,7 +38,7 @@ auto rloc(const string& execPath) -> string {
const std::unique_ptr<Runfiles> runfiles(Runfiles::Create(execPath, &error));
if (runfiles == nullptr) {
fprintf(stderr, "Error! %s\n", error.c_str());
std::cerr << "Error! " << error << '\n';
abort();
// error handling
}
@@ -78,9 +79,9 @@ auto FilesystemUtils::MapFilesDirectory() -> string {
void FilesystemUtils::MakeDirectoryIfNecessary(const string& directoryPath) {
if (fs::create_directories(directoryPath))
fprintf(stderr, "Directory %s created\n", directoryPath.c_str());
std::cerr << "Directory " << directoryPath << " created\n";
else
fprintf(stderr, "No new directory created for %s\n", directoryPath.c_str());
std::cerr << "No new directory created for " << directoryPath << '\n';
}
auto FilesystemUtils::SaveFilesDirectory() -> string {
@@ -140,11 +141,11 @@ auto FilesystemUtils::AtomicallySaveToPath(const string& path, const byte_vector
if (ostr.good()) {
const int err = rename(tempPath.c_str(), path.c_str());
if (err == -1) {
fprintf(stderr, "Failed to move file to %s! Errno %d\n", path.c_str(), errno);
std::cerr << "Failed to move file to " << path << "! Errno " << errno << '\n';
return false;
}
} else {
fprintf(stderr, "Failed writing to %s!\n", tempPath.c_str());
std::cerr << "Failed writing to " << tempPath << "!\n";
return false;
}
@@ -6,8 +6,8 @@
// Copyright © 2017 none. All rights reserved.
//
#ifndef FilesystemUtils_hpp
#define FilesystemUtils_hpp
#ifndef EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
#define EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
#include <string>
#include <vector>
@@ -36,4 +36,4 @@ public:
static auto LoadFromPath(const string& path) -> byte_vector;
};
#endif /* FilesystemUtils_hpp */
#endif // EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
+3 -3
View File
@@ -6,8 +6,8 @@
// Copyright © 2018 none. All rights reserved.
//
#ifndef MapUtils_hpp
#define MapUtils_hpp
#ifndef EAGLE0_COMMON_MAP_UTILS_HPP
#define EAGLE0_COMMON_MAP_UTILS_HPP
#include <string>
#include <unordered_map>
@@ -19,4 +19,4 @@ auto IntForKey(const std::unordered_map<std::string, std::string>& map, const st
auto BoolForKey(const std::unordered_map<std::string, std::string>& map, const std::string& key)
-> bool;
#endif /* MapUtils_hpp */
#endif // EAGLE0_COMMON_MAP_UTILS_HPP
@@ -6,16 +6,17 @@
// Copyright © 2019 none. All rights reserved.
//
#ifndef ProtobufWarningSuppression_h
#define ProtobufWarningSuppression_h
#ifndef EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
#define EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
#define SUPPRESS_PROTOBUF_WARNINGS \
_Pragma("GCC diagnostic ignored \"-Wpragmas\"") \
_Pragma("GCC diagnostic ignored \"-Wsign-conversion\"") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") _Pragma( \
"GCC diagnostic ignored \"-Wsign-conversion\"") \
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") _Pragma( \
"GCC diagnostic ignored \"-Wdocumentation-deprecated-sync\"") \
_Pragma("GCC diagnostic ignored \"-Wdocumentation\"") _Pragma( \
"GCC diagnostic ignored \"-Wshorten-64-to-32\"") \
_Pragma("GCC diagnostic ignored \"-Wnested-anon-types\"")
#endif /* ProtobufWarningSuppression_h */
#endif // EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
@@ -6,11 +6,13 @@
// Copyright (c) 2015 none. All rights reserved.
//
#ifndef __eagle0__ShardokRandomGenerator__
#define __eagle0__ShardokRandomGenerator__
#ifndef EAGLE0_COMMON_RANDOM_GENERATOR_HPP
#define EAGLE0_COMMON_RANDOM_GENERATOR_HPP
#include <cstddef>
#include <memory>
#include <random>
#include <vector>
class RandomGenerator;
@@ -22,6 +24,10 @@ private:
public:
RandomGenerator() = default;
RandomGenerator(const RandomGenerator&) = default;
auto operator=(const RandomGenerator&) -> RandomGenerator& = default;
RandomGenerator(RandomGenerator&&) = default;
auto operator=(RandomGenerator&&) -> RandomGenerator& = default;
virtual ~RandomGenerator() = default;
@@ -35,7 +41,7 @@ public:
template<class T>
auto RandomElement(const std::vector<T>& vec) -> T {
return vec[IntBelow((int)vec.size())];
return vec.at(static_cast<std::size_t>(IntBelow(static_cast<int>(vec.size()))));
}
static auto ChanceOpenEndedPercentileAtOrAbove(double value) -> double;
@@ -56,4 +62,4 @@ public:
auto IntBetween(int min, int max) -> int override; // inclusive, exclusive (min<=return<max)
};
#endif /* defined(__eagle0__ShardokRandomGenerator__) */
#endif // EAGLE0_COMMON_RANDOM_GENERATOR_HPP
@@ -6,10 +6,11 @@
// Copyright © 2018 none. All rights reserved.
//
#ifndef SequenceRandomGenerator_h
#define SequenceRandomGenerator_h
#ifndef EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
#define EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
#include <memory>
#include <utility>
#include <vector>
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
@@ -24,22 +25,22 @@
// which produces: initial=2 (triggers open-ended), accumulated=52, final=2-52=-50
class SequenceRandomGenerator : public ::RandomGenerator {
private:
const std::vector<double> sequence;
std::vector<double> sequence;
size_t position = 0;
auto DoubleZeroToOne() -> double override {
const double nextVal = sequence[position];
const double nextVal = sequence.at(position);
position++;
if (position >= sequence.size()) { position = 0; }
return nextVal;
}
public:
explicit SequenceRandomGenerator(std::vector<double> s) : sequence(std::move(s)), position(0) {}
explicit SequenceRandomGenerator(std::vector<double> s) : sequence(std::move(s)) {}
static auto WithSequence(const std::vector<double>& s)
-> std::shared_ptr<SequenceRandomGenerator> {
return std::make_shared<SequenceRandomGenerator>(s);
}
};
#endif /* SequenceRandomGenerator_h */
#endif // EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
@@ -0,0 +1,101 @@
#include "ShardokLatencyTrace.hpp"
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <sstream>
namespace net::eagle0::common {
namespace {
auto ReadEnvEnabled() -> bool {
const char* value = std::getenv("EAGLE0_SHARDOK_LATENCY_TRACE");
if (value == nullptr) { return false; }
const std::string text(value);
return text == "1" || text == "true" || text == "TRUE" || text == "yes" || text == "YES";
}
auto TraceEnabled() -> bool {
static const bool enabled = ReadEnvEnabled();
return enabled;
}
auto ReadGameFilter() -> std::string {
const char* value = std::getenv("EAGLE0_SHARDOK_LATENCY_GAME_ID");
return value == nullptr ? "" : std::string(value);
}
auto GameFilter() -> const std::string& {
static const std::string filter = ReadGameFilter();
return filter;
}
auto JsonEscape(std::string_view value) -> std::string {
std::ostringstream out;
for (const char c : value) {
switch (c) {
case '"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
case '\t': out << "\\t"; break;
default: out << c; break;
}
}
return out.str();
}
auto LogMutex() -> std::mutex& {
static auto mutex = new std::mutex();
return *mutex;
}
} // namespace
auto ShardokLatencyTraceEnabledForGame(std::string_view gameId) -> bool {
if (!TraceEnabled()) { return false; }
const std::string& filter = GameFilter();
return filter.empty() || filter == gameId;
}
void EmitShardokLatencyTrace(
std::string_view event,
std::string_view gameId,
const ShardokLatencyFields& fields,
std::chrono::steady_clock::duration duration) {
if (!ShardokLatencyTraceEnabledForGame(gameId)) { return; }
const auto durationMs =
std::chrono::duration_cast<std::chrono::microseconds>(duration).count() / 1000.0;
std::ostringstream out;
out << "{\"event\":\"shardok_latency\","
<< "\"component\":\"shardok\","
<< "\"span\":\"" << JsonEscape(event) << "\","
<< "\"game_id\":\"" << JsonEscape(gameId) << "\","
<< "\"duration_ms\":" << durationMs;
for (const auto& [key, value] : fields) {
out << ",\"" << JsonEscape(key) << "\":\"" << JsonEscape(value) << "\"";
}
out << "}";
std::scoped_lock lock(LogMutex());
std::cerr << out.str() << '\n';
}
ScopedShardokLatencyTrace::ScopedShardokLatencyTrace(
std::string event,
std::string gameId,
ShardokLatencyFields fields)
: event_(std::move(event)),
gameId_(std::move(gameId)),
fields_(std::move(fields)) {
if (ShardokLatencyTraceEnabledForGame(gameId_)) { start_ = std::chrono::steady_clock::now(); }
}
ScopedShardokLatencyTrace::~ScopedShardokLatencyTrace() {
if (!start_.has_value()) { return; }
EmitShardokLatencyTrace(event_, gameId_, fields_, std::chrono::steady_clock::now() - *start_);
}
} // namespace net::eagle0::common
@@ -0,0 +1,42 @@
#pragma once
#include <chrono>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace net::eagle0::common {
using ShardokLatencyFields = std::vector<std::pair<std::string, std::string>>;
auto ShardokLatencyTraceEnabledForGame(std::string_view gameId) -> bool;
void EmitShardokLatencyTrace(
std::string_view event,
std::string_view gameId,
const ShardokLatencyFields& fields,
std::chrono::steady_clock::duration duration);
class ScopedShardokLatencyTrace {
public:
ScopedShardokLatencyTrace(
std::string event,
std::string gameId,
ShardokLatencyFields fields = {});
~ScopedShardokLatencyTrace();
ScopedShardokLatencyTrace(const ScopedShardokLatencyTrace&) = delete;
auto operator=(const ScopedShardokLatencyTrace&) -> ScopedShardokLatencyTrace& = delete;
ScopedShardokLatencyTrace(ScopedShardokLatencyTrace&&) = delete;
auto operator=(ScopedShardokLatencyTrace&&) -> ScopedShardokLatencyTrace& = delete;
private:
std::string event_;
std::string gameId_;
ShardokLatencyFields fields_;
std::optional<std::chrono::steady_clock::time_point> start_;
};
} // namespace net::eagle0::common
@@ -10,6 +10,7 @@
#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
@@ -171,7 +172,7 @@ public:
// Get detailed queue information for debugging
void debug_queue_state() const {
std::unique_lock<std::mutex> lock(const_cast<std::mutex&>(queue_mutex));
printf("ThreadPool: Queue size: %zu\n", tasks.size());
std::cout << "ThreadPool: Queue size: " << tasks.size() << '\n';
if (!tasks.empty()) {
// Create a copy to inspect priorities without modifying queue
auto queue_copy = tasks;
@@ -180,9 +181,9 @@ public:
priorities.push_back(queue_copy.top().priority);
queue_copy.pop();
}
printf("ThreadPool: Priorities in queue: ");
for (int p : priorities) { printf("%d ", p); }
printf("\n");
std::cout << "ThreadPool: Priorities in queue: ";
for (int p : priorities) { std::cout << p << ' '; }
std::cout << '\n';
}
}
@@ -197,4 +198,4 @@ public:
} // namespace eagle0::common
#endif // EAGLE0_THREADPOOL_HPP
#endif // EAGLE0_THREADPOOL_HPP
+4 -4
View File
@@ -6,14 +6,14 @@
// Copyright © 2018 none. All rights reserved.
//
#ifndef TsvParser_hpp
#define TsvParser_hpp
#ifndef EAGLE0_COMMON_TSV_PARSER_HPP
#define EAGLE0_COMMON_TSV_PARSER_HPP
#include <string>
#include <unordered_map>
#include <vector>
typedef std::unordered_map<std::string, std::string> StringMap;
using StringMap = std::unordered_map<std::string, std::string>;
// Parses a TSV, with the first column becoming map keys and subsequent columns becoming values
// for different maps in the returned vector.
@@ -23,4 +23,4 @@ public:
std::vector<StringMap> ParseColumnEntryTsv(std::string tsv);
};
#endif /* TsvParser_hpp */
#endif // EAGLE0_COMMON_TSV_PARSER_HPP
@@ -6,8 +6,8 @@
// Copyright © 2017 none. All rights reserved.
//
#ifndef byte_vector_h
#define byte_vector_h
#ifndef EAGLE0_COMMON_BYTE_VECTOR_HPP
#define EAGLE0_COMMON_BYTE_VECTOR_HPP
#include <cstdint>
#include <cstring>
@@ -184,4 +184,4 @@ inline auto operator<<(std::ostream& ostr, byte_vector vec) -> std::ostream& {
return ostr;
}
#endif /* byte_vector_h */
#endif // EAGLE0_COMMON_BYTE_VECTOR_HPP
@@ -9,9 +9,11 @@
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <limits>
#include <mutex>
#include <random>
#include <sstream>
#include <stdexcept>
#include <thread>
@@ -862,12 +864,12 @@ auto AbstractMCTSAI::LogSearchResults(
// Log selected command
const std::string selectedDesc =
bestChild->action ? bestChild->action->getDescription() : "Unknown";
printf("MCTS: Selected %s (visit:%d, reward:%.2f, lookahead:%.2f) from %zu options\n",
selectedDesc.c_str(),
bestChild->visitCount,
bestChild->averageReward,
bestChild->lookaheadScore,
rootNode->children.size());
std::ostringstream selectedLine;
selectedLine << std::fixed << std::setprecision(2) << "MCTS: Selected " << selectedDesc
<< " (visit:" << bestChild->visitCount << ", reward:" << bestChild->averageReward
<< ", lookahead:" << bestChild->lookaheadScore << ") from "
<< rootNode->children.size() << " options";
std::cout << selectedLine.str() << '\n';
// Log top 3 actions by visits
std::vector<const MCTSNode*> sortedChildren;
@@ -879,20 +881,17 @@ auto AbstractMCTSAI::LogSearchResults(
// Show all actions if there are <= 10, otherwise top 5
const size_t numToShow = sortedChildren.size() <= 10 ? sortedChildren.size() : 5;
printf("MCTS: Top %zu actions by visits (out of %zu total):\n",
numToShow,
sortedChildren.size());
std::cout << "MCTS: Top " << numToShow << " actions by visits (out of " << sortedChildren.size()
<< " total):\n";
for (size_t i = 0; i < numToShow; ++i) {
const auto* child = sortedChildren[i];
printf(" [%zu] visits:%d avgReward:%.2f immediate:%.2f lookahead:%.2f",
i,
child->visitCount,
child->averageReward,
child->immediateScore,
child->lookaheadScore);
std::ostringstream line;
line << std::fixed << std::setprecision(2) << " [" << i << "] visits:" << child->visitCount
<< " avgReward:" << child->averageReward << " immediate:" << child->immediateScore
<< " lookahead:" << child->lookaheadScore;
// Show the action's own description
if (child->action) { printf(" %s", child->action->getDescription().c_str()); }
if (child->action) { line << ' ' << child->action->getDescription(); }
// Show sequence preview
if (!child->children.empty()) {
@@ -905,16 +904,15 @@ auto AbstractMCTSAI::LogSearchResults(
}
}
if (bestNext && bestNext->action) {
printf(" -> %s", bestNext->action->getDescription().c_str());
line << " -> " << bestNext->action->getDescription();
}
}
printf("\n");
std::cout << line.str() << '\n';
}
printf("MCTS: Tree stats - max depth:%d, total nodes:%d, root visits:%d\n",
result.searchDepth,
result.nodesEvaluated,
rootNode->visitCount);
std::cout << "MCTS: Tree stats - max depth:" << result.searchDepth
<< ", total nodes:" << result.nodesEvaluated
<< ", root visits:" << rootNode->visitCount << '\n';
// Log best sequence from chosen action (following most-visited path)
std::vector<const MCTSNode*> bestSequence;
@@ -940,7 +938,10 @@ auto AbstractMCTSAI::LogSearchResults(
}
if (!bestSequence.empty()) {
printf("MCTS: Best sequence from chosen action (final: %.2f):\n", sequenceScore);
std::ostringstream sequenceLine;
sequenceLine << std::fixed << std::setprecision(2)
<< "MCTS: Best sequence from chosen action (final: " << sequenceScore << "):";
std::cout << sequenceLine.str() << '\n';
int displayedStep = 0;
for (size_t i = 0; i < bestSequence.size(); ++i) {
const auto* node = bestSequence[i];
@@ -949,29 +950,31 @@ auto AbstractMCTSAI::LogSearchResults(
if (i > 0 && node->parent && node->parent->IsChanceNode()) { continue; }
displayedStep++;
printf(" %d.", displayedStep);
if (node->action) { printf(" %s", node->action->getDescription().c_str()); }
std::ostringstream line;
line << " " << displayedStep << ".";
if (node->action) { line << ' ' << node->action->getDescription(); }
// If this is a chance node, display outcome probabilities and scores
if (node->IsChanceNode() && !node->outcomeProbabilities.empty()) {
printf(" [");
line << " [";
for (size_t j = 0; j < node->outcomeProbabilities.size(); ++j) {
if (j > 0) printf(", ");
if (j > 0) { line << ", "; }
const double prob = node->outcomeProbabilities[j] * 100;
// Show lookahead score for each outcome if child exists
if (j < node->children.size() && node->children[j]->visitCount > 0) {
printf("%.0f%%->%.1f", prob, node->children[j]->lookaheadScore);
line << std::fixed << std::setprecision(0) << prob << "%->"
<< std::setprecision(1) << node->children[j]->lookaheadScore;
} else {
printf("%.0f%%->?", prob);
line << std::fixed << std::setprecision(0) << prob << "%->?";
}
}
printf("]");
line << "]";
}
printf(" (visits:%d, immediate:%.2f, lookahead:%.2f)\n",
node->visitCount,
node->immediateScore,
node->lookaheadScore);
line << std::fixed << std::setprecision(2) << " (visits:" << node->visitCount
<< ", immediate:" << node->immediateScore << ", lookahead:" << node->lookaheadScore
<< ")";
std::cout << line.str() << '\n';
// For non-root nodes in the sequence, show what the top alternatives were
// Skip showing alternatives for chance nodes (they have outcome children, not action
@@ -991,17 +994,17 @@ auto AbstractMCTSAI::LogSearchResults(
});
// Show top 3 alternatives at this decision point
printf(" Alternatives at this node (%zu total):\n", siblings.size());
std::cout << " Alternatives at this node (" << siblings.size() << " total):\n";
const size_t topN = std::min(siblings.size(), size_t(3));
for (size_t j = 0; j < topN; ++j) {
const auto* alt = siblings[j];
printf(" [%zu] visits:%d immediate:%.2f lookahead:%.2f",
j,
alt->visitCount,
alt->immediateScore,
alt->lookaheadScore);
if (alt->action) { printf(" %s", alt->action->getDescription().c_str()); }
printf("\n");
std::ostringstream altLine;
altLine << std::fixed << std::setprecision(2) << " [" << j
<< "] visits:" << alt->visitCount
<< " immediate:" << alt->immediateScore
<< " lookahead:" << alt->lookaheadScore;
if (alt->action) { altLine << ' ' << alt->action->getDescription(); }
std::cout << altLine.str() << '\n';
}
}
}
@@ -1013,7 +1016,7 @@ auto AbstractMCTSAI::DumpTreeToFile(const MCTSNode* root, const std::string& fil
std::ofstream out(filepath);
if (!out) {
fprintf(stderr, "Failed to open dump file: %s\n", filepath.c_str());
std::cerr << "Failed to open dump file: " << filepath << '\n';
return;
}
@@ -1042,7 +1045,7 @@ auto AbstractMCTSAI::DumpTreeToFile(const MCTSNode* root, const std::string& fil
out << "\n=== End of Tree Dump ===\n";
out.close();
printf("MCTS: Tree dumped to %s\n", filepath.c_str());
std::cout << "MCTS: Tree dumped to " << filepath << '\n';
}
auto AbstractMCTSAI::DumpNodeRecursive(
@@ -1,3 +1,4 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("//tools:copts.bzl", "COPTS")
cc_library(
@@ -141,9 +141,8 @@ public:
// Default implementation: no filtering, so filtered index = original index
[[nodiscard]] virtual size_t mapFilteredIndexToOriginal(
size_t filteredIndex,
const MCTSGameState& state) const {
const MCTSGameState& /*state*/) const {
// Default: no filtering, index stays the same
(void)state; // Suppress unused parameter warning
return filteredIndex;
}
@@ -158,4 +157,4 @@ public:
} // namespace mcts
} // namespace shardok
#endif // EAGLE0_MCTS_GAME_ENGINE_HPP
#endif // EAGLE0_MCTS_GAME_ENGINE_HPP
@@ -6,8 +6,11 @@
#define EAGLE0_ABSTRACT_MCTSNODE_HPP
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <memory>
#include <sstream>
#include <vector>
#include "MCTSAction.hpp"
@@ -205,13 +208,13 @@ struct MCTSNode {
isMaximizingPlayer ? child->lookaheadScore : -child->lookaheadScore;
const double explorationValue =
explorationConstant * std::sqrt(std::log(visitCount) / child->visitCount);
printf(" UCB: %s lookahead=%.2f expl=%.2f (+%.2f) = %.2f [%s]\n",
isMaximizingPlayer ? "MAX" : "MIN",
child->lookaheadScore,
exploitationValue,
explorationValue,
value,
child->action ? child->action->getDescription().c_str() : "root");
std::ostringstream line;
line << std::fixed << std::setprecision(2)
<< " UCB: " << (isMaximizingPlayer ? "MAX" : "MIN")
<< " lookahead=" << child->lookaheadScore << " expl=" << exploitationValue
<< " (+" << explorationValue << ") = " << value << " ["
<< (child->action ? child->action->getDescription() : "root") << "]";
std::cout << line.str() << '\n';
}
if (value > bestValue) {
+1 -30
View File
@@ -1,3 +1,4 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools:copts.bzl", "COPTS")
cc_library(
@@ -27,36 +28,6 @@ cc_binary(
deps = [":shardok-server-lib"],
)
cc_binary(
name = "shardok-inspector",
srcs = ["shardok_inspector.cpp"],
copts = COPTS,
data = [
"//src/main/resources/net/eagle0/shardok:battalion_types",
"//src/main/resources/net/eagle0/shardok:settings",
"//src/main/resources/net/eagle0/shardok/maps",
],
visibility = ["//visibility:public"],
deps = [
"//src/main/cpp/net/eagle0/common:byte_vector",
"//src/main/cpp/net/eagle0/common:filesystem_utils",
"//src/main/cpp/net/eagle0/common:tsv_parser",
"//src/main/cpp/net/eagle0/shardok/library:engine",
],
)
cc_binary(
name = "corrupt-game-checker",
srcs = ["CorruptGameChecker.cpp"],
copts = COPTS,
deps = [
"//src/main/cpp/net/eagle0/common:byte_vector",
"//src/main/cpp/net/eagle0/common:filesystem_utils",
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
"//src/main/protobuf/net/eagle0/common:shardok_internal_interface_cc_grpc",
],
)
cc_binary(
name = "hex-map-migrator",
srcs = ["HexMapMigrator.cpp"],
@@ -1,46 +0,0 @@
//
// Created by Dan Crosby on 1/9/22.
//
#include "src/main/cpp/net/eagle0/common/byte_vector.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
#include "src/main/protobuf/net/eagle0/common/shardok_internal_interface.pb.h"
#include "src/main/protobuf/net/eagle0/shardok/storage/game.pb.h"
auto main(int argc, char** argv) -> int {
char* path = argv[1];
printf("Checking file %s\n", path);
auto bytes = byte_vector::FromPath(path);
net::eagle0::shardok::storage::Game game;
game.ParseFromArray(bytes.data(), bytes.size());
printf("The game ID is %s\n", game.game_id().c_str());
printf("The size is %lu\n", bytes.size());
const int arCount = game.action_result_size();
printf("There are %d results\n", arCount);
for (int arIndex = 0; arIndex < arCount; arIndex++) {
shardok::GameStateW gameState =
shardok::GameStateW::FromByteString(game.action_result(arIndex).state_after_fb());
const auto* hexMap = gameState->hex_map();
for (int terrainIndex = 0; terrainIndex < hexMap->terrain()->size(); terrainIndex++) {
const auto* terrain = hexMap->terrain()->Get(terrainIndex);
if (terrain->type() == net::eagle0::shardok::storage::fb::Terrain_::Type_STILL_WATER)
continue;
if (terrain->type() == net::eagle0::shardok::storage::fb::Terrain_::Type_RIVER)
continue;
if (terrain->modifier().bridge().present()) {
printf("Caught one! arIndex %d, terrainIndex %d\n", arIndex, terrainIndex);
printf("Action result was %s\n",
game.action_result(arIndex).action_result().DebugString().c_str());
exit(0);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More