├── agent-ignore.txt ├── resources ├── public │ ├── .gitignore │ ├── images │ │ └── favicon.ico │ └── iconfont │ │ ├── material-icons.woff │ │ └── material-icons.woff2 ├── .gitignore ├── skyreplays.properties ├── icon.ico ├── icon1024.png ├── icon128.png ├── icon16.png ├── icon256.png ├── icon32.png ├── icon48.png ├── icon512.png ├── icon64.png ├── FAsdfz.properties ├── 186669__fordps3__computer-boop.wav ├── skylobby-web.properties ├── skylobby-stream-fix.properties └── skylobby.desktop ├── .clj-kondo ├── .gitignore ├── ci-config.edn └── config.edn ├── cryogen ├── content │ ├── css │ │ ├── example.css │ │ └── sassexample.scss │ ├── img │ │ ├── initial-ui.png │ │ ├── battle-and-bots.png │ │ ├── failed-dworld-minimap.png │ │ └── battle-minimap-and-resources.png │ ├── md │ │ └── pages │ │ │ └── index.md │ └── config.edn ├── themes │ ├── lotus │ │ ├── config.edn │ │ ├── html │ │ │ ├── tags.html │ │ │ ├── home.html │ │ │ ├── tag.html │ │ │ ├── archives.html │ │ │ ├── author.html │ │ │ ├── prev-next.html │ │ │ ├── post-content.html │ │ │ ├── page.html │ │ │ ├── previews.html │ │ │ ├── post.html │ │ │ └── 404.html │ │ ├── README.md │ │ └── css │ │ │ ├── _buttons.scss │ │ │ ├── lotus-highlightjs.min.css │ │ │ ├── _variables.scss │ │ │ ├── _layout.scss │ │ │ └── _menu.scss │ ├── blue │ │ ├── html │ │ │ ├── tags.html │ │ │ ├── tag.html │ │ │ ├── author.html │ │ │ ├── archives.html │ │ │ ├── home.html │ │ │ ├── post-content.html │ │ │ ├── previews.html │ │ │ ├── page.html │ │ │ ├── 404.html │ │ │ └── post.html │ │ └── css │ │ │ └── screen.css │ ├── nucleus │ │ ├── html │ │ │ ├── tags.html │ │ │ ├── tag.html │ │ │ ├── archives.html │ │ │ ├── author.html │ │ │ ├── home.html │ │ │ ├── post-content.html │ │ │ ├── previews.html │ │ │ ├── page.html │ │ │ ├── 404.html │ │ │ └── post.html │ │ └── css │ │ │ ├── buttons.css │ │ │ ├── reset.css │ │ │ ├── typography.css │ │ │ └── menu.css │ └── blue_centered │ │ └── html │ │ ├── tags.html │ │ ├── tag.html │ │ ├── author.html │ │ ├── archives.html │ │ ├── home.html │ │ ├── post-content.html │ │ ├── previews.html │ │ ├── page.html │ │ ├── 404.html │ │ └── post.html ├── src │ └── cryogen │ │ ├── core.clj │ │ └── server.clj ├── .gitignore ├── README.md ├── deps.edn └── project.clj ├── native-res ├── linux │ └── META-INF │ │ └── native-image │ │ └── skylobby │ │ ├── java │ │ ├── proxy-config.json │ │ ├── predefined-classes-config.json │ │ ├── serialization-config.json │ │ └── resource-config.json │ │ └── clj │ │ └── .gitignore └── windows │ └── META-INF │ └── native-image │ └── skylobby │ ├── java │ ├── proxy-config.json │ ├── .gitignore │ ├── predefined-classes-config.json │ ├── serialization-config.json │ └── resource-config.json │ └── clj │ └── .gitignore ├── repo ├── io │ └── github │ │ └── memo33 │ │ └── jsquish │ │ ├── 2.0.1 │ │ ├── jsquish-2.0.1.jar.sha1 │ │ ├── jsquish-2.0.1.pom.sha1 │ │ ├── jsquish-2.0.1.jar │ │ └── jsquish-2.0.1.pom │ │ └── maven-metadata-local.xml └── gr │ └── zdimensions │ └── jsquish │ ├── 1.0 │ ├── jsquish-1.0.jar │ └── jsquish-1.0.pom │ └── maven-metadata-local.xml ├── jpackage ├── linux ├── windows ├── mac └── lobby ├── package └── win │ └── Elevate.exe ├── src ├── cljs │ └── skylobby │ │ ├── css.cljs │ │ ├── view │ │ ├── settings.cljs │ │ ├── tasks.cljs │ │ ├── side_nav.cljs │ │ ├── server_nav.cljs │ │ └── servers_nav.cljs │ │ ├── web.cljs │ │ └── util.cljs └── clj │ ├── skylobby │ ├── fx │ │ ├── text_ext.clj │ │ ├── font_icon.clj │ │ ├── flag_icon.clj │ │ ├── tooltip_nofocus.clj │ │ ├── virtualized_scroll_pane.clj │ │ ├── color.clj │ │ ├── server_tab.clj │ │ ├── battles_window.clj │ │ ├── channels.clj │ │ ├── battle_chat.clj │ │ └── event │ │ │ └── direct.clj │ ├── discord.clj │ └── download │ │ └── butler.clj │ └── spring_lobby │ ├── sound.clj │ ├── spring │ └── uikeys.clj │ └── replays.clj ├── native-image-agent-filter.json ├── jvm-flags ├── .gitmodules ├── package.json ├── graal ├── clj │ └── skylobby │ │ ├── cli │ │ └── util.clj │ │ ├── replay.clj │ │ ├── event │ │ └── user.clj │ │ ├── client │ │ ├── stls.clj │ │ ├── message.clj │ │ └── gloss.clj │ │ ├── chat.clj │ │ ├── battle.clj │ │ ├── lua.clj │ │ ├── git.clj │ │ ├── task.clj │ │ └── config.clj └── cljc │ └── skylobby │ ├── common │ └── util.cljc │ └── data.cljc ├── dist ├── arch │ ├── skylobby.desktop │ └── skylobby └── linux │ └── skylobby.desktop ├── tests.edn ├── test └── clj │ └── skylobby │ ├── fx │ ├── flag_icon_test.clj │ ├── main_test.clj │ ├── mods_test.clj │ ├── server_tab_test.clj │ ├── engines_test.clj │ ├── map_sync_test.clj │ ├── mod_sync_test.clj │ ├── channels_test.clj │ ├── minimap_test.clj │ ├── chat_test.clj │ ├── engine_sync_test.clj │ ├── battle_chat_test.clj │ ├── battles_window_test.clj │ ├── battles_buttons_test.clj │ ├── pick_spring_root_test.clj │ ├── sync_test.clj │ ├── user_test.clj │ ├── root_test.clj │ ├── host_battle_test.clj │ ├── matchmaking_test.clj │ ├── server_test.clj │ ├── scenarios_test.clj │ ├── reset_password_test.clj │ ├── register_test.clj │ ├── color_test.clj │ ├── import_test.clj │ ├── settings_test.clj │ ├── tasks_test.clj │ ├── rapid_test.clj │ ├── spring_info_test.clj │ ├── main_tabs_test.clj │ ├── welcome_test.clj │ ├── battles_table_test.clj │ ├── console_test.clj │ ├── bottom_bar_test.clj │ ├── download_test.clj │ ├── channel_test.clj │ ├── spring_options_test.clj │ ├── maps_test.clj │ ├── players_table_test.clj │ └── battle_test.clj │ ├── task │ └── handler_test.clj │ ├── task_test.clj │ ├── client │ └── gloss_test.clj │ ├── fx_test.clj │ └── spads_test.clj ├── .gitignore ├── dev └── clj │ ├── test │ └── tests.clj │ ├── native │ └── native_image.clj │ ├── jar │ └── uberjar.clj │ ├── user │ └── repl.clj │ └── graal │ └── repl.clj ├── shadow-cljs.edn ├── .github └── workflows │ ├── lint.yml │ ├── changelog.yml │ ├── pages.yml │ └── test.yml ├── update-native-res.sh ├── update-native-res.ps1 ├── LICENSE └── README.md /agent-ignore.txt: -------------------------------------------------------------------------------- 1 | io.netty 2 | -------------------------------------------------------------------------------- /resources/public/.gitignore: -------------------------------------------------------------------------------- 1 | /js/ 2 | -------------------------------------------------------------------------------- /resources/.gitignore: -------------------------------------------------------------------------------- 1 | /skylobby.version 2 | -------------------------------------------------------------------------------- /resources/skyreplays.properties: -------------------------------------------------------------------------------- 1 | arguments="skyreplays" 2 | -------------------------------------------------------------------------------- /.clj-kondo/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !config.edn 4 | !ci-config.edn 5 | -------------------------------------------------------------------------------- /cryogen/content/css/example.css: -------------------------------------------------------------------------------- 1 | a { 2 | text-decoration-style: dashed; 3 | } -------------------------------------------------------------------------------- /native-res/linux/META-INF/native-image/skylobby/java/proxy-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/java/proxy-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /native-res/linux/META-INF/native-image/skylobby/clj/.gitignore: -------------------------------------------------------------------------------- 1 | /initialized-classes.txt 2 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/clj/.gitignore: -------------------------------------------------------------------------------- 1 | /initialized-classes.txt 2 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/java/.gitignore: -------------------------------------------------------------------------------- 1 | /.lock 2 | /agent-pid* 3 | -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon.ico -------------------------------------------------------------------------------- /repo/io/github/memo33/jsquish/2.0.1/jsquish-2.0.1.jar.sha1: -------------------------------------------------------------------------------- 1 | 7fda6fc496a1a368d3b3b4d6da37b84b8b8e392f -------------------------------------------------------------------------------- /repo/io/github/memo33/jsquish/2.0.1/jsquish-2.0.1.pom.sha1: -------------------------------------------------------------------------------- 1 | 1e02650f05b4eb08d4e3a0893062a7145b1b0200 -------------------------------------------------------------------------------- /resources/icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon1024.png -------------------------------------------------------------------------------- /resources/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon128.png -------------------------------------------------------------------------------- /resources/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon16.png -------------------------------------------------------------------------------- /resources/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon256.png -------------------------------------------------------------------------------- /resources/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon32.png -------------------------------------------------------------------------------- /resources/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon48.png -------------------------------------------------------------------------------- /resources/icon512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon512.png -------------------------------------------------------------------------------- /resources/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/icon64.png -------------------------------------------------------------------------------- /cryogen/themes/lotus/config.edn: -------------------------------------------------------------------------------- 1 | {:sass-src ["css"] 2 | :resources ["img"]} 3 | -------------------------------------------------------------------------------- /jpackage/linux: -------------------------------------------------------------------------------- 1 | --linux-shortcut 2 | --icon "resources/icon128.png" 3 | --resource-dir "dist/linux" 4 | -------------------------------------------------------------------------------- /package/win/Elevate.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/package/win/Elevate.exe -------------------------------------------------------------------------------- /resources/FAsdfz.properties: -------------------------------------------------------------------------------- 1 | extension=sdfz 2 | mime-type=application/sdfz 3 | description=Spring Replay 4 | -------------------------------------------------------------------------------- /cryogen/content/css/sassexample.scss: -------------------------------------------------------------------------------- 1 | body { 2 | a { 3 | text-decoration-style: dashed; 4 | } 5 | } -------------------------------------------------------------------------------- /cryogen/content/img/initial-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/cryogen/content/img/initial-ui.png -------------------------------------------------------------------------------- /resources/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/public/images/favicon.ico -------------------------------------------------------------------------------- /src/cljs/skylobby/css.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.css) 2 | 3 | 4 | (def header-class 5 | "f4 ba bw1 br3 no-underline pv2 ph3 dib") 6 | -------------------------------------------------------------------------------- /cryogen/content/img/battle-and-bots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/cryogen/content/img/battle-and-bots.png -------------------------------------------------------------------------------- /native-image-agent-filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "excludeClasses": "io.netty.**" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /resources/186669__fordps3__computer-boop.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/186669__fordps3__computer-boop.wav -------------------------------------------------------------------------------- /cryogen/content/img/failed-dworld-minimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/cryogen/content/img/failed-dworld-minimap.png -------------------------------------------------------------------------------- /repo/gr/zdimensions/jsquish/1.0/jsquish-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/repo/gr/zdimensions/jsquish/1.0/jsquish-1.0.jar -------------------------------------------------------------------------------- /resources/public/iconfont/material-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/public/iconfont/material-icons.woff -------------------------------------------------------------------------------- /resources/public/iconfont/material-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/resources/public/iconfont/material-icons.woff2 -------------------------------------------------------------------------------- /cryogen/content/img/battle-minimap-and-resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/cryogen/content/img/battle-minimap-and-resources.png -------------------------------------------------------------------------------- /resources/skylobby-web.properties: -------------------------------------------------------------------------------- 1 | arguments="headless" 2 | java-options=-Xmx2g -XX:+ExitOnOutOfMemoryError -XX:+DoEscapeAnalysis -XX:+UseCompressedOops 3 | -------------------------------------------------------------------------------- /repo/io/github/memo33/jsquish/2.0.1/jsquish-2.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skynet-gh/skylobby/HEAD/repo/io/github/memo33/jsquish/2.0.1/jsquish-2.0.1.jar -------------------------------------------------------------------------------- /jpackage/windows: -------------------------------------------------------------------------------- 1 | --win-menu 2 | --type "msi" 3 | --icon "resources/icon.ico" 4 | --win-dir-chooser 5 | --java-options "-Dprism.allowhidpi=true -Dglass.win.minHiDPI=1" 6 | -------------------------------------------------------------------------------- /native-res/linux/META-INF/native-image/skylobby/java/predefined-classes-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type":"agent-extracted", 4 | "classes":[ 5 | ] 6 | } 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /native-res/linux/META-INF/native-image/skylobby/java/serialization-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "types":[ 3 | ], 4 | "lambdaCapturingTypes":[ 5 | ], 6 | "proxies":[ 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/java/predefined-classes-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type":"agent-extracted", 4 | "classes":[ 5 | ] 6 | } 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /jvm-flags: -------------------------------------------------------------------------------- 1 | -XX:+UseZGC -Xmx2g -XX:+ExitOnOutOfMemoryError -XX:+DoEscapeAnalysis -XX:+UseCompressedOops --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED 2 | -------------------------------------------------------------------------------- /jpackage/mac: -------------------------------------------------------------------------------- 1 | --icon "resources/icon.ico" 2 | --java-options "-Xmx2g -XX:+ExitOnOutOfMemoryError -XX:+DoEscapeAnalysis -XX:+UseCompressedOops --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED" 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/lua-parser"] 2 | path = lib/lua-parser 3 | url = https://github.com/bkiers/lua-parser.git 4 | [submodule "lib/grammars-v4"] 5 | path = lib/grammars-v4 6 | url = https://github.com/antlr/grammars-v4.git 7 | -------------------------------------------------------------------------------- /cryogen/content/md/pages/index.md: -------------------------------------------------------------------------------- 1 | {:title "Index" 2 | :layout :page 3 | :page-index 0 4 | :navbar? true 5 | :home? true} 6 | 7 | ## skylobby 8 | 9 | Yet another Spring lobby. 10 | 11 | [Development diary](/pages/development/) 12 | -------------------------------------------------------------------------------- /resources/skylobby-stream-fix.properties: -------------------------------------------------------------------------------- 1 | java-options=-XX:+UseZGC -Xmx2g -Dprism.order=sw -XX:+ExitOnOutOfMemoryError -XX:+DoEscapeAnalysis -XX:+UseCompressedOops --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "dayjs": "^1.11.0", 4 | "material-icons": "^1.10.6", 5 | "react": "^18.0.0", 6 | "react-dom": "^18.0.0" 7 | }, 8 | "devDependencies": { 9 | "shadow-cljs": "^2.20.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cryogen/src/cryogen/core.clj: -------------------------------------------------------------------------------- 1 | (ns cryogen.core 2 | (:require [cryogen-core.compiler :refer [compile-assets-timed]] 3 | [cryogen-core.plugins :refer [load-plugins]])) 4 | 5 | (defn -main [] 6 | (load-plugins) 7 | (compile-assets-timed) 8 | (System/exit 0)) 9 | -------------------------------------------------------------------------------- /graal/clj/skylobby/cli/util.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.cli.util) 2 | 3 | 4 | (set! *warn-on-reflection* true) 5 | 6 | 7 | (defn print-and-exit [exit-code & messages] 8 | (doseq [message messages] 9 | (println message)) 10 | (shutdown-agents) 11 | (System/exit exit-code)) 12 | -------------------------------------------------------------------------------- /cryogen/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *jar 4 | /lib/ 5 | /classes/ 6 | /target/ 7 | /checkouts/ 8 | .lein-deps-sum 9 | .lein-repl-history 10 | .lein-plugins/ 11 | .lein-failures 12 | /public/ 13 | /themes/lotus/css/blog.css 14 | /themes/lotus/css/blog.css.map 15 | .cpcache/ 16 | -------------------------------------------------------------------------------- /dist/arch/skylobby.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=skylobby 4 | Exec=skylobby 5 | Icon=skylobby 6 | Comment=Spring RTS games lobby client 7 | Terminal=false 8 | Categories=Game;StrategyGame; 9 | Keywords=real-time;strategy;lobby;network;multiplayer;spring;game; 10 | -------------------------------------------------------------------------------- /resources/skylobby.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=skylobby 4 | Exec=skylobby 5 | Icon=skylobby 6 | Comment=Spring RTS games lobby client 7 | Terminal=false 8 | Categories=Game;StrategyGame; 9 | Keywords=real-time;strategy;lobby;network;multiplayer;spring;game; 10 | -------------------------------------------------------------------------------- /tests.edn: -------------------------------------------------------------------------------- 1 | #kaocha/v1 2 | {:kaocha/tests 3 | [{:kaocha.testable/type :kaocha.type/clojure.test 4 | :kaocha.testable/id :unit 5 | :kaocha/ns-patterns ["-test$"] 6 | :kaocha/source-paths ["src/clj" "graal/clj"] 7 | :kaocha/test-paths ["test/clj"]}] 8 | :kaocha.watch/type :hawk} 9 | -------------------------------------------------------------------------------- /graal/clj/skylobby/replay.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.replay) 2 | 3 | 4 | (set! *warn-on-reflection* true) 5 | 6 | 7 | (defprotocol ReplayIndex 8 | (all-replays [this]) 9 | (replay-by-id [this replay-id]) 10 | (replay-by-path [this path]) 11 | (update-replay [this replay]) 12 | (update-replays [this replays])) 13 | -------------------------------------------------------------------------------- /dist/linux/skylobby.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=skylobby 4 | Exec=/opt/skylobby/bin/skylobby 5 | Icon=/opt/skylobby/lib/skylobby.png 6 | Comment=Spring RTS games lobby client 7 | Terminal=false 8 | Categories=Game;StrategyGame; 9 | Keywords=real-time;strategy;lobby;network;multiplayer;spring;game; 10 | MimeType= 11 | -------------------------------------------------------------------------------- /src/cljs/skylobby/view/settings.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.view.settings 2 | (:require 3 | [skylobby.view.side-nav :as side-nav])) 4 | 5 | 6 | (defn settings-page [_] 7 | [:div {:class "flex"} 8 | [side-nav/side-nav] 9 | [:div {:class "flex-auto justify-center"} 10 | [:div {:class "flex justify-center mb2 f2"} 11 | "Settings"]]]) 12 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/flag_icon_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.flag-icon-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [skylobby.fx.flag-icon :as fx.flag-icon])) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (deftest flag-icon 11 | (is (map? 12 | (fx.flag-icon/flag-icon 13 | {:country-code "US"})))) 14 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/main_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.main-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.main :as fx.main])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest main-window 12 | (is (map? 13 | (fx.main/main-window 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/mods_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.mods-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.mods :as fx.mods])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest mods-view 12 | (is (map? 13 | (fx.mods/mods-view-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/server_tab_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.server-tab-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.server-tab :as fx.server-tab])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest server-tab 12 | (is (map? 13 | (fx.server-tab/server-tab {:fx/context (fx/create-context nil)})))) 14 | -------------------------------------------------------------------------------- /repo/gr/zdimensions/jsquish/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | gr.zdimensions 4 | jsquish 5 | 6 | 1.0 7 | 8 | 1.0 9 | 10 | 20201225204629 11 | 12 | 13 | -------------------------------------------------------------------------------- /repo/io/github/memo33/jsquish/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.github.memo33 4 | jsquish 5 | 6 | 2.0.1 7 | 8 | 2.0.1 9 | 10 | 20220626092300 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/engines_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.engines-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.engines :as fx.engines])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest engines-view 12 | (is (map? 13 | (fx.engines/engines-view-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/map_sync_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.map-sync-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.map-sync :as fx.map-sync])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest map-sync 12 | (is (map? 13 | (fx.map-sync/map-sync-pane-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/mod_sync_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.mod-sync-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.mod-sync :as fx.mod-sync])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest mod-sync 12 | (is (map? 13 | (fx.mod-sync/mod-sync-pane-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/channels_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.channels-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.channels :as fx.channels])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest channels-table 12 | (is (map? 13 | (fx.channels/channels-table 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/minimap_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.minimap-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.minimap :as fx.minimap])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest minimap-pane-impl 12 | (is (map? 13 | (fx.minimap/minimap-pane-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | *.swp 3 | /*.bin 4 | /*.db 5 | /*.deb 6 | /*.dll 7 | /*.exe 8 | /*.jar 9 | /*.log* 10 | /*.rpm 11 | /.calva/ 12 | /.classpath 13 | /.cpcache/ 14 | /.lsp/ 15 | /.nrepl-port 16 | /.project 17 | /.settings/ 18 | /.shadow-cljs/ 19 | /classes/ 20 | /native-image-args 21 | /node_modules/ 22 | /pom.xml 23 | /skylobby-*.msi 24 | /skylobby 25 | /target/ 26 | /trace.edn 27 | /*.build_artifacts.txt 28 | -------------------------------------------------------------------------------- /dist/arch/skylobby: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec /usr/lib/jvm/java-17-openjdk/bin/java \ 3 | -Dglass.gtk.uiScale=1.1 \ 4 | -XX:+UseZGC \ 5 | -Xmx2g \ 6 | -XX:+ExitOnOutOfMemoryError \ 7 | -XX:+DoEscapeAnalysis \ 8 | -XX:+UseCompressedOops \ 9 | --add-opens=java.base/java.nio=ALL-UNNAMED \ 10 | --add-opens=java.base/sun.nio.ch=ALL-UNNAMED \ 11 | -jar '/usr/share/java/skylobby/skylobby.jar' \ 12 | "$@" 13 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/chat_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.chat-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.chat :as fx.chat])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest chat-window-impl 12 | (is (map? 13 | (fx.chat/chat-window-impl 14 | {:fx/context (fx/create-context {:show-chat-window true})})))) 15 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Tags{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 9 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Tags{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 9 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Tags{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 9 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/engine_sync_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.engine-sync-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.engine-sync :as fx.engine-sync])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest engine-sync 12 | (is (map? 13 | (fx.engine-sync/engine-sync-pane-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/tags.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Tags{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 9 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /dev/clj/test/tests.clj: -------------------------------------------------------------------------------- 1 | (ns tests 2 | (:require 3 | [com.jakemccrary.test-refresh :refer [monitor-project]] 4 | hashp.core 5 | [pjstadig.humane-test-output])) 6 | 7 | 8 | (defn -main [] 9 | (pjstadig.humane-test-output/activate!) 10 | (monitor-project 11 | ["test/clj"] 12 | {:nses-and-selectors [:ignore [[(constantly true)]]] 13 | :do-not-monitor-keystrokes true 14 | :with-repl false})) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/battle_chat_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battle-chat-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.battle-chat :as fx.battle-chat])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battle-chat-window-impl 12 | (is (map? 13 | (fx.battle-chat/battle-chat-window-impl 14 | {:fx/context (fx/create-context {})})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/battles_window_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battles-window-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.battles-window :as fx.battles-window])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battles-window 12 | (is (map? 13 | (fx.battles-window/battles-window-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/battles_buttons_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battles-buttons-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.battles-buttons :as fx.battles-buttons])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battles-buttons-view 12 | (is (map? 13 | (fx.battles-buttons/battles-buttons-view 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/text_ext.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.text-ext 2 | (:require 3 | [cljfx.composite :as composite] 4 | [cljfx.fx.text :as fx.text]) 5 | (:import 6 | (org.fxmisc.richtext TextExt))) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (def props 13 | (merge 14 | fx.text/props 15 | (composite/props TextExt))) 16 | 17 | (def lifecycle 18 | (composite/describe TextExt 19 | :ctor [] 20 | :props props)) 21 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/pick_spring_root_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.pick-spring-root-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.pick-spring-root :as fx.pick-spring-root])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest pick-spring-root-view 12 | (is (map? 13 | (fx.pick-spring-root/pick-spring-root-view 14 | {:fx/context (fx/create-context nil)})))) 15 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:deps {:aliases [:cljs]} 2 | :builds 3 | {:frontend 4 | {:target :browser 5 | :output-dir "resources/public/js" 6 | :asset-path "/js" 7 | :modules {:main {:entries [skylobby.js] 8 | :init-fn skylobby.js/init}}} 9 | :main 10 | {:target :browser 11 | :output-dir "resources/public/js" 12 | :asset-path "/js" 13 | :modules {:main {:entries [skylobby.web] 14 | :init-fn skylobby.web/init}}}}} 15 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/home.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% include "/html/post-content.html" %} 5 | {% if disqus? %} 6 |
7 | View Comments 8 |
9 | {% endif %} 10 | 11 | {% with prev=post.prev next=post.next%} 12 | {% include "/html/prev-next.html" %} 13 | {% endwith %} 14 | 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/font_icon.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.font-icon 2 | (:require 3 | [cljfx.composite :as composite] 4 | [cljfx.lifecycle :as lifecycle]) 5 | (:import 6 | (org.kordamp.ikonli.javafx FontIcon))) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (def props 13 | (composite/props FontIcon 14 | :icon-literal [:setter lifecycle/scalar])) 15 | 16 | (def lifecycle 17 | (composite/describe FontIcon 18 | :ctor [] 19 | :props props)) 20 | -------------------------------------------------------------------------------- /jpackage/lobby: -------------------------------------------------------------------------------- 1 | --verbose 2 | --input target 3 | --main-jar "skylobby.jar" 4 | --name "skylobby" 5 | --main-class "spring_lobby.main" 6 | --vendor "skynet" 7 | --java-options "-XX:+UseZGC -Xmx2g -XX:+ExitOnOutOfMemoryError -XX:+DoEscapeAnalysis -XX:+UseCompressedOops --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED" 8 | --file-associations "resources/FAsdfz.properties" 9 | --add-launcher skylobby-stream-fix="resources/skylobby-stream-fix.properties" 10 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts Tagged "{{name}}"{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts Tagged "{{name}}"{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts Tagged "{{name}}"{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/sync_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.sync-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.sync :as fx.sync])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest sync-pane 12 | (is (map? 13 | (fx.sync/sync-pane 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.sync/sync-pane 17 | {:fx/context (fx/create-context nil) 18 | :issues [{}]})))) 19 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts Tagged "{{name}}"{% endblock %} 3 | {% block content %} 4 |
5 | 8 | 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /cryogen/README.md: -------------------------------------------------------------------------------- 1 | # Repo docs on gh-pages using Cryogen 2 | 3 | [GitHub Pages for this repo](https://skynet-gh/skylobby/) using [Cryogen](https://github.com/cryogen-project/cryogen). 4 | 5 | ### Dev 6 | 7 | To run a local server, run 8 | 9 | ``` 10 | lein serve 11 | ``` 12 | 13 | A browser should be opened to http://localhost:3000/skylobby 14 | 15 | ### Build 16 | 17 | GitHub actions now publishes content to the `gh-pages` branch automatically. To build manually, run 18 | 19 | ``` 20 | clj -M:build 21 | ``` 22 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/README.md: -------------------------------------------------------------------------------- 1 | # About Lotus 2 | An elegant and responsive theme for Cryogen by [Matthew Davidson](https://github.com/KingMob). To see it live, visit my site, [modulolotus.net](https://modulolotus.net). 3 | 4 | # Install 5 | 6 | 1. Change the `:theme` key in `content/config.edn` to "lotus". 7 | 2. Replace the GitHub and LinkedIn links in `base.html` with links to your own profiles. (More social media icons can be found in `icons.svg`.) 8 | 3. Replace the white lotus logo with the logo of your choice in `base.html`. 9 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/user_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.user-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.user :as fx.user])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest users-view 12 | (is (map? 13 | (fx.user/users-view-impl 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | 18 | (deftest users-table 19 | (is (map? 20 | (fx.user/users-table-impl 21 | {:fx/context (fx/create-context nil)})))) 22 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | workflow_dispatch: 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4.1.1 13 | - uses: DeLaGuardo/clojure-lint-action@master 14 | with: 15 | clj-kondo-args: --config-paths ^:replace [] --config .clj-kondo\ci-config.edn --lint src/clj graal/clj src/cljs graal/cljc 16 | github_token: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /repo/gr/zdimensions/jsquish/1.0/jsquish-1.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | gr.zdimensions 6 | jsquish 7 | 1.0 8 | POM was created from install:install-file 9 | 10 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/root_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.root-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.root :as fx.root])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battle-window 12 | (is (map? 13 | (fx.root/battle-window 14 | {:fx/context (fx/create-context nil) 15 | :screen-bounds {}})))) 16 | 17 | (deftest root-view 18 | (is (map? 19 | (fx.root/root-view-impl {:fx/context (fx/create-context nil)})))) 20 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/author.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts by {{author}} {% endblock %} 3 | {% block content %} 4 |
5 |
6 |

Posts by {{author}}

7 |
8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/host_battle_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.host-battle-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.host-battle :as fx.host-battle])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest host-battle-window 12 | (is (map? 13 | (fx.host-battle/host-battle-window-impl 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.host-battle/host-battle-window-impl 17 | {:fx/context (fx/create-context {:show-host-battle-window true})})))) 18 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/author.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts by {{author}} {% endblock %} 3 | {% block content %} 4 |
5 |
6 |

Posts by {{author}}

7 |
8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /graal/cljc/skylobby/common/util.cljc: -------------------------------------------------------------------------------- 1 | (ns skylobby.common.util) 2 | 3 | 4 | (defn server-type 5 | "Returns a keyword representing the type of server given its key. Used for dispatching actions 6 | based on these broad server groups." 7 | [server-key] 8 | (cond 9 | (string? server-key) :spring-lobby 10 | (= :local server-key) :singleplayer 11 | (map? server-key) 12 | (if (:host server-key) 13 | :direct-host 14 | :direct-client) 15 | :else nil)) 16 | 17 | (defn is-direct? [server-key] 18 | (#{:direct-host :direct-client} (server-type server-key))) 19 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/matchmaking_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.matchmaking-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.matchmaking :as fx.matchmaking])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest matchmaking-view 12 | (is (map? 13 | (fx.matchmaking/matchmaking-view 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest matchmaking-window 18 | (is (map? 19 | (fx.matchmaking/matchmaking-window-impl 20 | {:fx/context (fx/create-context nil)})))) 21 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/archives.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Archives{% endblock %} 3 | {% block content %} 4 |
5 | 8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/archives.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Archives{% endblock %} 3 | {% block content %} 4 |
5 | 8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /update-native-res.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jar="target/skylobby.jar" 4 | 5 | if [[ ! -f "$jar" ]] 6 | then 7 | ./build-jar.sh 8 | fi 9 | 10 | wget -N https://github.com/skynet-gh/clojure-native-image-agent/releases/download/v0.2.0%2Bfix-noclass%2Bcustom-ignore/clojure-native-image-agent.jar 11 | 12 | config="native-res/linux/META-INF/native-image/skylobby" 13 | 14 | java \ 15 | -javaagent:clojure-native-image-agent.jar=initialize-class=skylobby.main,output-dir=$config/clj,ignore-file=agent-ignore.txt \ 16 | -agentlib:native-image-agent=config-merge-dir=$config/java \ 17 | -jar $jar "$@" 18 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/archives.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Archives{% endblock %} 3 | {% block content %} 4 |
5 | 8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/server_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.server-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.server :as fx.server])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest servers-window 12 | (is (map? 13 | (fx.server/servers-window-impl 14 | {:fx/context (fx/create-context nil) 15 | :screen-bounds {}}))) 16 | (is (map? 17 | (fx.server/servers-window-impl 18 | {:fx/context (fx/create-context {:show-servers-window true}) 19 | :screen-bounds {}})))) 20 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/archives.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Archives{% endblock %} 3 | {% block content %} 4 |
5 | 8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/scenarios_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.scenarios-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.scenarios :as fx.scenarios])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest scenarios-root 12 | (is (map? 13 | (fx.scenarios/scenarios-root 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest scenarios-window 18 | (is (map? 19 | (fx.scenarios/scenarios-window-impl 20 | {:fx/context (fx/create-context nil) 21 | :screen-bounds {}})))) 22 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/reset_password_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.reset-password-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.reset-password :as fx.reset-password])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest reset-password-window 12 | (is (map? 13 | (fx.reset-password/reset-password-window-impl 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.reset-password/reset-password-window-impl 17 | {:fx/context (fx/create-context {:show-reset-password-window true})})))) 18 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/author.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts by {{author}} {% endblock %} 3 | {% block content %} 4 |
5 |
6 |

Posts by {{author}}

7 |
8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/author.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: Posts by {{author}} {% endblock %} 3 | {% block content %} 4 |
5 |
6 |

Posts by {{author}}

7 |
8 | {% for group in groups %} 9 |

{{group.group}}

10 | 17 | {% endfor %} 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/register_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.register-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.register :as fx.register])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest register-window 12 | (is (map? 13 | (fx.register/register-window-impl 14 | {:fx/context (fx/create-context nil) 15 | :screen-bounds {}}))) 16 | (is (map? 17 | (fx.register/register-window-impl 18 | {:fx/context (fx/create-context {:show-register-window true}) 19 | :screen-bounds {}})))) 20 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/css/_buttons.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .button { 4 | background: none; 5 | border: $default-border; 6 | position: relative; 7 | display: inline-block; 8 | padding: 7px 12px; 9 | margin: 0; 10 | width: auto; 11 | font-family: $wide-font-family; 12 | font-size: $button-font-size; 13 | text-transform: uppercase; 14 | 15 | cursor: pointer; 16 | text-align: left; 17 | text-decoration: none; 18 | letter-spacing: 2px; 19 | 20 | &:hover { 21 | background: $default-border-color; 22 | color: $white; 23 | } 24 | 25 | &:active { 26 | top: 1px; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/css/lotus-highlightjs.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#fbfbfb}.hljs,.hljs-subst{color:#000}.hljs-string,.hljs-meta,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-addition{color:#756bb1}.hljs-comment,.hljs-quote{color:#636363}.hljs-number,.hljs-regexp,.hljs-literal,.hljs-bullet,.hljs-link{color:#31a354}.hljs-deletion,.hljs-variable{color:#88f}.hljs-keyword,.hljs-selector-tag,.hljs-title,.hljs-section,.hljs-built_in,.hljs-doctag,.hljs-type,.hljs-tag,.hljs-name,.hljs-selector-id,.hljs-selector-class,.hljs-strong{color:#3182bd}.hljs-emphasis{font-style:italic}.hljs-attribute{color:#e6550d} -------------------------------------------------------------------------------- /cryogen/themes/blue/html/home.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% include "/html/post-content.html" %} 5 | {% if disqus? %} 6 |
7 | View Comments 8 |
9 | {% endif %} 10 | 11 |
12 | {% if post.prev %} 13 | « {{post.prev.title}} 14 | {% endif %} 15 | {% if post.next %} 16 | {{post.next.title}} » 17 | {% endif %} 18 |
19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/prev-next.html: -------------------------------------------------------------------------------- 1 |
2 | {% if prev %} 3 | 4 | {% else %} 5 | 6 | {% endif %} 7 | {% if next %} 8 | 9 | {% else %} 10 | 11 | {% endif %} 12 |
13 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/home.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% include "/html/post-content.html" %} 5 | {% if disqus? %} 6 |
7 | View Comments 8 |
9 | {% endif %} 10 | 11 |
12 | {% if post.prev %} 13 | « {{post.prev.title}} 14 | {% endif %} 15 | {% if post.next %} 16 | {{post.next.title}} » 17 | {% endif %} 18 |
19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/post-content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{post.date|date:longDate}}
4 | {% if post.author %} 5 | By: {{post.author}} 6 | {% endif %} 7 |
8 |

{{post.title}}

9 |
10 |
11 | {% if post.toc %}{{post.toc|safe}}{% endif %} 12 | {{post.content|safe}} 13 |
14 | {% if post.tags|not-empty %} 15 |
16 | Tags: 17 | {% for tag in post.tags %} 18 | {{tag.name}} 19 | {% endfor %} 20 |
21 | {% endif %} 22 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/post-content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{post.date|date:longDate}}
4 | {% if post.author %} 5 | By: {{post.author}} 6 | {% endif %} 7 |
8 |

{{post.title}}

9 |
10 |
11 | {% if post.toc %}{{post.toc|safe}}{% endif %} 12 | {{post.content|safe}} 13 |
14 | {% if post.tags|not-empty %} 15 |
16 | Tags: 17 | {% for tag in post.tags %} 18 | {{tag.name}} 19 | {% endfor %} 20 |
21 | {% endif %} 22 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/home.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% include "/html/post-content.html" %} 5 | {% if disqus? %} 6 |
7 | View Comments 8 |
9 | {% endif %} 10 | 11 |
12 | {% if post.prev %} 13 | « {{post.prev.title}} 14 | {% endif %} 15 | {% if post.next %} 16 | {{post.next.title}} » 17 | {% endif %} 18 |
19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/post-content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{post.date|date:longDate}} 4 | {% if post.author %} 5 | By: {{post.author}} 6 | {% endif %} 7 |
8 |

{{post.title}}

9 |
10 |
11 | {% if post.toc %}{{post.toc|safe}}{% endif %} 12 | {{post.content|safe}} 13 |
14 | {% if post.tags|not-empty %} 15 |
16 |
17 | Tags: 18 | {% for tag in post.tags %} 19 | {{tag.name}} 20 | {% endfor %} 21 |
22 | {% endif %} 23 |
24 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/color_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.color-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [skylobby.fx.color :as fx.color]) 5 | (:import 6 | (javafx.scene.paint Color))) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (deftest spring-color-to-javafx 13 | (is (= "0x000000ff" 14 | (str 15 | (fx.color/spring-color-to-javafx nil)))) 16 | (is (= "0x393000ff" 17 | (str 18 | (fx.color/spring-color-to-javafx 12345))))) 19 | 20 | (deftest javafx-color-to-spring 21 | (is (= 0 22 | (fx.color/javafx-color-to-spring nil))) 23 | (is (= 12345 24 | (fx.color/javafx-color-to-spring (Color/web "0x393000ff"))))) 25 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/import_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.import-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.import :as fx.import])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest importer-root 12 | (is (map? 13 | (fx.import/importer-root 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest import-window 18 | (is (map? 19 | (fx.import/import-window 20 | {:fx/context (fx/create-context nil) 21 | :screen-bounds {}}))) 22 | (is (map? 23 | (fx.import/import-window 24 | {:fx/context (fx/create-context {:show-importer true}) 25 | :screen-bounds {}})))) 26 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/settings_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.settings-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.settings :as fx.settings])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battle-settings 12 | (is (map? 13 | (fx.settings/battle-settings 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest settings-root 18 | (is (map? 19 | (fx.settings/settings-root 20 | {:fx/context (fx/create-context nil)})))) 21 | 22 | 23 | (deftest settings-window 24 | (is (map? 25 | (fx.settings/settings-window-impl 26 | {:fx/context (fx/create-context nil) 27 | :screen-bounds {}})))) 28 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/post-content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{post.title}}

4 | 10 |
11 |
12 |
13 | {% if post.toc %}{{post.toc|safe}}{% endif %} 14 | {{post.content|safe}} 15 |
16 | {% if post.tags|not-empty %} 17 |
18 |
19 | Tags: 20 | {% for tag in post.tags %} 21 | {{tag.name}} 22 | {% endfor %} 23 |
24 | {% endif %} 25 |
26 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/tasks_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.tasks-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.tasks :as fx.tasks])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest tasks-root 12 | (is (map? 13 | (fx.tasks/tasks-root 14 | {:fx/context (fx/create-context nil) 15 | :screen-bounds {}}))) 16 | ) 17 | 18 | (deftest tasks-window 19 | (is (map? 20 | (fx.tasks/tasks-window 21 | {:fx/context (fx/create-context nil) 22 | :screen-bounds {}}))) 23 | (is (map? 24 | (fx.tasks/tasks-window 25 | {:fx/context (fx/create-context {:show-tasks-window true}) 26 | :screen-bounds {}})))) 27 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/rapid_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.rapid-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.rapid :as fx.rapid])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest rapid-download-root 12 | (is (map? 13 | (fx.rapid/rapid-download-root 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest rapid-download-window 18 | (is (map? 19 | (fx.rapid/rapid-download-window 20 | {:fx/context (fx/create-context nil) 21 | :screen-bounds {}}))) 22 | (is (map? 23 | (fx.rapid/rapid-download-window 24 | {:fx/context (fx/create-context {:show-rapid-downloader true}) 25 | :screen-bounds {}})))) 26 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/spring_info_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.spring-info-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.spring-info :as fx.spring-info])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest spring-info-root 12 | (is (map? 13 | (fx.spring-info/spring-info-root 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.spring-info/spring-info-root 17 | {:fx/context (fx/create-context {:spring-crash-archive-not-found {}})})))) 18 | 19 | 20 | (deftest spring-info-window 21 | (is (map? 22 | (fx.spring-info/spring-info-window 23 | {:fx/context (fx/create-context nil) 24 | :screen-bounds {}})))) 25 | -------------------------------------------------------------------------------- /src/cljs/skylobby/view/tasks.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.view.tasks 2 | (:require 3 | [re-frame.core :as rf] 4 | [skylobby.view.side-nav :as side-nav])) 5 | 6 | 7 | (defn listen [query-v] 8 | @(rf/subscribe query-v)) 9 | 10 | (defn tasks-page [_] 11 | (let [{:keys [current-tasks tasks-by-kind]} (listen [:skylobby/tasks])] 12 | [:div {:class "flex"} 13 | [side-nav/side-nav] 14 | [:div {:class "flex-auto justify-center"} 15 | [:div {:class "flex justify-center mb2 f2"} 16 | "Tasks"] 17 | [:div 18 | "Current: " 19 | (str (count current-tasks))] 20 | [:div 21 | "Queued: " 22 | (str (count (mapcat second tasks-by-kind)))] 23 | [:div 24 | "Types: " 25 | (str (keys tasks-by-kind))]]])) 26 | -------------------------------------------------------------------------------- /cryogen/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.10.1"} 2 | ring-server/ring-server {:mvn/version "0.5.0"} 3 | ring/ring-devel {:mvn/version "1.8.2"} 4 | compojure/compojure {:mvn/version "1.6.2"} 5 | cryogen-flexmark/cryogen-flexmark {:mvn/version "0.1.4"} 6 | cryogen-core/cryogen-core {:mvn/version "0.4.0"}} 7 | :aliases {;; Run with `clojure -M:build` 8 | :build {:main-opts ["-m" "cryogen.core"]} 9 | ;; Start a server serving the blog: `clojure -X:serve` 10 | ;; (requires tools-deps 0.9.745+) 11 | :serve {:exec-fn cryogen.server/serve 12 | :exec-args {:port 3000}} 13 | :serve-fast {:exec-fn cryogen.server/serve 14 | :exec-args {:port 3000, :fast true}}}} 15 | -------------------------------------------------------------------------------- /update-native-res.ps1: -------------------------------------------------------------------------------- 1 | 2 | $jar = ".\target\skylobby.jar" 3 | 4 | if (!(Test-Path -Path $jar)) 5 | { 6 | .\build-jar.ps1 7 | } 8 | 9 | $agent = "clojure-native-image-agent" 10 | $agentjar = "$agent.jar" 11 | 12 | if (!(Test-Path -Path $agentjar)) 13 | { 14 | $url = "https://github.com/skynet-gh/$agent/releases/download/v0.2.0%2Bfix-noclass%2Bcustom-ignore/$agentjar" 15 | Write-Host "Downloading from $url to $agentjar" 16 | Invoke-WebRequest $url -OutFile $agentjar 17 | } 18 | 19 | $config = "native-res\windows\META-INF\native-image\skylobby" 20 | 21 | & "$env:GRAALVM_HOME\bin\java" ` 22 | -javaagent:clojure-native-image-agent.jar="initialize-class=skylobby.main,output-dir=$config\clj,ignore-file=agent-ignore.txt" ` 23 | -agentlib:native-image-agent="config-merge-dir=$config\java" ` 24 | -jar $jar $args 25 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/flag_icon.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.flag-icon 2 | (:require 3 | [taoensso.timbre :as log]) 4 | (:import 5 | (griffon.javafx.support.flagicons FlagIcon))) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (defn flag-image [^String country-code] 12 | (try 13 | (FlagIcon. country-code) 14 | (catch Exception e 15 | (log/trace e "Error creating flag icon")))) 16 | 17 | (def flag-image-memoized 18 | (memoize flag-image)) 19 | 20 | 21 | (defn flag-icon [{:keys [^String country-code no-text]}] 22 | (let [image (flag-image-memoized country-code)] 23 | (merge 24 | {:fx/type :label 25 | :text (if (and no-text image) "" (str country-code))} 26 | (when image 27 | {:graphic 28 | {:fx/type :image-view 29 | :image image}})))) 30 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/previews.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% for post in posts %} 5 |
6 |

{{post.title}}

7 |
8 | {% if post.author %} 9 |
{{post.author}}
10 | {% endif %} 11 |
{{post.date|date:longDate}}
12 |
13 |
14 | {{post.content|safe}} 15 | Continue reading → 16 |
17 | {% endfor %} 18 | 19 |
20 | {% if prev-uri %} 21 | « Prev 22 | {% endif %} 23 | {% if next-uri %} 24 | Next » 25 | {% endif %} 26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/main_tabs_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.main-tabs-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.main-tabs :as fx.main-tabs])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest my-channels-view-impl 12 | (is (map? 13 | (fx.main-tabs/my-channels-view-impl 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.main-tabs/my-channels-view-impl 17 | {:fx/context (fx/create-context {:by-server {"server" {:my-channels {"c1" {}}}}}) 18 | :server "sever"})))) 19 | 20 | 21 | (deftest battle-details 22 | (is (map? 23 | (fx.main-tabs/battle-details 24 | {:fx/context (fx/create-context nil)})))) 25 | 26 | 27 | (deftest main-tab-view 28 | (is (map? 29 | (fx.main-tabs/main-tab-view 30 | {:fx/context (fx/create-context nil)})))) 31 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/previews.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% for post in posts %} 5 |
6 |

{{post.title}}

7 |
8 | {% if post.author %} 9 |
{{post.author}}
10 | {% endif %} 11 |
{{post.date|date:longDate}}
12 |
13 |
14 | {{post.content|safe}} 15 | Continue reading → 16 |
17 | {% endfor %} 18 | 19 |
20 | {% if prev-uri %} 21 | « Prev 22 | {% endif %} 23 | {% if next-uri %} 24 | Next » 25 | {% endif %} 26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/page.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block subtitle %}: {{page.title}}{% endblock %} 3 | {% block meta %} 4 | {% if page.description %} 5 | 6 | {% comment %} OpenGraph tags {% endcomment %} 7 | 8 | {% endif %} 9 | 10 | 11 | 12 | {% endblock %} 13 | {% block content %} 14 |
15 | 18 | {% if page.toc %}{{page.toc|safe}}{% endif %} 19 | {{page.content|safe}} 20 | 21 | {% with prev=page.prev next=page.next%} 22 | {% include "/html/prev-next.html" %} 23 | {% endwith %} 24 | 25 |
26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/previews.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% for post in posts %} 5 |
6 |

{{post.title}}

7 | 13 |
14 | {{post.content|safe}} 15 |
16 | Continue reading → 17 |
18 | {% endfor %} 19 | 20 |
21 | {% if prev-uri %} 22 | 23 | {% endif %} 24 | {% if next-uri %} 25 | 26 | {% endif %} 27 |
28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Create Release When Changelog Changes 2 | on: 3 | push: 4 | branches: [ master ] 5 | paths: 6 | - 'CHANGELOG.md' 7 | workflow_dispatch: 8 | jobs: 9 | changelog: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v4.1.1 13 | - name: Get last version from tag 14 | id: lasttag 15 | shell: bash 16 | run: echo ::set-output name=version::$(git describe --abbrev=0 --tags --match '[0-9]*\.[0-9]*\.[0-9]*') 17 | - uses: coditory/changelog-parser@v1.0.2 18 | id: changelog 19 | - if: ${{ steps.changelog.outputs.version != steps.lasttag.outputs.version }} 20 | uses: ncipollo/release-action@v1.13.0 21 | with: 22 | body: ${{ steps.changelog.outputs.description }} 23 | prerelease: true 24 | tag: ${{ steps.changelog.outputs.version }} 25 | token: ${{ secrets.CREATE_RELEASE }} 26 | -------------------------------------------------------------------------------- /cryogen/project.clj: -------------------------------------------------------------------------------- 1 | (defproject cryogen "0.1.0" 2 | :description "Simple static site generator" 3 | :url "https://github.com/lacarmen/cryogen" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.10.1"] 7 | [ring/ring-devel "1.8.2"] 8 | [compojure "1.6.2"] 9 | [ring-server "0.5.0"] 10 | [cryogen-flexmark "0.1.4"] 11 | [cryogen-core "0.4.0"]] 12 | :plugins [[lein-ring "0.12.5"]] 13 | :main cryogen.core 14 | :ring {:init cryogen.server/init 15 | :handler cryogen.server/handler} 16 | :aliases {"serve" ["run" "-m" "cryogen.server"] 17 | "serve-fast" ["run" "-m" "cryogen.server" "fast"]}) 18 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/previews.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {% block content %} 3 |
4 | {% for post in posts %} 5 |
6 |

{{post.title}}

7 |
8 | {% if post.author %} 9 |
{{post.author}}
10 | {% endif %} 11 | {{post.date|date:longDate}} 12 |
13 |
14 | {{post.content|safe}} 15 |
16 | Continue reading → 17 |
18 | {% endfor %} 19 | 20 |
21 | {% if prev-uri %} 22 | « Prev 23 | {% endif %} 24 | {% if next-uri %} 25 | Next » 26 | {% endif %} 27 |
28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /src/clj/spring_lobby/sound.clj: -------------------------------------------------------------------------------- 1 | (ns spring-lobby.sound 2 | (:require 3 | [clojure.java.io :as io] 4 | [skylobby.fs :as fs] 5 | [taoensso.timbre :as log]) 6 | (:import 7 | (javafx.scene.media Media MediaPlayer))) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (def ring-resource-name 14 | "186669__fordps3__computer-boop.wav") 15 | 16 | 17 | (defn play-ring 18 | [{:keys [ring-sound-file ring-volume use-default-ring-sound]}] 19 | (future 20 | (try 21 | (let [ring-uri (if (or use-default-ring-sound (not ring-sound-file)) 22 | (str (io/resource ring-resource-name)) 23 | (some-> ring-sound-file fs/file .toURI str)) 24 | media (Media. ring-uri) 25 | media-player (MediaPlayer. media)] 26 | (.setVolume media-player (or ring-volume 1.0)) 27 | (.play media-player)) 28 | (catch Exception e 29 | (log/error e "Error playing ring"))))) 30 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/welcome_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.welcome-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.welcome :as fx.welcome])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest connect-button 12 | (is (map? 13 | (fx.welcome/connect-button {:fx/context (fx/create-context nil)})))) 14 | 15 | (deftest singleplayer-buttons 16 | (is (map? 17 | (fx.welcome/singleplayer-buttons {:fx/context (fx/create-context nil)})))) 18 | 19 | (deftest multiplayer-buttons 20 | (is (map? 21 | (fx.welcome/multiplayer-buttons {:fx/context (fx/create-context nil)})))) 22 | 23 | (deftest welcome-view 24 | (is (map? 25 | (fx.welcome/welcome-view {:fx/context (fx/create-context nil)}))) 26 | (is (map? 27 | (fx.welcome/welcome-view 28 | {:fx/context 29 | (fx/create-context 30 | {:by-server {:local {:battle {:battle-id :singleplayer}}}})})))) 31 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/css/buttons.css: -------------------------------------------------------------------------------- 1 | .button { 2 | position: relative; 3 | display: inline-block; 4 | padding: 7px 12px; 5 | margin: 0; 6 | width: auto; 7 | font-family: "Montserrat"; 8 | font-size: 16px; 9 | text-transform: uppercase; 10 | font-weight: 400; 11 | cursor: pointer; 12 | background: none; 13 | border: none; 14 | outline: none; 15 | text-align: left; 16 | letter-spacing: 2px; 17 | } 18 | 19 | .button.fullbutton { 20 | width: 100%; 21 | } 22 | 23 | .button.fullbutton span { 24 | float: right; 25 | } 26 | 27 | .button:after { 28 | content: ''; 29 | position: absolute; 30 | z-index: -1; 31 | } 32 | 33 | /** Colours **/ 34 | 35 | .button { 36 | background: none; 37 | border: 2px solid #A71E2E; 38 | color: #A71E2E !important; 39 | } 40 | 41 | .button:hover { 42 | background: #A71E2E; 43 | color: #FFF !important; 44 | } 45 | 46 | .button:active { 47 | top: 2px; 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/cljs/skylobby/web.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.web 2 | (:require 3 | [com.fulcrologic.fulcro.application :as app] 4 | [com.fulcrologic.fulcro.components :refer [defsc] :as comp] 5 | [com.fulcrologic.fulcro.dom :as dom])) 6 | 7 | 8 | (defonce app 9 | (app/fulcro-app)) 10 | 11 | 12 | (defsc Root [_this _props] 13 | (dom/div "TODO")) 14 | 15 | 16 | (defn ^:export init 17 | "Shadow-cljs sets this up to be our entry-point function. See shadow-cljs.edn `:init-fn` in the modules of the main build." 18 | [] 19 | (app/mount! app Root "root") 20 | (js/console.log "Loaded")) 21 | 22 | (defn ^:export refresh 23 | "During development, shadow-cljs will call this on every hot reload of source. See shadow-cljs.edn" 24 | [] 25 | ;; re-mounting will cause forced UI refresh, update internals, etc. 26 | (app/mount! app Root "app") 27 | ;; As of Fulcro 3.3.0, this addition will help with stale queries when using dynamic routing: 28 | (comp/refresh-dynamic-queries! app) 29 | (js/console.log "Hot reload")) 30 | -------------------------------------------------------------------------------- /test/clj/skylobby/task/handler_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.task.handler-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [skylobby.task.handler :as handler])) 5 | 6 | 7 | (deftest parse-rapid-progress 8 | (is (= {:current 332922 9 | :total 332922} 10 | (handler/parse-rapid-progress "[Progress] 100% [==============================] 332922/332922"))) 11 | (is (= {:current 0 12 | :total 0} 13 | (handler/parse-rapid-progress "[Progress] 0% [ ] 0/0"))) 14 | (is (= {:current 171880 15 | :total 1024954} 16 | (handler/parse-rapid-progress "[Progress] 17% [===== ] 171880/1024954")))) 17 | 18 | (deftest set-sdd-modinfo-version 19 | (is (= "" 20 | (handler/set-sdd-modinfo-version "" "$VERSION"))) 21 | (is (= "version = '$VERSION'" 22 | (handler/set-sdd-modinfo-version "version = 'xxxx'" "$VERSION"))) 23 | (is (= "version = 'git:xxxxxxx'" 24 | (handler/set-sdd-modinfo-version "version = '$VERSION'" "git:xxxxxxx")))) 25 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/battles_table_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battles-table-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.battles-table :as fx.battles-table])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest battles-table 12 | (is (map? 13 | (fx.battles-table/battles-table-impl 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.battles-table/battles-table-impl 17 | {:fx/context (fx/create-context {:by-server {"server" {:battles {"1" {:battle-title "b1"}}}}}) 18 | :server-key "server"})))) 19 | 20 | (deftest battles-table-with-images 21 | (is (map? 22 | (fx.battles-table/battles-table-with-images-impl 23 | {:fx/context (fx/create-context nil)}))) 24 | (is (map? 25 | (fx.battles-table/battles-table-with-images-impl 26 | {:fx/context (fx/create-context {:by-server {"server" {:battles {"1" {:battle-title "b1"}}}}}) 27 | :server-key "server"})))) 28 | -------------------------------------------------------------------------------- /graal/clj/skylobby/event/user.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.event.user 2 | (:require 3 | [skylobby.util :as u] 4 | [taoensso.timbre :as log])) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (defn update-user-state 11 | [state-atom server-key {:keys [username] :as id} user-state] 12 | ; TODO bots 13 | (log/info "Updating user state for" id "with" user-state) 14 | (case (u/server-type server-key) 15 | :direct-host 16 | (let [state (swap! state-atom update-in [:by-server server-key :users username] u/deep-merge user-state)] 17 | (if-let [broadcast-fn (get-in state [:by-server server-key :server :broadcast-fn])] 18 | (let [users (get-in state [:by-server server-key :users])] 19 | (broadcast-fn [:skylobby.direct/users users])) 20 | (log/warn "No broadcast-fn" server-key))) 21 | :direct-client 22 | (if-let [send-fn (get-in @state-atom [:by-server server-key :client :send-fn])] 23 | (send-fn [:skylobby.direct.client/user-state user-state]) 24 | (log/warn "No send-fn" server-key)))) 25 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Build GitHub Pages Cryogen 2 | on: 3 | push: 4 | branches: [ master ] 5 | paths: 6 | - 'cryogen' 7 | workflow_dispatch: 8 | jobs: 9 | pages: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v4.1.1 13 | - uses: actions/setup-java@v4.0.0 14 | with: 15 | java-version: '21' 16 | distribution: 'adopt' 17 | - uses: DeLaGuardo/setup-clojure@12.1 18 | with: 19 | cli: latest 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | - uses: actions/cache@v3.3.2 22 | with: 23 | path: ~/.m2/repository 24 | key: ${{ runner.os }}-maven-${{ hashFiles('cryogen/project.clj') }} 25 | restore-keys: | 26 | ${{ runner.os }}-maven- 27 | - run: clojure -M:build 28 | working-directory: ./cryogen 29 | - uses: peaceiris/actions-gh-pages@v3.9.3 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./cryogen/public/skylobby 33 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/java/serialization-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "types":[ 3 | { 4 | "name":"byte[]" 5 | }, 6 | { 7 | "name":"java.io.IOException" 8 | }, 9 | { 10 | "name":"java.lang.Boolean" 11 | }, 12 | { 13 | "name":"java.lang.Exception" 14 | }, 15 | { 16 | "name":"java.lang.Integer" 17 | }, 18 | { 19 | "name":"java.lang.Long" 20 | }, 21 | { 22 | "name":"java.lang.Number" 23 | }, 24 | { 25 | "name":"java.lang.Object[]" 26 | }, 27 | { 28 | "name":"java.lang.String" 29 | }, 30 | { 31 | "name":"java.lang.Throwable" 32 | }, 33 | { 34 | "name":"java.net.SocketException" 35 | }, 36 | { 37 | "name":"java.util.ArrayList" 38 | }, 39 | { 40 | "name":"java.util.HashSet" 41 | }, 42 | { 43 | "name":"java.util.TreeMap" 44 | }, 45 | { 46 | "name":"long[]" 47 | } 48 | ], 49 | "lambdaCapturingTypes":[ 50 | ], 51 | "proxies":[ 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/tooltip_nofocus.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.tooltip-nofocus 2 | (:require 3 | [cljfx.composite :as composite] 4 | [cljfx.fx.tooltip :as fx.tooltip]) 5 | (:import 6 | (javafx.scene.control Tooltip))) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | ; https://stackoverflow.com/a/45468459/984393 13 | 14 | (defn create [] 15 | (proxy [Tooltip] [] 16 | (show 17 | ([^javafx.stage.Window owner] 18 | (when (.isFocused owner) 19 | (let [^Tooltip this this] 20 | (proxy-super show owner)))) 21 | ([owner ^double x ^double y] 22 | (let [^Tooltip this this] 23 | (if (instance? javafx.scene.Node owner) 24 | (let [^javafx.scene.Node owner owner] 25 | (when (.isFocused owner) 26 | (proxy-super show owner x y))) 27 | (let [^javafx.stage.Window owner owner] 28 | (when (.isFocused owner) 29 | (proxy-super show owner x y))))))))) 30 | 31 | (def lifecycle 32 | (composite/lifecycle 33 | {:props fx.tooltip/props 34 | :args [] 35 | :ctor create})) 36 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/console_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.console-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.console :as fx.console])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest console-document 12 | (is (some? 13 | (fx.console/console-document 14 | [{:message-type "PING" 15 | :message "PING" 16 | :source :client 17 | :timestamp 0}])))) 18 | 19 | 20 | (deftest console-view 21 | (is (map? 22 | (fx.console/console-view 23 | {:fx/context (fx/create-context {:selected-tab-main {"server-key" "console"} 24 | :by-server {"server-key" {:console-log [{:message-type "PING" 25 | :message "PING" 26 | :source :client 27 | :timestamp 0}]}}}) 28 | :server-key "server-key"})))) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 skynet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/page.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{page.title}}{% endblock %} 3 | {% block meta %} 4 | {% if page.description %} 5 | 6 | {% comment %} OpenGraph tags {% endcomment %} 7 | 8 | {% endif %} 9 | 10 | 11 | 12 | {% endblock %} 13 | {% block content %} 14 |
15 | 18 | {% if page.toc %}{{page.toc|safe}}{% endif %} 19 | {{page.content|safe}} 20 | 21 |
22 | {% if page.prev %} 23 | « {{page.prev.title}} 24 | {% endif %} 25 | {% if page.next %} 26 | {{page.next.title}} » 27 | {% endif %} 28 |
29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/virtualized_scroll_pane.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.virtualized-scroll-pane 2 | (:require 3 | [cljfx.coerce :as coerce] 4 | [cljfx.composite :as composite] 5 | [cljfx.fx.region :as fx.region] 6 | [cljfx.lifecycle :as lifecycle] 7 | [cljfx.mutator :as mutator]) 8 | (:import 9 | (javafx.scene.control ScrollPane$ScrollBarPolicy) 10 | (org.fxmisc.flowless VirtualizedScrollPane))) 11 | 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | (def props 16 | (merge 17 | fx.region/props 18 | (composite/props VirtualizedScrollPane 19 | :content [mutator/forbidden lifecycle/dynamic] 20 | :hbar-policy [:setter lifecycle/scalar 21 | :coerce (coerce/enum ScrollPane$ScrollBarPolicy) 22 | :default :as-needed] 23 | :vbar-policy [:setter lifecycle/scalar 24 | :coerce (coerce/enum ScrollPane$ScrollBarPolicy) 25 | :default :as-needed]))) 26 | 27 | (def lifecycle 28 | (composite/lifecycle 29 | {:props props 30 | :args [:content] 31 | :ctor (fn [content] 32 | (VirtualizedScrollPane. content))})) 33 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/bottom_bar_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.bottom-bar-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fs :as fs] 6 | [skylobby.fx.bottom-bar :as fx.bottom-bar])) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (deftest app-update-button 13 | (is (map? 14 | (fx.bottom-bar/app-update-button 15 | {:fx/context (fx/create-context nil)}))) 16 | (is (map? 17 | (fx.bottom-bar/app-update-button 18 | {:fx/context (fx/create-context {:app-update-available true})})))) 19 | 20 | 21 | (deftest bottom-bar-impl 22 | (is (map? 23 | (fx.bottom-bar/bottom-bar-impl 24 | {:fx/context (fx/create-context nil)}))) 25 | (is (map? 26 | (fx.bottom-bar/bottom-bar-impl 27 | {:fx/context (fx/create-context {:music-dir "."})}))) 28 | (is (map? 29 | (fx.bottom-bar/bottom-bar-impl 30 | {:fx/context (fx/create-context {:music-dir "." 31 | :music-now-playing (fs/file ".") 32 | :music-queue [:song1 :song2]})})))) 33 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/color.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.color 2 | (:require 3 | [com.evocomputing.colors :as colors] 4 | [skylobby.util :as u]) 5 | (:import 6 | (javafx.scene.paint Color))) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (defn spring-color-to-javafx 13 | "Returns the rgb int color represention for the given Spring bgr int color." 14 | [spring-color] 15 | (let [spring-color-int (or (u/to-number spring-color) 0) 16 | [r g b _a] (:rgba (colors/create-color spring-color-int)) 17 | reversed (colors/create-color 18 | {:r b 19 | :g g 20 | :b r})] 21 | (Color/web (format "#%06x" (colors/rgb-int reversed))))) 22 | 23 | (defn javafx-color-to-spring 24 | "Returns the spring bgr int color format from a javafx color." 25 | [^Color color] 26 | (if color 27 | (colors/rgba-int 28 | (colors/create-color 29 | {:r (Math/round (* 255 (.getBlue color))) ; switch blue to red 30 | :g (Math/round (* 255 (.getGreen color))) 31 | :b (Math/round (* 255 (.getRed color))) ; switch red to blue 32 | :a 0})) 33 | 0)) 34 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/download_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.download-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.download :as fx.download])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest downloader-root 12 | (is (map? 13 | (fx.download/downloader-root 14 | {:fx/context (fx/create-context nil)}))) 15 | (is (map? 16 | (fx.download/downloader-root 17 | {:fx/context (fx/create-context {:downloadables-by-url {"url" {:download-source-name "d" 18 | :resource-filename "f1"}} 19 | :download-source-name "d" 20 | :download-filter "f"})})))) 21 | 22 | 23 | (deftest download-window 24 | (is (map? 25 | (fx.download/download-window 26 | {:fx/context (fx/create-context nil) 27 | :screen-bounds {}}))) 28 | (is (map? 29 | (fx.download/download-window 30 | {:fx/context (fx/create-context {:show-downloader true}) 31 | :screen-bounds {}})))) 32 | -------------------------------------------------------------------------------- /src/cljs/skylobby/view/side_nav.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.view.side-nav 2 | (:require 3 | [re-frame.core :as rf] 4 | [skylobby.util :as u])) 5 | 6 | 7 | (defn listen [query-v] 8 | @(rf/subscribe query-v)) 9 | 10 | 11 | (def navs 12 | [{:route :skylobby/servers 13 | :icon "web"} 14 | {:route :skylobby/replays 15 | :icon "movie"} 16 | {:route :skylobby/settings 17 | :icon "settings"} 18 | {:route :skylobby/tasks 19 | :icon "memory"} 20 | {:route :skylobby/quit 21 | :icon "logout"}]) 22 | 23 | (defn side-nav [_] 24 | (let [current-route (listen [:skylobby/current-route])] 25 | [:div {:class "flex-column"} 26 | (for [{:keys [icon route]} navs] 27 | (let [color (if (= route (get-in current-route [:data :name])) 28 | "red" 29 | "gray")] 30 | ^{:key route} 31 | [:div.ma2 32 | [:div 33 | {:class (str "ba bw1 br2 ma2 b--" color " ")} 34 | [:a 35 | { 36 | :class color 37 | :href (u/href route)} 38 | [:span.material-icons 39 | {:style {:font-size 48}} 40 | icon]]]]))])) 41 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/page.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{page.title}}{% endblock %} 3 | {% block meta %} 4 | {% if page.description %} 5 | 6 | {% comment %} OpenGraph tags {% endcomment %} 7 | 8 | {% endif %} 9 | 10 | 11 | 12 | {% endblock %} 13 | {% block content %} 14 |
15 | 18 | {% if page.toc %}{{page.toc|safe}}{% endif %} 19 | {{page.content|safe}} 20 | 21 |
22 | {% if page.prev %} 23 | « {{page.prev.title}} 24 | {% endif %} 25 | {% if all page.prev page.next %} 26 | || 27 | {% endif %} 28 | {% if page.next %} 29 | {{page.next.title}} » 30 | {% endif %} 31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/page.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{page.title}}{% endblock %} 3 | {% block meta %} 4 | {% if page.description %} 5 | 6 | {% comment %} OpenGraph tags {% endcomment %} 7 | 8 | {% endif %} 9 | 10 | 11 | 12 | {% endblock %} 13 | {% block content %} 14 |
15 | 18 | {% if page.toc %}{{page.toc|safe}}{% endif %} 19 | {{page.content|safe}} 20 | 21 |
22 | {% if page.prev %} 23 | « {{page.prev.title}} 24 | {% endif %} 25 | {% if all page.prev page.next %} 26 | || 27 | {% endif %} 28 | {% if page.next %} 29 | {{page.next.title}} » 30 | {% endif %} 31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /.clj-kondo/ci-config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as {com.fulcrologic.fulcro.components/defsc clojure.core/defn} 2 | :linters 3 | {:unresolved-symbol 4 | {:exclude [(diehard.core/defratelimiter) 5 | (taoensso.nippy/extend-freeze) 6 | (taoensso.nippy/extend-thaw)]} 7 | :unresolved-var 8 | {:exclude [gloss.core/compile-frame 9 | gloss.io/to-byte-buffer]} 10 | :warn-on-reflection 11 | {:level :warn 12 | :warn-only-on-interop false} 13 | :aliased-namespace-symbol {:level :off} 14 | :docstring-no-summary {:level :off} 15 | :docstring-leading-trailing-whitespace {:level :off} 16 | :dynamic-var-not-earmuffed {:level :off} 17 | :reduce-without-init {:level :warn} 18 | :keyword-binding {:level :warn} 19 | :main-without-gen-class {:level :off} 20 | :missing-docstring {:level :off} 21 | :namespace-name-mismatch {:level :error} 22 | :redundant-fn-wrapper {:level :off} 23 | :refer {:level :off} 24 | :single-key-in {:level :warn} 25 | :shadowed-var {:level :off} 26 | :used-underscored-binding {:level :warn} 27 | :unused-value {:level :warn} 28 | :unsorted-required-namespaces {:level :warn}} 29 | :output 30 | { 31 | :exclude-files ["event_stub.clj"]}} 32 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as {com.fulcrologic.fulcro.components/defsc clojure.core/defn} 2 | :linters 3 | {:unresolved-symbol 4 | {:exclude [(diehard.core/defratelimiter) 5 | (taoensso.nippy/extend-freeze) 6 | (taoensso.nippy/extend-thaw)]} 7 | :unresolved-var 8 | {:exclude [gloss.core/compile-frame 9 | gloss.io/to-byte-buffer]} 10 | :warn-on-reflection 11 | {:level :warn 12 | :warn-only-on-interop false} 13 | :aliased-namespace-symbol {:level :info} 14 | :docstring-no-summary {:level :info} 15 | :docstring-leading-trailing-whitespace {:level :info} 16 | :dynamic-var-not-earmuffed {:level :info} 17 | :reduce-without-init {:level :warn} 18 | :keyword-binding {:level :warn} 19 | :main-without-gen-class {:level :info} 20 | :missing-docstring {:level :info} 21 | :namespace-name-mismatch {:level :error} 22 | :redundant-fn-wrapper {:level :info} 23 | :refer {:level :info} 24 | :single-key-in {:level :warn} 25 | :shadowed-var {:level :info} 26 | :used-underscored-binding {:level :warn} 27 | :unused-value {:level :warn} 28 | :unsorted-required-namespaces {:level :warn}} 29 | :output 30 | { 31 | :exclude-files ["event_stub.clj"]}} 32 | -------------------------------------------------------------------------------- /repo/io/github/memo33/jsquish/2.0.1/jsquish-2.0.1.pom: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.github.memo33 5 | jsquish 6 | jar 7 | jsquish 8 | 2.0.1 9 | 10 | 11 | BSD 3-Clause 12 | https://opensource.org/licenses/BSD-3-Clause 13 | repo 14 | 15 | 16 | jsquish 17 | 18 | com.github.memo33 19 | 20 | 21 | 22 | bintraymemo33maven 23 | bintray-memo33-maven 24 | https://dl.bintray.com/memo33/maven/ 25 | default 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/clj/skylobby/task_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.task-test 2 | (:require 3 | [clojure.test :refer [deftest is testing]] 4 | [skylobby.task :as task])) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (deftest add-task! 11 | (testing "kind and dedupe in queue" 12 | (let [state (atom {})] 13 | (task/add-task! state {:spring-lobby/task-type :spring-lobby/fake-task}) 14 | (task/add-task! state {:spring-lobby/task-type :spring-lobby/fake-task}) 15 | (task/add-task! state {:spring-lobby/task-type :spring-lobby/refresh-engines}) 16 | (task/add-task! state {:spring-lobby/task-type :spring-lobby/download-and-extract}) 17 | (task/add-task! state {:spring-lobby/task-type :spring-lobby/update-rapid}) 18 | (is (= {:tasks-by-kind 19 | {:spring-lobby/other-task 20 | #{{:spring-lobby/task-type :spring-lobby/fake-task}} 21 | :spring-lobby/index-task 22 | #{{:spring-lobby/task-type :spring-lobby/refresh-engines}} 23 | :spring-lobby/download-task 24 | #{{:spring-lobby/task-type :spring-lobby/download-and-extract}} 25 | :spring-lobby/rapid-task 26 | #{{:spring-lobby/task-type :spring-lobby/update-rapid}}}} 27 | @state))))) 28 | -------------------------------------------------------------------------------- /graal/clj/skylobby/client/stls.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.client.stls 2 | (:require 3 | [taoensso.timbre :as log]) 4 | (:import 5 | (io.netty.channel ChannelPipeline) 6 | (io.netty.handler.ssl SslContextBuilder SslHandler) 7 | (io.netty.handler.ssl.util InsecureTrustManagerFactory))) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (defn upgrade-pipeline [^ChannelPipeline pipeline] 14 | (if pipeline 15 | (let [; https://github.com/clj-commons/aleph/blob/master/src/aleph/netty.clj#L721-L724 16 | ssl-context-builder (SslContextBuilder/forClient) 17 | _ (.trustManager ssl-context-builder InsecureTrustManagerFactory/INSTANCE) 18 | _ (.startTls ssl-context-builder true) 19 | ssl-context (.build ssl-context-builder) 20 | ch (.channel pipeline) 21 | engine (.newEngine ssl-context (.alloc ch)) 22 | handler (SslHandler. engine false) 23 | handshake-future (.handshakeFuture handler)] 24 | (.addFirst pipeline "ssl" handler) 25 | (log/info "Added SslHandler to TCP pipeline") 26 | (log/info "Waiting for SSL handshake") 27 | (let [handshake @handshake-future] 28 | (log/info "SSL handshake finished" handshake) 29 | true)) 30 | (log/warn "No TCP pipeline to upgrade to TLS!"))) 31 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/channel_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.channel-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.channel :as fx.channel])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest channel-document 12 | (is (some? 13 | (fx.channel/channel-document 14 | [{:message-type :ex 15 | :text "message" 16 | :timestamp 0 17 | :username "me"}]))) 18 | (is (some? 19 | (fx.channel/channel-document 20 | [{:message-type nil 21 | :text "message 2" 22 | :timestamp 1 23 | :username "me"}])))) 24 | 25 | 26 | (deftest channel-view-history-impl 27 | (is (map? 28 | (fx.channel/channel-view-history-impl 29 | {:fx/context (fx/create-context nil)})))) 30 | 31 | 32 | (deftest channel-view-input 33 | (is (map? 34 | (fx.channel/channel-view-input 35 | {:fx/context (fx/create-context nil)})))) 36 | 37 | 38 | (deftest channel-view-users 39 | (is (map? 40 | (fx.channel/channel-view-users 41 | {:fx/context (fx/create-context nil)})))) 42 | 43 | 44 | (deftest channel-view 45 | (is (map? 46 | (fx.channel/channel-view 47 | {:fx/context (fx/create-context nil)})))) 48 | -------------------------------------------------------------------------------- /cryogen/content/config.edn: -------------------------------------------------------------------------------- 1 | {:site-title "skylobby" 2 | :author "skynet-gh" 3 | :description "Docs and blog for skylobby project" 4 | :site-url "https://skynet-gh.github.io/skylobby/" 5 | :post-root "posts" 6 | :page-root "pages" 7 | :public-dest "public" 8 | :blog-prefix "/skylobby" 9 | :rss-name "feed.xml" 10 | :rss-filters ["cryogen"] 11 | :recent-posts 3 12 | :post-date-format "yyyy-MM-dd" 13 | :archive-group-format "yyyy MMMM" 14 | :sass-src [] 15 | :sass-path "sass.bat" 16 | :theme "blue" 17 | :resources ["img"] 18 | :keep-files [".git"] 19 | :disqus? false 20 | :disqus-shortname "" 21 | :ignored-files [#"\.#.*" #".*\.swp$"] 22 | :previews? false 23 | :posts-per-page 5 24 | :blocks-per-preview 2 25 | :clean-urls :trailing-slash 26 | :collapse-subdirs? false 27 | :hide-future-posts? true 28 | :klipse {} 29 | :description-include-elements #{:p :h1 :h2 :h3 :h4 :h5 :h6} 30 | :debug? false} 31 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/spring_options_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.spring-options-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.spring-options :as fx.spring-options])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest split-by 12 | (is (= [] 13 | (fx.spring-options/split-by (constantly false) []))) 14 | (is (= [[:x :y :y :y] 15 | [:x :y :y] 16 | [:x :y] 17 | [:x :y :y]] 18 | (fx.spring-options/split-by #{:x} [:x :y :y :y :x :y :y :x :y :x :y :y])))) 19 | 20 | (deftest modoptions-table 21 | (is (map? 22 | (fx.spring-options/modoptions-table {:fx/context (fx/create-context nil)}))) 23 | (is (map? 24 | (fx.spring-options/modoptions-table 25 | {:fx/context 26 | (fx/create-context 27 | {:modoptions 28 | [[:1 {:type "section"}] 29 | [:2 {:type "number"}] 30 | [:2 {:type "list"}]]})})))) 31 | 32 | (deftest modoptions-view 33 | (is (map? 34 | (fx.spring-options/modoptions-view {:fx/context (fx/create-context nil)}))) 35 | (is (map? 36 | (fx.spring-options/modoptions-view 37 | {:fx/context 38 | (fx/create-context 39 | {:modoptions 40 | [[:1 {:type "section"}] 41 | [:2 {:type "number"}] 42 | [:2 {:type "list"}]]})})))) 43 | -------------------------------------------------------------------------------- /graal/clj/skylobby/client/message.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.client.message 2 | (:refer-clojure :exclude [send]) 3 | (:require 4 | [clojure.string :as string] 5 | [manifold.stream :as s] 6 | [skylobby.util :as u] 7 | [taoensso.timbre :as log])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (defn send 14 | ([state-atom client-data message] 15 | (send state-atom client-data message nil)) 16 | ([state-atom client-data message {:keys [log-message] :or {log-message message}}] 17 | (if-let [client (:client client-data)] 18 | (let [server-key (u/server-key client-data)] 19 | (log/info (str "[" server-key "] > '" log-message "'")) 20 | @(s/put! client (str message "\n")) 21 | (let [k (-> message 22 | (string/split #"\s") 23 | first)] 24 | (swap! state-atom 25 | (fn [state] 26 | (cond-> ((u/append-console-log-fn server-key :client log-message) state) 27 | (= "PING" k) 28 | (assoc-in [:by-server server-key :last-ping] (u/curr-millis)) 29 | (= "MYBATTLESTATUS" k) 30 | (assoc-in 31 | [:by-server server-key :expecting-responses k] 32 | {:sent-message message 33 | :sent-millis (u/curr-millis)})))))) 34 | (log/error (ex-info "No client to send message" {:message log-message}))))) 35 | -------------------------------------------------------------------------------- /graal/clj/skylobby/chat.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.chat 2 | (:require 3 | [clojure.string :as string])) 4 | 5 | 6 | (set! *warn-on-reflection* true) 7 | 8 | 9 | (defn ignore-users-set [ignore-users] 10 | (->> ignore-users 11 | (filter second) 12 | (map first) 13 | set)) 14 | 15 | (defn hide-spads-set [hide-spads-messages] 16 | (->> hide-spads-messages 17 | (filter second) 18 | (map first) 19 | set)) 20 | 21 | (defn visible-message? 22 | [{:keys [ 23 | hide-barmanager-messages 24 | hide-joinas-spec 25 | hide-spads-set 26 | hide-vote-messages 27 | ignore-users-set]} 28 | {:keys [message-type relay spads text username vote]}] 29 | (not 30 | (or (and 31 | username 32 | (contains? ignore-users-set username)) 33 | (and (= "Coordinator" username) 34 | (string/starts-with? text "skylobby")) 35 | (and 36 | (:on-behalf-of relay) 37 | (contains? ignore-users-set (:on-behalf-of relay))) 38 | (contains? hide-spads-set (:spads-message-type spads)) 39 | (and hide-vote-messages 40 | (:vote vote)) 41 | (and hide-joinas-spec 42 | (= "joinas spec" 43 | (:command vote))) 44 | (and hide-barmanager-messages 45 | (and (= :ex message-type) 46 | text 47 | (string/starts-with? text "* BarManager|")))))) 48 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | 29 | 30 |
31 |
32 |
33 |

404

34 |

Error ! Page Not Found

35 |
36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | 29 | 30 |
31 |
32 |
33 |

404

34 |

Error ! Page Not Found

35 |
36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 28 | 29 | 30 |
31 |
32 |
33 |

404

34 |

Error ! Page Not Found

35 |
36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/css/_variables.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | 3 | $black: #000; 4 | $red: #A71E2E; 5 | $green: #00D400; 6 | $purple: #522158; 7 | $very-dark-grey: #111; 8 | $dark-grey: #444; 9 | $grey: #808080; 10 | $light-grey: #ccc; 11 | $very-light-grey: #fbfbfb; 12 | $white: #fff; 13 | 14 | $base-color: $purple; 15 | $base-font-color: $very-dark-grey; 16 | $body-bgcolor: $very-light-grey; 17 | 18 | // Fonts 19 | $font-weight-light: 300; 20 | $font-weight-normal: 400; 21 | $font-weight-heavy: 600; 22 | $font-weight-header: 700; 23 | $default-font-weight: $font-weight-normal; 24 | 25 | $default-font-size: 16px; 26 | $default-line-height: 1.25; 27 | $button-font-size: 14px; 28 | 29 | 30 | 31 | $default-font-family: Oxygen, Tahoma, Verdana, Helvetica, Arial, sans-serif; 32 | $wide-font-family: Montserrat, sans-serif; 33 | $mono-font-family: Inconsolata, Consolas, "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", "Droid Sans Mono", "Andale Mono", Anonymous, "Monaco", monospaced; 34 | 35 | // Borders 36 | $default-border-color: $base-color; 37 | $default-border-width: 2px; 38 | $default-border-style: solid; 39 | $default-border: $default-border-width $default-border-style $default-border-color; 40 | $default-border-radius: 2px; 41 | 42 | // Spacing 43 | $default-gutter: 20px; 44 | $sidebar-width: 220px; 45 | 46 | // Media 47 | // $tablet-breakpoint: 768px; 48 | // $mobile-breakpoint: 480px; 49 | $tablet-breakpoint: 900px; 50 | $mobile-breakpoint: 700px; 51 | 52 | // Effects 53 | $default-opacity: 0.85; 54 | -------------------------------------------------------------------------------- /dev/clj/native/native_image.clj: -------------------------------------------------------------------------------- 1 | (ns native-image 2 | (:require 3 | [clojure.java.io :as io] 4 | [clojure.pprint :refer [pprint]] 5 | [clojure.tools.namespace.repl :refer [disable-unload! disable-reload!]] 6 | clj.native-image 7 | [clojure.tools.namespace.find :refer [find-namespaces-in-dir]])) 8 | 9 | 10 | (disable-unload!) 11 | (disable-reload!) 12 | 13 | 14 | ; adapted from https://github.com/taylorwood/clj.native-image/blob/master/src/clj/native_image.clj 15 | ; mostly just to put classpath in a file since it's too big for a command on Windows 16 | 17 | 18 | (defn build [main-ns opts] 19 | (let [nat-image-path (clj.native-image/native-image-bin-path) 20 | deps-map (clj.native-image/merged-deps) 21 | namespaces (mapcat (comp find-namespaces-in-dir io/file) (:paths deps-map))] 22 | (clj.native-image/prep-compile-path) 23 | (doseq [ns (distinct (cons main-ns namespaces))] 24 | (println "Compiling" ns) 25 | (compile (symbol ns))) 26 | (let [cp (clj.native-image/native-image-classpath)] 27 | (spit "native-image-args" (str "-cp " cp))) 28 | (let [args (concat 29 | ["@native-image-args"] 30 | opts)] 31 | (println "Running native-image with options" (with-out-str (pprint args))) 32 | (System/exit 33 | (clj.native-image/exec-native-image 34 | nat-image-path 35 | args 36 | nil 37 | main-ns))))) 38 | 39 | (defn -main [main-ns & args] 40 | (try 41 | (build main-ns args) 42 | (finally 43 | (shutdown-agents)))) 44 | -------------------------------------------------------------------------------- /test/clj/skylobby/client/gloss_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.client.gloss-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [skylobby.client.gloss :as cu])) 5 | 6 | 7 | (deftest decode-client-status 8 | (is (= {:bot false, 9 | :access false, 10 | :rank 0, 11 | :away false, 12 | :ingame false} 13 | (cu/decode-client-status "0"))) 14 | (is (= {:bot false, 15 | :access false, 16 | :rank 0, 17 | :away false, 18 | :ingame true} 19 | (cu/decode-client-status "1"))) 20 | (is (= {:bot false, 21 | :access false, 22 | :rank 2, 23 | :away false, 24 | :ingame false} 25 | (cu/decode-client-status "8"))) 26 | (is (= {:bot true, 27 | :access false, 28 | :rank 0, 29 | :away false, 30 | :ingame false} 31 | (cu/decode-client-status "64")))) 32 | 33 | 34 | 35 | (deftest decode-battle-status 36 | (is (= {:ready false, 37 | :ally 0, 38 | :handicap 0, 39 | :mode true, 40 | :sync 2, 41 | :id 0, 42 | :side 0} 43 | (cu/decode-battle-status "8389632")))) 44 | 45 | (deftest encode-battle-status 46 | (let [encoded (cu/encode-battle-status 47 | {:ready false, 48 | :ally 0, 49 | :handicap 0, 50 | :mode true, 51 | :sync 2, 52 | :id 0, 53 | :side 0})] 54 | (is (= "8389632" 55 | encoded)))) 56 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/post.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{post.title}}{% endblock %} 3 | {% block meta %} 4 | 5 | {% if post.description %} 6 | 7 | {% comment %} OpenGraph tags {% endcomment %} 8 | 9 | {% endif %} 10 | 11 | 12 | 13 | {% endblock %} 14 | {% block content %} 15 |
16 | {% include "/html/post-content.html" %} 17 | 18 | {% with prev=post.prev next=post.next%} 19 | {% include "/html/prev-next.html" %} 20 | {% endwith %} 21 | 22 | {% if disqus-shortname %} 23 |
24 | 35 | {% endif %} 36 | 37 | 38 |
39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /graal/clj/skylobby/battle.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.battle 2 | "Utilities for processing battle data.") 3 | 4 | 5 | (set! *warn-on-reflection* true) 6 | 7 | 8 | (defn battle-players-and-bots-users 9 | "Returns the sequence of all players and bots for a battle." 10 | [{:keys [battle users]}] 11 | (concat 12 | (mapv 13 | (fn [[k v]] (assoc v :username k :user (get users k))) 14 | (:users battle)) 15 | (mapv 16 | (fn [[k v]] 17 | (assoc v 18 | :bot-name k 19 | :user {:client-status {:bot true}})) 20 | (:bots battle)))) 21 | 22 | (defn battle-players-and-bots 23 | "Returns the sequence of all players and bots for a battle." 24 | [battle] 25 | (when battle 26 | (->> (mapcat battle [:users :bots]) 27 | (mapv second)))) 28 | 29 | 30 | (defn- first-avail [n-set] 31 | (loop [i 0] 32 | (if (or (contains? n-set i) 33 | (contains? n-set (str i))) 34 | (recur (inc i)) 35 | i))) 36 | 37 | (defn available-team-id [battle] 38 | (let [ids (->> battle 39 | battle-players-and-bots 40 | (filter (comp :mode :battle-status)) ; remove spectators 41 | (map (comp :id :battle-status)) 42 | set)] 43 | (first-avail ids))) 44 | 45 | (defn available-ally [battle] 46 | (let [allies (->> battle 47 | battle-players-and-bots 48 | (filter (comp :mode :battle-status)) ; remove spectators 49 | (map (comp :ally :battle-status)) 50 | set)] 51 | (first-avail allies))) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # skylobby [![test](https://github.com/skynet-gh/skylobby/actions/workflows/test.yml/badge.svg)](https://github.com/skynet-gh/skylobby/actions/workflows/test.yml) [![license](https://img.shields.io/github/license/skynet-gh/skylobby)](LICENSE) 2 | 3 | A [Spring RTS](https://springrts.com/) lobby for [uberserver](https://github.com/spring/uberserver). 4 | 5 | ## Usage 6 | 7 | Install instructions and basic usage can be found in the [User Guide](https://github.com/skynet-gh/skylobby/wiki/User-Guide). 8 | 9 | Feel free to open an [issue](https://github.com/skynet-gh/skylobby/issues) if you find a bug or have a feature request. 10 | 11 | ## Dev 12 | 13 | Below are instructions on how to build and contribute to skylobby. 14 | 15 | You will need [Java 11 or higher](https://adoptium.net/) and the [Clojure CLI tools](https://clojure.org/guides/getting_started#_clojure_installer_and_cli_tools). 16 | 17 | ### REPL 18 | 19 | Runs an interactive compiler as well as the UI. If you make a change to something in `src/clj` it will trigger a recompile and re-render the UI from the running state. 20 | 21 | You will also need to install `rlwrap`. Run: 22 | 23 | ```bash 24 | clj -M:nrepl 25 | ``` 26 | 27 | Logs are written to `repl.log`. 28 | 29 | ### Jar 30 | 31 | To build an executable jar file: 32 | 33 | ```bash 34 | clojure -M:uberjar 35 | ``` 36 | 37 | And then run it with: 38 | 39 | ```bash 40 | java -jar target/skylobby.jar 41 | ``` 42 | 43 | To build an installer, then run `jpackage` for your platform, for example on Windows 44 | 45 | ```bash 46 | jpackage @jpackage/common @jpackage/windows 47 | ``` 48 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/maps_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.maps-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fs :as fs] 6 | [skylobby.fx.maps :as fx.maps])) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (deftest maps-view 13 | (is (map? 14 | (fx.maps/maps-view-impl 15 | {:fx/context (fx/create-context nil)})))) 16 | 17 | 18 | (deftest maps-window 19 | (is (map? 20 | (fx.maps/maps-window 21 | {:fx/context (fx/create-context nil) 22 | :screen-bounds {}}))) 23 | (let [f (fs/file ".")] 24 | (is (map? 25 | (fx.maps/maps-window 26 | {:fx/context (fx/create-context {:show-maps true 27 | :spring-isolation-dir f 28 | :by-spring-root {(fs/canonical-path f) {:maps [{:map-name "map1"} 29 | {:map-name "map2"}]}}}) 30 | :screen-bounds {}}))) 31 | (is (map? 32 | (fx.maps/maps-window 33 | {:fx/context (fx/create-context {:show-maps true 34 | :spring-isolation-dir f 35 | :filter-maps-name "map1" 36 | :by-spring-root {(fs/canonical-path f) {:maps [{:map-name "map1"} 37 | {:map-name "map2"}]}}}) 38 | :screen-bounds {}}))))) 39 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 47 | 48 | 49 | 50 |
51 |
52 |

Emptiness - 404

53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /src/clj/skylobby/discord.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.discord 2 | (:require 3 | [cheshire.core :as json] 4 | [clj-http.client :as http] 5 | [clojure.string :as string] 6 | [skylobby.util :as u] 7 | [taoensso.timbre :as log])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (def base-url "https://discord.com/api/webhooks/") 14 | 15 | (def cooldown 180000) ; 3 minutes 16 | 17 | (def tokens 18 | {"898249621527220235" "czlXYXJqSlpEY1VINU1UenpjMVpFNGJ1cGVBV1N5XzBXbXpPR2Z2M3pwQmJKOWdzbTdNbHFGYlJoMXJ6b296Y0x0Q0Y="}) 19 | 20 | 21 | (defn channel-to-promote [{:keys [mod-name]}] 22 | (when (and mod-name 23 | (or 24 | (string/starts-with? mod-name "Balanced Annihilation") 25 | (string/starts-with? mod-name "Metal Factions") 26 | (string/starts-with? mod-name "Tech Annihilation"))) 27 | "898249621527220235")) 28 | 29 | (defn promote-battle [channel-id {:keys [battle-title map-name mod-name team-counts]}] 30 | (let [ 31 | url (str base-url channel-id "/" (u/base64-decode (get tokens channel-id))) 32 | total-players (reduce (fnil + 0 0) 0 (flatten team-counts)) 33 | body {:content (str "**" total-players " players** in " battle-title "\n" 34 | "**" mod-name "** on **" map-name "**") 35 | :username "skylobby"} 36 | _ (log/info "Posting Discord promotion to" url ":" body) 37 | response 38 | (http/post url 39 | {:body (json/generate-string body) 40 | :content-type :json 41 | :accept :json 42 | :as :json})] 43 | (log/info "Response from Discord:" (:body response)))) 44 | -------------------------------------------------------------------------------- /src/clj/spring_lobby/spring/uikeys.clj: -------------------------------------------------------------------------------- 1 | (ns spring-lobby.spring.uikeys 2 | (:require 3 | [clojail.core :as clojail] 4 | [clojure.java.io :as io] 5 | [clojure.string :as string] 6 | [instaparse.core :as instaparse] 7 | [taoensso.timbre :as log])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (def uikeys-grammar 14 | (instaparse/parser 15 | "uikeys = line* 16 | = (bind | notbind | #'\\s*') 17 | bind = <'bind'> <#'\\s+'> #'[^\\s]+' <#'\\s+'> #'[^/\\r\\n]+' comment? 18 | notbind = !'bind' #'\\w+' #'\\s+' #'.*' 19 | comment = <'//'> #'.*'" 20 | :auto-whitespace :standard)) 21 | 22 | (defn postprocess [parsed-uikeys] 23 | (->> parsed-uikeys 24 | (filter coll?) 25 | (filter (comp #{:bind} first)) 26 | (map 27 | (fn [[_kind key-combo action [_comment comment-str]]] 28 | {:bind-key key-combo 29 | :bind-action (string/trim action) 30 | :bind-comment comment-str})) 31 | (filter some?))) 32 | 33 | 34 | (defn parse-uikeys 35 | ([] 36 | (parse-uikeys (slurp (io/resource "uikeys.tmp")))) 37 | ([uikeys-raw] 38 | (clojail/thunk-timeout 39 | #(let [parsed (instaparse/parse uikeys-grammar uikeys-raw)] 40 | (if (instaparse/failure? parsed) 41 | (do 42 | (log/debug parsed) 43 | (throw (ex-info "Failed to parse" {:uikeys-pr-str (pr-str uikeys-raw)}))) 44 | (postprocess parsed))) 45 | 2000))) 46 | 47 | (defn parse-bind-keys [bind-keys-str] 48 | (let [combos (string/split bind-keys-str #"(? parameters :path :server-url) 24 | username (-> parameters :query :username) 25 | server-key (u/get-server-key server-url username) 26 | battle (listen [:skylobby/battle server-key]) 27 | route-names (concat 28 | (when battle 29 | [:skylobby/room]) 30 | [:skylobby/battles :skylobby/channels :skylobby/console]) 31 | battle-title (str "Battle: " (listen [:skylobby/battle-title server-key]))] 32 | [:div {:class "flex justify-center"} 33 | (for [route-name route-names] 34 | ^{:key route-name} 35 | [:div {:class "pa3"} 36 | [:a 37 | {:class (str css/header-class " " 38 | (if (or (= route-name (-> current-route :data :name)) 39 | (and (= :skylobby/chat route-name) 40 | (= :skylobby/channels (-> current-route :data :name)))) 41 | "gold" 42 | "gray")) 43 | :href (u/href route-name {:server-url server-url} {:username username})} 44 | (or (get server-route-names route-name) 45 | battle-title)]])])) 46 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Unit test 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | workflow_dispatch: 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | java: ['17', '21'] 14 | fail-fast: false 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v4.1.1 18 | - uses: actions/setup-java@v4.0.0 19 | with: 20 | java-version: ${{ matrix.java }} 21 | distribution: 'adopt' 22 | - uses: DeLaGuardo/setup-clojure@12.1 23 | with: 24 | cli: latest 25 | github-token: ${{ secrets.GITHUB_TOKEN }} 26 | - uses: actions/cache@v3.3.2 27 | with: 28 | path: ~/.m2/repository 29 | key: ${{ runner.os }}-maven-${{ hashFiles('deps.edn') }} 30 | restore-keys: | 31 | ${{ runner.os }}-maven- 32 | - if: ${{ matrix.os != 'windows-latest' }} 33 | run: clojure -M:test:headless:runner 34 | shell: bash 35 | - if: ${{ matrix.os == 'windows-latest' }} 36 | run: clojure -M:test:headless:runner 37 | shell: powershell 38 | coverage: 39 | if: ${{ false }} # disable for now 40 | runs-on: ubuntu-22.04 41 | steps: 42 | - uses: actions/checkout@v4.1.1 43 | - uses: actions/setup-java@v4.0.0 44 | with: 45 | java-version: '21' 46 | distribution: 'adopt' 47 | - uses: DeLaGuardo/setup-clojure@12.1 48 | with: 49 | cli: latest 50 | github-token: ${{ secrets.GITHUB_TOKEN }} 51 | - run: clojure -M:coverage:headless 52 | - run: bash <(curl -s https://codecov.io/bash) -f target/coverage/codecov.json 53 | -------------------------------------------------------------------------------- /cryogen/themes/blue/html/post.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{post.title}}{% endblock %} 3 | {% block meta %} 4 | 5 | {% if post.description %} 6 | 7 | {% comment %} OpenGraph tags {% endcomment %} 8 | 9 | {% endif %} 10 | 11 | 12 | 13 | {% endblock %} 14 | {% block content %} 15 |
16 | {% include "/html/post-content.html" %} 17 |
18 | {% if post.prev %} 19 | « {{post.prev.title}} 20 | {% endif %} 21 | {% if post.next %} 22 | {{post.next.title}} » 23 | {% endif %} 24 |
25 | 26 | {% if disqus-shortname %} 27 |
28 | 39 | {% endif %} 40 | 41 | 42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /cryogen/themes/blue_centered/html/post.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{post.title}}{% endblock %} 3 | {% block meta %} 4 | 5 | {% if post.description %} 6 | 7 | {% comment %} OpenGraph tags {% endcomment %} 8 | 9 | {% endif %} 10 | 11 | 12 | 13 | {% endblock %} 14 | {% block content %} 15 |
16 | {% include "/html/post-content.html" %} 17 |
18 | {% if post.prev %} 19 | « {{post.prev.title}} 20 | {% endif %} 21 | {% if post.next %} 22 | {{post.next.title}} » 23 | {% endif %} 24 |
25 | 26 | {% if disqus-shortname %} 27 |
28 | 39 | {% endif %} 40 | 41 | 42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/html/post.html: -------------------------------------------------------------------------------- 1 | {% extends "/html/base.html" %} 2 | {%block subtitle %}: {{post.title}}{% endblock %} 3 | {% block meta %} 4 | 5 | {% if post.description %} 6 | 7 | {% comment %} OpenGraph tags {% endcomment %} 8 | 9 | {% endif %} 10 | 11 | 12 | 13 | {% endblock %} 14 | {% block content %} 15 |
16 | {% include "/html/post-content.html" %} 17 |
18 | {% if post.prev %} 19 | « {{post.prev.title}} 20 | {% endif %} 21 | {% if post.next %} 22 | {{post.next.title}} » 23 | {% endif %} 24 |
25 | 26 | {% if disqus-shortname %} 27 |
28 | 39 | {% endif %} 40 | 41 | 42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /graal/clj/skylobby/lua.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.lua 2 | (:import 3 | (org.luaj.vm2 LuaValue) 4 | (org.luaj.vm2.lib.jse JsePlatform))) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (def mocks 11 | "VFS = {} 12 | 13 | function VFS.DirList(dir, fileType) 14 | return {} 15 | end 16 | 17 | function getfenv() 18 | local t = {} 19 | t[\"mapinfo\"] = {} 20 | return t 21 | end 22 | 23 | Spring = {} 24 | 25 | function Spring.Log(x, level, message) 26 | return {} 27 | end 28 | 29 | ") 30 | 31 | 32 | (defn table-to-map 33 | "Returns a map from the given lua table, with keys converted to keywowrds and inner table values 34 | converted to maps as well." 35 | [^LuaValue lv] 36 | (let [table (.checktable lv)] 37 | (loop [m {} 38 | prevk LuaValue/NIL] 39 | (let [kv (.next table prevk) 40 | k (.arg1 kv) 41 | v (.arg kv 2)] 42 | (if (.isnil k) 43 | m 44 | (recur 45 | (let [kk (keyword (.toString k)) 46 | vs (cond 47 | (.istable v) (table-to-map v) 48 | :else 49 | (.toString v))] 50 | (assoc m kk vs)) 51 | k)))))) 52 | 53 | (defn read-mapinfo 54 | "Returns a map repsenting mapinfo from the given string representation of mapinfo.lua." 55 | [s] 56 | (let [globals (JsePlatform/standardGlobals) 57 | lua-chunk (.load globals (str mocks s)) 58 | res (.call lua-chunk)] 59 | (table-to-map res))) 60 | 61 | (defn read-modinfo 62 | "Returns a map repsenting modinfo from the given string representation of modinfo.lua." 63 | [s] 64 | (let [globals (JsePlatform/standardGlobals) 65 | lua-chunk (.load globals (str mocks s)) 66 | res (.call lua-chunk)] 67 | (table-to-map res))) 68 | -------------------------------------------------------------------------------- /src/cljs/skylobby/view/servers_nav.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.view.servers-nav 2 | (:require 3 | [re-frame.core :as rf] 4 | [skylobby.common.util :as cu] 5 | [skylobby.css :as css] 6 | [skylobby.util :as u])) 7 | 8 | 9 | (defn listen [query-v] 10 | @(rf/subscribe query-v)) 11 | 12 | (defn servers-nav [] 13 | (let [current-route (listen [:skylobby/current-route]) 14 | {:keys [parameters]} current-route 15 | server-key (u/params-server-key parameters)] 16 | [:div 17 | [:div {:class "flex justify-center"} 18 | (let [route-name :skylobby/servers] 19 | ^{:key route-name} 20 | [:div 21 | {:class "pa3"} 22 | [:a 23 | {:class (str css/header-class " " 24 | (if (= route-name (-> current-route :data :name)) 25 | "purple" 26 | "gray")) 27 | :href (u/href route-name)} 28 | "Servers"]]) 29 | (for [{:keys [server-id server-url username]} (filter :server-id (listen [:skylobby/active-servers]))] 30 | (let [is-direct (cu/is-direct? server-id)] 31 | ^{:key (str server-id)} 32 | [:div 33 | {:class "pa3"} 34 | [:a 35 | { 36 | :class (str css/header-class " " 37 | (if (= server-id server-key) 38 | "purple" 39 | "gray")) 40 | :href (if is-direct 41 | (u/href :skylobby/direct-battle {:server-key server-id}) 42 | (u/href :skylobby/battles {:server-url server-url} {:username username}))} 43 | (if is-direct 44 | (let [{:keys [server-type username hostname port]} server-id] 45 | (str (name server-type) " " username "@" hostname ":" port)) 46 | server-id)]]))]])) 47 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | skylobby.fx)) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (def screen-bounds 11 | {:min-x -2560.0 12 | :min-y 0.0 13 | :max-x 2560.0 14 | :max-y 1440.0 15 | :width 5120.0 16 | :height 1440.0}) 17 | 18 | 19 | (deftest fitx 20 | (is (= 0 21 | (with-redefs [skylobby.fx/get-screen-bounds (constantly nil)] 22 | (skylobby.fx/fitx nil)))) 23 | (is (= -128.0 24 | (with-redefs [skylobby.fx/get-screen-bounds (constantly screen-bounds)] 25 | (skylobby.fx/fitx nil)))) 26 | (is (= -128.0 27 | (skylobby.fx/fitx screen-bounds)))) 28 | (deftest fity 29 | (is (= 0 30 | (with-redefs [skylobby.fx/get-screen-bounds (constantly nil)] 31 | (skylobby.fx/fity nil)))) 32 | (is (= 592.0 33 | (with-redefs [skylobby.fx/get-screen-bounds (constantly screen-bounds)] 34 | (skylobby.fx/fity nil)))) 35 | (is (= 592.0 36 | (skylobby.fx/fity screen-bounds)))) 37 | (deftest fitwidth 38 | (is (= 256 39 | (with-redefs [skylobby.fx/get-screen-bounds (constantly nil)] 40 | (skylobby.fx/fitwidth nil -10000)))) 41 | (is (= 256 42 | (with-redefs [skylobby.fx/get-screen-bounds (constantly screen-bounds)] 43 | (skylobby.fx/fitwidth nil -10000)))) 44 | (is (= 256 45 | (skylobby.fx/fitwidth screen-bounds -10000)))) 46 | (deftest fitheight 47 | (is (= 256 48 | (with-redefs [skylobby.fx/get-screen-bounds (constantly nil)] 49 | (skylobby.fx/fitheight nil -10000)))) 50 | (is (= 256 51 | (with-redefs [skylobby.fx/get-screen-bounds (constantly screen-bounds)] 52 | (skylobby.fx/fitheight nil -10000)))) 53 | (is (= 256 54 | (skylobby.fx/fitheight screen-bounds -10000)))) 55 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/css/_layout.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | body { 4 | display: flex; 5 | flex-flow: row nowrap; 6 | align-items: stretch; 7 | justify-content: space-between; 8 | 9 | @media (max-width: $mobile-breakpoint) { 10 | flex-flow: column nowrap; 11 | } 12 | } 13 | 14 | #sidebar { 15 | @media (min-width: $mobile-breakpoint) { 16 | flex: 0 0 auto; 17 | height: 100vh; 18 | } 19 | @media (max-width: $mobile-breakpoint) { 20 | flex: 1 0 auto; 21 | width: 100vw; 22 | } 23 | } 24 | 25 | #sidebar-links { 26 | display: flex; 27 | flex-flow: column nowrap; 28 | justify-content: space-between; 29 | align-items: stretch; 30 | 31 | @media (max-width: $mobile-breakpoint) { 32 | flex-flow: row nowrap; 33 | } 34 | } 35 | 36 | .post-header { 37 | h1 { 38 | margin-top: 0; 39 | } 40 | 41 | .byline { 42 | display: flex; 43 | justify-content: space-between; 44 | margin-bottom: 1.375rem; 45 | 46 | @media (max-width: $tablet-breakpoint) { 47 | margin-bottom: 1.375rem; 48 | } 49 | 50 | @media (max-width: $mobile-breakpoint) { 51 | margin-bottom: 1.25rem; 52 | } 53 | } 54 | } 55 | 56 | #main { 57 | flex: 1 1 0; 58 | 59 | @media (min-width: $mobile-breakpoint) { 60 | height: 100vh; 61 | overflow-y: scroll; 62 | } 63 | } 64 | 65 | #prev-next { 66 | display: flex; 67 | align-items: center; 68 | flex-flow: row nowrap; 69 | justify-content: space-between; 70 | 71 | .prev, .next { 72 | display: flex; 73 | align-items: center; 74 | flex-flow: row nowrap; 75 | 76 | flex: 1 1 50%; 77 | fill: currentColor; 78 | text-decoration: none; 79 | 80 | .icon { 81 | flex: 0 0 1em; 82 | height: 1em; 83 | width: 1em; 84 | } 85 | 86 | .nav-text { 87 | flex: 1 1 0; 88 | margin-left: 1em; 89 | margin-right: 1em; 90 | text-align: center; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) 2 | Removed code from first reset list since it conflics with highlight.js. 3 | */ 4 | 5 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, 6 | a, abbr, acronym, address, big, cite, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, 7 | table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | font-size: 100%; 12 | font: inherit; 13 | vertical-align: baseline; 14 | } 15 | 16 | /* HTML5 display-role reset for older browsers */ 17 | 18 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 19 | display: block; 20 | } 21 | 22 | body { 23 | line-height: 1; 24 | } 25 | 26 | ol, ul { 27 | list-style: none; 28 | } 29 | 30 | blockquote, q { 31 | quotes: none; 32 | } 33 | 34 | blockquote:before, blockquote:after, q:before, q:after { 35 | content: ''; 36 | content: none; 37 | } 38 | 39 | table { 40 | border-collapse: collapse; 41 | border-spacing: 0; 42 | } 43 | 44 | /* Clear Fix Styles */ 45 | 46 | .clearfix:after { 47 | visibility: hidden; 48 | display: block; 49 | font-size: 0; 50 | content: " "; 51 | clear: both; 52 | height: 0; 53 | } 54 | 55 | .clearfix { 56 | display: inline-block; 57 | } 58 | 59 | /* start commented backslash hack \*/ 60 | * html .clearfix { 61 | height: 1%; 62 | } 63 | 64 | .clearfix { 65 | display: block; 66 | } 67 | 68 | /* close commented backslash hack */ 69 | { 70 | height: 1% 71 | ; 72 | } 73 | 74 | .clearfix { 75 | display: block; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /dev/clj/jar/uberjar.clj: -------------------------------------------------------------------------------- 1 | (ns uberjar 2 | (:require 3 | [clojure.java.io :as io] 4 | [clojure.tools.build.api :as b] 5 | [skylobby.git :as git])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (def lib 'skylobby/skylobby) 12 | (def here (io/file ".")) 13 | (def latest-git-id (git/latest-id here)) 14 | (def git-tag (git/tag-or-latest-id here)) 15 | (def version 16 | (if (= latest-git-id git-tag) 17 | (str "git:" (git/short-git-commit latest-git-id)) 18 | git-tag)) 19 | (def src-dirs ["src/clj" "graal/clj" "graal/cljc" "resources"]) 20 | (def class-dir "target/classes") 21 | (def basis (b/create-basis {:project "deps.edn" 22 | :aliases [:headless]})) 23 | (def app-name (name lib)) 24 | (def uber-file (format "target/%s.jar" app-name)) 25 | 26 | 27 | 28 | (defn spit-version-resource [] 29 | (println "Spitting version resource file") 30 | (spit (io/file "resources" (str app-name ".version")) version)) 31 | 32 | 33 | (defn -main [& _args] 34 | (println "\nDeleting target\n") 35 | (b/delete {:path "target"}) 36 | (spit-version-resource) 37 | (b/copy-dir {:src-dirs src-dirs 38 | :target-dir class-dir}) 39 | (println "\nCompiling clj\n") 40 | (b/compile-clj {:basis basis 41 | :src-dirs src-dirs 42 | :class-dir class-dir 43 | :java-opts [ 44 | "-Dtestfx.robot=glass" 45 | "-Dglass.platform=Monocle" 46 | "-Dmonocle.platform=Headless" 47 | "-Dprism.order=sw"]}) 48 | (println "\nBuilding uberjar\n") 49 | (b/uber {:class-dir class-dir 50 | :uber-file uber-file 51 | :basis basis 52 | :main 'spring-lobby.main 53 | :manifest {"Build-Number" version}}) 54 | (println "\nDeleting classes dir\n") 55 | (b/delete {:path "target/classes"}) 56 | (println "\nExiting\n") 57 | (System/exit 0)) 58 | -------------------------------------------------------------------------------- /graal/cljc/skylobby/data.cljc: -------------------------------------------------------------------------------- 1 | (ns skylobby.data 2 | (:require 3 | [clojure.string :as string])) 4 | 5 | 6 | (defn filter-battles 7 | [battles {:keys [filter-battles 8 | hide-empty-battles 9 | hide-locked-battles 10 | hide-passworded-battles 11 | hide-running-battles 12 | users]}] 13 | (let [ 14 | filter-battles (when (string? filter-battles) 15 | filter-battles) 16 | filter-lc (when-not (string/blank? filter-battles) 17 | (string/lower-case filter-battles))] 18 | (->> battles 19 | vals 20 | (filter :battle-title) 21 | (filter 22 | (fn [{:keys [battle-map battle-modname battle-title]}] 23 | (if filter-lc 24 | (or (and battle-map (string/includes? (string/lower-case battle-map) filter-lc)) 25 | (and battle-modname (string/includes? (string/lower-case battle-modname) filter-lc)) 26 | (string/includes? (string/lower-case battle-title) filter-lc)) 27 | true))) 28 | (remove 29 | (fn [{:keys [battle-passworded]}] 30 | (if hide-passworded-battles 31 | (= "1" battle-passworded) 32 | false))) 33 | (remove 34 | (fn [{:keys [battle-locked]}] 35 | (if hide-locked-battles 36 | (= "1" battle-locked) 37 | false))) 38 | (remove 39 | (fn [{:keys [users]}] 40 | (if hide-empty-battles 41 | (boolean (<= (count users) 1)) ; TODO bot vs human hosts 42 | false))) 43 | (remove 44 | (fn [{:keys [host-username]}] 45 | (if hide-running-battles 46 | (boolean (get-in users [host-username :client-status :ingame])) 47 | false))) 48 | (sort-by (juxt (comp count :users) :battle-spectators)) 49 | reverse 50 | doall))) 51 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/players_table_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.players-table-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.players-table :as fx.players-table])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest ai-options-window 12 | (is (map? 13 | (fx.players-table/ai-options-window 14 | {:fx/context (fx/create-context {})})))) 15 | 16 | (deftest player-context-menu 17 | (is (map? 18 | (fx.players-table/player-context-menu 19 | {:fx/context (fx/create-context {})})))) 20 | 21 | (deftest player-status-tooltip-label 22 | (is (map? 23 | (fx.players-table/player-status-tooltip-label 24 | {:fx/context (fx/create-context {})})))) 25 | 26 | (deftest player-status 27 | (is (map? 28 | (fx.players-table/player-status 29 | {:fx/context (fx/create-context {})})))) 30 | 31 | (deftest player-name-tooltip 32 | (is (map? 33 | (fx.players-table/player-name-tooltip 34 | {:fx/context (fx/create-context {})})))) 35 | 36 | 37 | (def fake-players 38 | [{} 39 | {:battle-status {:mode true 40 | :ally 0 41 | :id 0}}]) 42 | 43 | (deftest players-table 44 | (is (map? 45 | (fx.players-table/players-table-impl 46 | {:fx/context 47 | (fx/create-context 48 | { 49 | :players-table-columns {:skill true 50 | :ally true 51 | :team true 52 | :color true 53 | :status true 54 | :spectator true 55 | :faction true 56 | :country true 57 | :bonus true}}) 58 | :players fake-players})))) 59 | 60 | 61 | (deftest players-not-a-table 62 | (is (map? 63 | (fx.players-table/players-not-a-table 64 | {:fx/context 65 | (fx/create-context {}) 66 | :players fake-players})))) 67 | -------------------------------------------------------------------------------- /src/cljs/skylobby/util.cljs: -------------------------------------------------------------------------------- 1 | (ns skylobby.util 2 | (:require 3 | ["dayjs" :as dayjs] 4 | ["dayjs/plugin/utc" :as utc] 5 | [clojure.edn :as edn] 6 | [clojure.string :as string] 7 | [reitit.frontend.easy :as rfe])) 8 | 9 | 10 | (.extend dayjs utc) 11 | 12 | 13 | (defn get-server-key [server-url username] 14 | (str username "@" server-url)) 15 | 16 | (defn params-server-key [parameters] 17 | (if-let [s (get-in parameters [:path :server-key])] 18 | (edn/read-string s) 19 | (let [ 20 | server-url (-> parameters :path :server-url) 21 | username (-> parameters :query :username)] 22 | (get-server-key server-url username)))) 23 | 24 | 25 | (defn href 26 | "Return relative url for given route. Url can be used in HTML links." 27 | ([k] 28 | (href k nil nil)) 29 | ([k params] 30 | (href k params nil)) 31 | ([k params query] 32 | (rfe/href k params query))) 33 | 34 | 35 | (defn time-diff 36 | ([start] 37 | (time-diff start (dayjs))) 38 | ([start end] 39 | (dayjs/utc 40 | (.diff 41 | (dayjs end) 42 | (dayjs start) 43 | "milliseconds")))) 44 | 45 | (defn format-hours 46 | [timestamp] 47 | (.format (dayjs timestamp) "HH:mm:ss")) 48 | 49 | 50 | (defn spring-color-to-web [spring-color] 51 | (let [i (cond 52 | (string? spring-color) 53 | (js/parseInt spring-color) 54 | (number? spring-color) 55 | (int spring-color) 56 | :else 0) 57 | ; https://stackoverflow.com/a/11866980 58 | b (bit-and i 0xFF) 59 | g (unsigned-bit-shift-right (bit-and i 0xFF00) 8) 60 | r (unsigned-bit-shift-right (bit-and i 0xFF0000) 16)] 61 | ; string colors reverse r and b 62 | (str "#" (.padStart (.toString b 16) 2 "0") 63 | (.padStart (.toString g 16) 2 "0") 64 | (.padStart (.toString r 16) 2 "0")))) 65 | 66 | 67 | (defn battle-channel-name [{:keys [battle-id channel-name]}] 68 | (or channel-name 69 | (str "__battle__" battle-id))) 70 | 71 | (defn battle-channel-name? [channel-name] 72 | (and channel-name 73 | (string/starts-with? channel-name "__battle__"))) 74 | -------------------------------------------------------------------------------- /graal/clj/skylobby/git.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.git 2 | "Utils for working with git resources." 3 | (:require 4 | [clj-jgit.porcelain :as git] 5 | [clojure.string :as string]) 6 | (:import 7 | (java.io File) 8 | (org.eclipse.jgit.api Git) 9 | (org.eclipse.jgit.lib ObjectId Ref Repository) 10 | (org.eclipse.jgit.revwalk RevCommit))) 11 | 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | 16 | (def ba-repo-url 17 | "https://github.com/Balanced-Annihilation/Balanced-Annihilation.git") 18 | (def bar-repo-url 19 | "https://github.com/beyond-all-reason/Beyond-All-Reason.git") 20 | (def known-mod-repos 21 | [ba-repo-url 22 | bar-repo-url]) 23 | 24 | (defn- repo-latest-id [repo] 25 | (when-let [^RevCommit first-log (first (git/git-log repo))] 26 | (ObjectId/toString (:id first-log)))) 27 | 28 | (defn latest-id [^File f] 29 | (with-open [repo (git/load-repo f)] 30 | (repo-latest-id repo))) 31 | 32 | (defn fetch [^File f] 33 | (with-open [repo (git/load-repo f)] 34 | (git/git-fetch repo))) 35 | 36 | (defn reset-hard [^File f commit-id] 37 | (with-open [repo (git/load-repo f)] 38 | (git/git-reset repo :ref commit-id :mode :hard))) 39 | 40 | (defn- repo-tag-list [^Git repo] 41 | (for [^Ref tag (.call (.tagList repo))] 42 | (let [tag-name (.getName tag)] 43 | {:tag-name tag-name 44 | :short-tag-name (when tag-name (Repository/shortenRefName tag-name)) 45 | :object-id (ObjectId/toString (.getObjectId tag))}))) 46 | 47 | (defn tag-list [^File f] 48 | (with-open [repo (git/load-repo f)] 49 | (repo-tag-list repo))) 50 | 51 | ; https://stackoverflow.com/a/14933815/984393 52 | (defn tag-or-latest-id 53 | "Returns the current tag, if any. If there is no tag, returns the commit id string." 54 | [^File f] 55 | (with-open [repo (git/load-repo f)] 56 | (when-let [latest-id (repo-latest-id repo)] 57 | (let [tag-names-by-id (->> repo 58 | repo-tag-list 59 | (map (juxt :object-id :short-tag-name)) 60 | (into {}))] 61 | (or (get tag-names-by-id latest-id) 62 | latest-id))))) 63 | 64 | (defn short-git-commit [git-commit-id] 65 | (when-not (string/blank? git-commit-id) 66 | (subs git-commit-id 0 (min 7 (count git-commit-id))))) 67 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/server_tab.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.server-tab 2 | (:require 3 | [cljfx.api :as fx] 4 | skylobby.fx 5 | [skylobby.fx.battle :as fx.battle] 6 | [skylobby.fx.main-tabs :as fx.main-tabs] 7 | [taoensso.tufte :as tufte])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (defn- server-tab-impl 14 | [{:fx/keys [context] 15 | :keys [server-key]}] 16 | (let [battle-as-tab (fx/sub-val context :battle-as-tab) 17 | pop-out-battle (fx/sub-val context :pop-out-battle) 18 | battle (fx/sub-val context get-in [:by-server server-key :battle]) 19 | in-battle (and (seq battle) 20 | (or (not (:battle-id battle)) 21 | (not pop-out-battle))) 22 | show-battle-pane (and in-battle (not battle-as-tab)) 23 | divider-positions (fx/sub-val context :divider-positions) 24 | divider-key :server-tab 25 | tabs {:fx/type fx.main-tabs/main-tab-view 26 | :v-box/vgrow :always 27 | :server-key server-key}] 28 | {:fx/type :v-box 29 | :style {:-fx-font-size 14} 30 | :alignment :top-left 31 | :children 32 | [ 33 | (if show-battle-pane 34 | {:fx/type fx/ext-on-instance-lifecycle 35 | :v-box/vgrow :always 36 | :on-created (fn [^javafx.scene.control.SplitPane node] 37 | (skylobby.fx/add-divider-listener node divider-key)) 38 | :desc 39 | {:fx/type :split-pane 40 | :orientation :vertical 41 | :divider-positions [(or (get divider-positions divider-key) 42 | 0.20)] 43 | :items 44 | [tabs 45 | (if (:battle-id battle) 46 | {:fx/type fx.battle/battle-view 47 | :server-key server-key} 48 | {:fx/type :h-box 49 | :alignment :top-left 50 | :children 51 | [{:fx/type :v-box 52 | :h-box/hgrow :always 53 | :children 54 | [{:fx/type :label 55 | :style {:-fx-font-size 20} 56 | :text "Waiting for battle details from server..."}]}]})]}} 57 | tabs)]})) 58 | 59 | (defn server-tab [state] 60 | (tufte/profile {:dynamic? true 61 | :id :skylobby/ui} 62 | (tufte/p :server-tab 63 | (server-tab-impl state)))) 64 | -------------------------------------------------------------------------------- /test/clj/skylobby/fx/battle_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battle-test 2 | (:require 3 | [cljfx.api :as fx] 4 | [clojure.test :refer [deftest is]] 5 | [skylobby.fx.battle :as fx.battle])) 6 | 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | 11 | (deftest sync-button 12 | (is (map? 13 | (fx.battle/sync-button 14 | {:fx/context (fx/create-context nil)})))) 15 | 16 | 17 | (deftest spring-debug-window 18 | (is (map? 19 | (fx.battle/spring-debug-window 20 | {:fx/context (fx/create-context nil) 21 | :screen-bounds {}})))) 22 | 23 | 24 | (deftest battle-buttons 25 | (is (map? 26 | (fx.battle/battle-buttons 27 | {:fx/context (fx/create-context nil)}))) 28 | (is (map? 29 | (fx.battle/battle-buttons 30 | {:fx/context (fx/create-context nil) 31 | :server-key :local})))) 32 | 33 | 34 | (deftest battle-tabs 35 | (is (map? 36 | (fx.battle/battle-tabs 37 | {:fx/context (fx/create-context nil)})))) 38 | 39 | 40 | (deftest battle-votes-impl 41 | (is (map? 42 | (fx.battle/battle-votes-impl 43 | {:fx/context (fx/create-context nil)}))) 44 | (is (map? 45 | (fx.battle/battle-votes-impl 46 | {:fx/context (fx/create-context {:show-vote-log true})}))) 47 | (is (map? 48 | (fx.battle/battle-votes-impl 49 | {:fx/context (fx/create-context {:show-vote-log true 50 | :by-server {"server" {:battle {:battle-id "1"} 51 | :battles {"1" {:channel-name "channel" 52 | :host-username "host"}} 53 | :channels {"channel" {:messages [{:username "host" 54 | :message-type :ex 55 | :spads {:spads-message-type :called-vote 56 | :spads-parsed ["" "user" "vote"]}}]}}}}}) 57 | :server-key "server"})))) 58 | 59 | 60 | (deftest battle-view 61 | (is (map? 62 | (fx.battle/battle-view 63 | {:fx/context (fx/create-context nil)})))) 64 | -------------------------------------------------------------------------------- /dev/clj/user/repl.clj: -------------------------------------------------------------------------------- 1 | (ns repl 2 | (:require 3 | [cider.nrepl] 4 | [clojure.string :as string] 5 | [clojure.tools.cli :as cli] 6 | hashp.core 7 | [io.aviso.repl] 8 | [nrepl.cmdline] 9 | [reply.main] 10 | [taoensso.timbre :as timbre] 11 | [taoensso.timbre.appenders.community.rotor :as rotor] 12 | user)) 13 | 14 | 15 | (set! *warn-on-reflection* true) 16 | 17 | 18 | (def ^:private dev-log-path "repl.log") 19 | 20 | 21 | (defn log-to-file [] 22 | (println "Setting up log to" dev-log-path) 23 | (timbre/merge-config! 24 | {:min-level :info 25 | :appenders 26 | {:rotor (rotor/rotor-appender 27 | {:path dev-log-path 28 | :max-size 10000000 29 | :backlog 5})}})) 30 | 31 | (defn disable-print-log [] 32 | (timbre/merge-config! 33 | {:appenders 34 | {:println {:enabled? false}}})) 35 | 36 | 37 | (def middleware 38 | [;'cider.nrepl/cider-middleware 39 | 'cider.nrepl/wrap-clojuredocs 40 | 'cider.nrepl/wrap-classpath 41 | 'cider.nrepl/wrap-complete 42 | 'cider.nrepl/wrap-debug 43 | 'cider.nrepl/wrap-format 44 | 'cider.nrepl/wrap-info 45 | 'cider.nrepl/wrap-inspect 46 | 'cider.nrepl/wrap-macroexpand 47 | 'cider.nrepl/wrap-ns 48 | 'cider.nrepl/wrap-spec 49 | 'cider.nrepl/wrap-profile 50 | 'cider.nrepl/wrap-resource 51 | ;'cider.nrepl/wrap-refresh TODO re-render 52 | 'cider.nrepl/wrap-stacktrace 53 | 'cider.nrepl/wrap-test 54 | 'cider.nrepl/wrap-trace 55 | 'cider.nrepl/wrap-out 56 | 'cider.nrepl/wrap-undef 57 | 'cider.nrepl/wrap-version]) 58 | 59 | (def cli-options []) 60 | 61 | (defn -main [& args] 62 | (let [parsed (cli/parse-opts args cli-options)] 63 | (log-to-file) 64 | (disable-print-log) 65 | (future 66 | (nrepl.cmdline/-main 67 | "--middleware" 68 | (str "[" (string/join "," middleware) "]") 69 | ;"--interactive" 70 | "--color")) 71 | (user/init parsed) 72 | (loop [] 73 | (when 74 | (try 75 | (println "Trying to start REPLy") 76 | (reply.main/launch-nrepl 77 | {:attach (slurp ".nrepl-port") 78 | :caught io.aviso.repl/pretty-pst 79 | :color true}) 80 | false 81 | (catch Exception _e 82 | (println "Error connecting REPLy, sleeping") 83 | (Thread/sleep 500) 84 | true)) 85 | (recur))) 86 | (System/exit 0))) 87 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/battles_window.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battles-window 2 | (:require 3 | [cljfx.api :as fx] 4 | skylobby.fx 5 | [skylobby.fx.battles-table :as fx.battles-table] 6 | [skylobby.util :as u] 7 | [taoensso.tufte :as tufte])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (def window-key :battles) 14 | 15 | (def battles-window-width 1600) 16 | (def battles-window-height 1000) 17 | 18 | 19 | (defn battles-window-impl 20 | [{:fx/keys [context] 21 | :keys [screen-bounds]}] 22 | (let [ 23 | window-states (fx/sub-val context :window-states) 24 | show-battles-window (fx/sub-val context :show-battles-window) 25 | server-key (fx/sub-ctx context skylobby.fx/selected-tab-server-key-sub) 26 | client-data (fx/sub-val context get-in [:by-server server-key :client-data]) 27 | show (and show-battles-window client-data)] 28 | {:fx/type :stage 29 | :showing show 30 | :title (str u/app-name " Battles " server-key) 31 | :icons skylobby.fx/icons 32 | :on-close-request {:event/type :spring-lobby/dissoc 33 | :key :show-battles-window} 34 | :x (skylobby.fx/fitx screen-bounds (get-in window-states [window-key :x])) 35 | :y (skylobby.fx/fity screen-bounds (get-in window-states [window-key :y])) 36 | :width (skylobby.fx/fitwidth screen-bounds (get-in window-states [window-key :width]) battles-window-width) 37 | :height (skylobby.fx/fitheight screen-bounds (get-in window-states [window-key :height]) battles-window-height) 38 | :on-width-changed (partial skylobby.fx/window-changed window-key :width) 39 | :on-height-changed (partial skylobby.fx/window-changed window-key :height) 40 | :on-x-changed (partial skylobby.fx/window-changed window-key :x) 41 | :on-y-changed (partial skylobby.fx/window-changed window-key :y) 42 | :scene 43 | {:fx/type :scene 44 | :stylesheets (fx/sub-ctx context skylobby.fx/stylesheet-urls-sub) 45 | :root 46 | (if show 47 | {:fx/type fx.battles-table/battles-table 48 | :v-box/vgrow :always 49 | :server-key (u/server-key client-data)} 50 | {:fx/type :pane 51 | :pref-width battles-window-width 52 | :pref-height battles-window-height})}})) 53 | 54 | (defn battles-window-view [state] 55 | (tufte/profile {:dynamic? true 56 | :id :skylobby/ui} 57 | (tufte/p :battles-window 58 | (battles-window-impl state)))) 59 | -------------------------------------------------------------------------------- /graal/clj/skylobby/task.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.task 2 | (:require 3 | [taoensso.timbre :as log])) 4 | 5 | 6 | (set! *warn-on-reflection* true) 7 | 8 | 9 | (def index-tasks 10 | #{:spring-lobby/refresh-engines 11 | :spring-lobby/refresh-mods 12 | :spring-lobby/refresh-maps}) 13 | 14 | (def resource-tasks 15 | #{ 16 | :spring-lobby/map-details 17 | :spring-lobby/mod-details 18 | :spring-lobby/update-cached-minimaps}) 19 | 20 | (def download-tasks 21 | #{ 22 | :spring-lobby/download-and-extract 23 | :spring-lobby/download-springfiles 24 | :spring-lobby/extract-7z 25 | :spring-lobby/import 26 | :spring-lobby/http-downloadable}) 27 | 28 | (def rapid-tasks 29 | #{ 30 | :spring-lobby/rapid-download 31 | :spring-lobby/update-rapid 32 | :spring-lobby/update-rapid-packages 33 | :spring-lobby/delete-corrupt-rapid}) 34 | 35 | (def task-kinds 36 | [:spring-lobby/index-task 37 | :spring-lobby/resource-task 38 | :spring-lobby/download-task 39 | :spring-lobby/rapid-task 40 | :spring-lobby/other-task]) 41 | 42 | (defn task-kind [{:spring-lobby/keys [task-type]}] 43 | (cond 44 | (contains? index-tasks task-type) :spring-lobby/index-task 45 | (contains? resource-tasks task-type) :spring-lobby/resource-task 46 | (contains? download-tasks task-type) :spring-lobby/download-task 47 | (contains? rapid-tasks task-type) :spring-lobby/rapid-task 48 | :else :spring-lobby/other-task)) 49 | 50 | (defn add-task-state [state task] 51 | (if task 52 | (let [task-kind (task-kind task)] 53 | (log/info "Adding task" (pr-str task) "to" task-kind) 54 | (update-in state [:tasks-by-kind task-kind] 55 | (fn [tasks] 56 | (set (conj tasks task))))) 57 | (do 58 | (log/warn "Attempt to add nil task" task) 59 | state))) 60 | 61 | (defn add-task! [state-atom task] 62 | (swap! state-atom add-task-state task)) 63 | 64 | (defn add-multiple-tasks [tasks-by-kind new-tasks] 65 | (reduce-kv 66 | (fn [m k new-tasks] 67 | (update m k (fn [existing] 68 | (set (concat new-tasks existing))))) 69 | tasks-by-kind 70 | (group-by task-kind new-tasks))) 71 | 72 | (defn add-tasks! [state-atom new-tasks] 73 | (log/info "Adding tasks" (pr-str new-tasks)) 74 | (swap! state-atom update :tasks-by-kind add-multiple-tasks new-tasks)) 75 | 76 | 77 | (defn all-tasks [{:keys [current-tasks tasks-by-kind]}] 78 | (->> tasks-by-kind 79 | (mapcat second) 80 | (concat (vals current-tasks)) 81 | (filter some?) 82 | doall)) 83 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/channels.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.channels 2 | (:require 3 | [cljfx.api :as fx] 4 | skylobby.fx 5 | [skylobby.util :as u] 6 | [taoensso.tufte :as tufte])) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (defn sorted-channels-sub [context server-key] 13 | (let [ 14 | channels (fx/sub-val context get-in [:by-server server-key :channels])] 15 | (->> (vals channels) 16 | u/non-battle-channels 17 | (sort-by :channel-name String/CASE_INSENSITIVE_ORDER) 18 | doall))) 19 | 20 | (defn- channels-table-impl 21 | [{:fx/keys [context] 22 | :keys [server-key]}] 23 | (let [ 24 | my-channels (fx/sub-val context get-in [:by-server server-key :my-channels]) 25 | sorted-channels (fx/sub-ctx context sorted-channels-sub server-key)] 26 | {:fx/type :table-view 27 | :style {:-fx-min-width "240"} 28 | :column-resize-policy :constrained 29 | :items sorted-channels 30 | :columns 31 | [{:fx/type :table-column 32 | :text "Channel" 33 | :pref-width 100 34 | :cell-value-factory identity 35 | :cell-factory 36 | {:fx/cell-type :table-cell 37 | :describe (fn [i] {:text (str (:channel-name i))})}} 38 | {:fx/type :table-column 39 | :text "Users" 40 | :resizable false 41 | :pref-width 60 42 | :cell-value-factory identity 43 | :cell-factory 44 | {:fx/cell-type :table-cell 45 | :describe (fn [i] {:text (str (:user-count i))})}} 46 | {:fx/type :table-column 47 | :text "Actions" 48 | :resizable false 49 | :pref-width 80 50 | :cell-value-factory identity 51 | :cell-factory 52 | {:fx/cell-type :table-cell 53 | :describe 54 | (fn [{:keys [channel-name]}] 55 | {:text "" 56 | :graphic 57 | (merge 58 | {:fx/type :button} 59 | (if (contains? my-channels channel-name) 60 | {:text "Leave" 61 | :on-action {:event/type :spring-lobby/leave-channel 62 | :channel-name channel-name 63 | :server-key server-key}} 64 | {:text "Join" 65 | :on-action {:event/type :spring-lobby/join-channel 66 | :channel-name channel-name 67 | :server-key server-key}}))})}}]})) 68 | 69 | (defn channels-table [state] 70 | (tufte/profile {:dynamic? true 71 | :id :skylobby/ui} 72 | (tufte/p :channels-table 73 | (channels-table-impl state)))) 74 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/battle_chat.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.battle-chat 2 | (:require 3 | [cljfx.api :as fx] 4 | skylobby.fx 5 | [skylobby.fx.channel :as fx.channel] 6 | [skylobby.util :as u] 7 | [taoensso.tufte :as tufte])) 8 | 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | 13 | (def window-key :battle-chat) 14 | 15 | (def battle-chat-window-width 1600) 16 | (def battle-chat-window-height 1000) 17 | 18 | 19 | (defn battle-chat-window-impl 20 | [{:fx/keys [context] 21 | :keys [screen-bounds]}] 22 | (let [pop-out-chat (fx/sub-val context :pop-out-chat) 23 | window-states (fx/sub-val context :window-states) 24 | server-key (fx/sub-ctx context skylobby.fx/selected-tab-server-key-sub) 25 | battle-id (fx/sub-val context get-in [:by-server server-key :battle :battle-id]) 26 | show-battle-chat-window (boolean (and pop-out-chat battle-id))] 27 | {:fx/type :stage 28 | :showing show-battle-chat-window 29 | :title (str u/app-name " Battle Chat") 30 | :icons skylobby.fx/icons 31 | :on-close-request {:event/type :spring-lobby/dissoc 32 | :key :pop-out-chat} 33 | :x (skylobby.fx/fitx screen-bounds (get-in window-states [window-key :x] 0)) 34 | :y (skylobby.fx/fity screen-bounds (get-in window-states [window-key :y] 0)) 35 | :width (skylobby.fx/fitwidth screen-bounds (get-in window-states [window-key :width]) battle-chat-window-width) 36 | :height (skylobby.fx/fitheight screen-bounds (get-in window-states [window-key :height]) battle-chat-window-height) 37 | :on-width-changed (partial skylobby.fx/window-changed window-key :width) 38 | :on-height-changed (partial skylobby.fx/window-changed window-key :height) 39 | :on-x-changed (partial skylobby.fx/window-changed window-key :x) 40 | :on-y-changed (partial skylobby.fx/window-changed window-key :y) 41 | :scene 42 | {:fx/type :scene 43 | :stylesheets (fx/sub-ctx context skylobby.fx/stylesheet-urls-sub) 44 | :root 45 | (if show-battle-chat-window 46 | {:fx/type fx.channel/channel-view 47 | :h-box/hgrow :always 48 | :channel-name (fx/sub-ctx context skylobby.fx/battle-channel-sub server-key) 49 | :hide-users true 50 | :server-key server-key} 51 | {:fx/type :pane 52 | :pref-width battle-chat-window-width 53 | :pref-height battle-chat-window-height})}})) 54 | 55 | (defn battle-chat-window [state] 56 | (tufte/profile {:dynamic? true 57 | :id :skylobby/ui} 58 | (tufte/p :battle-chat-window 59 | (battle-chat-window-impl state)))) 60 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/css/typography.css: -------------------------------------------------------------------------------- 1 | .textleft { 2 | text-align: left; 3 | } 4 | 5 | .textcentre { 6 | text-align: center; 7 | } 8 | 9 | .textright { 10 | text-align: right; 11 | } 12 | 13 | p { 14 | margin: 0 0 20px 0; 15 | } 16 | 17 | strong, b { 18 | font-weight: 600; 19 | } 20 | 21 | em, i { 22 | font-style: italic; 23 | } 24 | 25 | h1 { 26 | margin: 0 0 10px 0; 27 | font-weight: 700; 28 | font-family: "Montserrat"; 29 | text-transform: uppercase; 30 | font-size: 46px; 31 | line-height: 42px; 32 | letter-spacing: -1px; 33 | color: #111; 34 | } 35 | 36 | h2 { 37 | margin: 30px 0 15px 0; 38 | font-weight: 350; 39 | font-family: "Montserrat"; 40 | text-transform: uppercase; 41 | font-size: 23px; 42 | line-height: 21px; 43 | letter-spacing: -0.5px; 44 | color: #111; 45 | } 46 | 47 | h3 { 48 | margin: 26px 0 13px 0; 49 | font-weight: 200; 50 | font-family: "Montserrat"; 51 | font-size: 16px; 52 | line-height: 16px; 53 | letter-spacing: -0.4px; 54 | color: #111; 55 | } 56 | 57 | h4, h5, h6 { 58 | margin: 20px 0 10px 0; 59 | font-weight: 100; 60 | font-family: "Montserrat"; 61 | font-size: 12px; 62 | line-height: 12px; 63 | color: #111; 64 | } 65 | 66 | ul { 67 | margin: 0 0 20px 35px; 68 | list-style-type: square; 69 | } 70 | 71 | ul li { 72 | padding: 0 0 0 2px; 73 | } 74 | 75 | ul ul { 76 | margin: 0 0 0 25px; 77 | } 78 | 79 | ol { 80 | margin: 0 0 20px 35px; 81 | list-style-type: decimal; 82 | } 83 | 84 | ol li { 85 | padding: 0 0 0 2px; 86 | } 87 | 88 | ol ol { 89 | margin: 0 0 0 25px; 90 | } 91 | 92 | hr { 93 | clear: both; 94 | float: left; 95 | width: 100%; 96 | padding: 0; 97 | margin: 30px 0 40px 0; 98 | border: none; 99 | border-top: 4px solid #A71E2E; 100 | text-align: center; 101 | } 102 | 103 | blockquote { 104 | background: #EEE; 105 | margin: 0 0 5px 0 !important; 106 | color: #555; 107 | border-left: 5px solid #A71E2E; 108 | padding: 15px; 109 | font-style: italic; 110 | } 111 | 112 | blockquote p { 113 | margin: 0; 114 | } 115 | 116 | blockquote p.author { 117 | text-align: right; 118 | font-size: 14px; 119 | letter-spacing: 2px; 120 | color: #999; 121 | } 122 | 123 | @media screen and (max-width: 768px) { 124 | 125 | .textcentre { 126 | text-align: left; 127 | } 128 | 129 | .textright { 130 | text-align: left; 131 | } 132 | 133 | } 134 | 135 | 136 | -------------------------------------------------------------------------------- /native-res/linux/META-INF/native-image/skylobby/java/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[{ 4 | "pattern":"\\QLinux-amd64/lib7-Zip-JBinding.so\\E" 5 | }, { 6 | "pattern":"\\QLinux-amd64/sevenzipjbinding-lib.properties\\E" 7 | }, { 8 | "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" 9 | }, { 10 | "pattern":"\\QMETA-INF/services/java.sql.Driver\\E" 11 | }, { 12 | "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" 13 | }, { 14 | "pattern":"\\QMETA-INF/services/javax.xml.datatype.DatatypeFactory\\E" 15 | }, { 16 | "pattern":"\\Qmozilla/public-suffix-list.txt\\E" 17 | }, { 18 | "pattern":"\\Qnet/jpountz/util/linux/amd64/liblz4-java.so\\E" 19 | }, { 20 | "pattern":"\\Qorg/apache/http/client/version.properties\\E" 21 | }, { 22 | "pattern":"\\Qorg/h2/util/data.zip\\E" 23 | }, { 24 | "pattern":"\\Qorg/jsoup/nodes/entities-base.properties\\E" 25 | }, { 26 | "pattern":"\\Qorg/jsoup/nodes/entities-full.properties\\E" 27 | }, { 28 | "pattern":"\\Qpublic/css/tachyons.min.css\\E" 29 | }, { 30 | "pattern":"\\Qpublic/iconfont/material-icons.css\\E" 31 | }, { 32 | "pattern":"\\Qpublic/iconfont/material-icons.woff2\\E" 33 | }, { 34 | "pattern":"\\Qpublic/images/favicon.ico\\E" 35 | }, { 36 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.async.impl.channels.js.map\\E" 37 | }, { 38 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.async.impl.dispatch.js.map\\E" 39 | }, { 40 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.async.impl.ioc_helpers.js.map\\E" 41 | }, { 42 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.js.map\\E" 43 | }, { 44 | "pattern":"\\Qpublic/js/cljs-runtime/goog.async.nexttick.js.map\\E" 45 | }, { 46 | "pattern":"\\Qpublic/js/cljs-runtime/module$node_modules$react_dom$cjs$react_dom_development.js.map\\E" 47 | }, { 48 | "pattern":"\\Qpublic/js/cljs-runtime/skylobby.js.js.map\\E" 49 | }, { 50 | "pattern":"\\Qpublic/js/cljs-runtime/taoensso.sente.js.map\\E" 51 | }, { 52 | "pattern":"\\Qpublic/js/cljs-runtime/taoensso.timbre.appenders.core.js.map\\E" 53 | }, { 54 | "pattern":"\\Qpublic/js/cljs-runtime/taoensso.timbre.js.map\\E" 55 | }, { 56 | "pattern":"\\Qpublic/js/main.js\\E" 57 | }, { 58 | "pattern":"\\Qsevenzipjbinding-platforms.properties\\E" 59 | }, { 60 | "pattern":"\\Qskylobby.version\\E" 61 | }, { 62 | "pattern":"\\Qskylobby/util.clj\\E" 63 | }, { 64 | "pattern":"\\Qskylobby/util__init.class\\E" 65 | }, { 66 | "pattern":"java.base:\\Qsun/net/www/content-types.properties\\E" 67 | }]}, 68 | "bundles":[] 69 | } 70 | -------------------------------------------------------------------------------- /src/clj/spring_lobby/replays.clj: -------------------------------------------------------------------------------- 1 | (ns spring-lobby.replays 2 | (:require 3 | [cljfx.api :as fx] 4 | [skylobby.fs :as fs] 5 | skylobby.fx 6 | [skylobby.fx.replay :as fx.replay] 7 | [skylobby.fx.settings :as fx.settings] 8 | [skylobby.util :as u] 9 | spring-lobby 10 | [taoensso.timbre :as log]) 11 | (:import 12 | (javafx.application Platform) 13 | (javafx.event Event)) 14 | (:gen-class)) 15 | 16 | 17 | (set! *warn-on-reflection* true) 18 | 19 | 20 | (def screen-bounds (skylobby.fx/get-screen-bounds)) 21 | 22 | 23 | (defn replays-on-close-request 24 | [^Event e] 25 | (log/debug "Replays window close request" e) 26 | (System/exit 0) 27 | (.consume e)) 28 | 29 | (defn root-view 30 | [_] 31 | {:fx/type fx/ext-many 32 | :desc 33 | [ 34 | {:fx/type fx.replay/replays-window 35 | :on-close-request replays-on-close-request 36 | :screen-bounds screen-bounds 37 | :settings-button true 38 | :show true 39 | :title (str "skyreplays " u/app-version)} 40 | {:fx/type fx.settings/settings-window 41 | :screen-bounds screen-bounds}]}) 42 | 43 | 44 | (defn create-renderer [] 45 | (log/info "Creating renderer") 46 | (let [r (fx/create-renderer 47 | :middleware (comp 48 | fx/wrap-context-desc 49 | (fx/wrap-map-desc (fn [_] {:fx/type root-view}))) 50 | :opts {:fx.opt/map-event-handler spring-lobby/event-handler 51 | :fx.opt/type->lifecycle #(or (fx/keyword->lifecycle %) 52 | (fx/fn->lifecycle-with-context %))})] 53 | (log/info "Mounting renderer") 54 | (fx/mount-renderer spring-lobby/*ui-state r))) 55 | 56 | (defn -main [& _args] 57 | (u/log-to-file (fs/canonical-path (fs/config-file (str u/app-name ".log")))) 58 | (let [before (u/curr-millis)] 59 | (log/info "Main start") 60 | (Platform/setImplicitExit true) 61 | (log/info "Set JavaFX implicit exit") 62 | (future 63 | (log/info "Start 7Zip init, async") 64 | (fs/init-7z!) 65 | (log/info "Finished 7Zip init")) 66 | (let [before-state (u/curr-millis) 67 | _ (log/info "Loading initial state") 68 | state (assoc (spring-lobby/initial-state) :show-replays true :standalone true)] 69 | (log/info "Loaded initial state in" (- (u/curr-millis) before-state) "ms") 70 | (reset! spring-lobby/*state state) 71 | (spring-lobby/add-ui-state-watcher spring-lobby/*state spring-lobby/*ui-state) 72 | (create-renderer)) 73 | (spring-lobby/init-async spring-lobby/*state) 74 | (log/info "Main finished in" (- (u/curr-millis) before) "ms"))) 75 | -------------------------------------------------------------------------------- /cryogen/themes/nucleus/css/menu.css: -------------------------------------------------------------------------------- 1 | /** Menu Styles **/ 2 | 3 | #menucont ul.menu { 4 | margin: 0; 5 | } 6 | 7 | #menucont ul.menu li { 8 | position: relative; 9 | float: left; 10 | width: 100%; 11 | list-style-type: none; 12 | padding: 0; 13 | } 14 | 15 | #menucont ul.menu li a { 16 | float: left; 17 | color: #FFF; 18 | width: 100%; 19 | padding: 10px 20px; 20 | margin: 0; 21 | font-weight: 300; 22 | text-transform: uppercase; 23 | line-height: 22px; 24 | text-decoration: none; 25 | letter-spacing: 2px; 26 | } 27 | 28 | #menucont ul.menu li a:hover { 29 | background: rgba(0, 0, 0, 0.2); 30 | } 31 | 32 | #menucont ul.menu li.active a, 33 | #menucont ul.menu li.active a:hover { 34 | background: rgba(0, 0, 0, 0.2); 35 | font-weight: 600; 36 | } 37 | 38 | #menucont .menutitle { 39 | display: none; 40 | } 41 | 42 | @media screen and (max-width: 480px) { 43 | 44 | #menucont .menu { 45 | display: none; 46 | margin: 0; 47 | } 48 | 49 | #menucont .menu.open { 50 | display: block; 51 | } 52 | 53 | #menucont ul.menu li { 54 | position: relative; 55 | float: none; 56 | } 57 | 58 | #menucont ul.menu li, #menucont ul.menu ul li { 59 | display: block; 60 | } 61 | 62 | #menucont ul.menu li a { 63 | width: 100%; 64 | display: inline-block; 65 | margin: 0; 66 | padding: 5px 0; 67 | text-indent: 0; 68 | line-height: 23px; 69 | color: rgba(255, 255, 255, 0.6); 70 | background: none; 71 | font-weight: 300; 72 | text-transform: uppercase; 73 | } 74 | 75 | #menucont ul.menu li.active a, 76 | #menucont ul.menu li.active a:hover { 77 | font-weight: 600; 78 | color: #FFF; 79 | background: none; 80 | } 81 | 82 | #menucont ul.menu li a:hover { 83 | color: #FFF; 84 | background: none; 85 | } 86 | 87 | #menucont ul.menu > li:last-child a { 88 | margin: 0 0 20px 0; 89 | } 90 | 91 | #menucont .menutitle { 92 | display: block; 93 | width: 100%; 94 | padding: 12px 42px 12px 0; 95 | display: block; 96 | cursor: pointer; 97 | text-align: left; 98 | color: #FFF; 99 | text-transform: uppercase; 100 | } 101 | 102 | #menucont .menutitle p { 103 | margin-bottom: 0; 104 | } 105 | 106 | #menucont .menutitle p strong { 107 | margin: 0 0 0 15px; 108 | font-size: 18px; 109 | font-weight: 300; 110 | letter-spacing: 2px; 111 | } 112 | 113 | #menucont .menutitle span { 114 | margin: 0; 115 | font-size: 20px; 116 | } 117 | 118 | } 119 | 120 | -------------------------------------------------------------------------------- /native-res/windows/META-INF/native-image/skylobby/java/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[{ 4 | "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" 5 | }, { 6 | "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" 7 | }, { 8 | "pattern":"\\QMETA-INF/services/java.sql.Driver\\E" 9 | }, { 10 | "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" 11 | }, { 12 | "pattern":"\\QMETA-INF/services/javax.xml.datatype.DatatypeFactory\\E" 13 | }, { 14 | "pattern":"\\QWindows-amd64/lib7-Zip-JBinding.dll\\E" 15 | }, { 16 | "pattern":"\\QWindows-amd64/sevenzipjbinding-lib.properties\\E" 17 | }, { 18 | "pattern":"\\Qmozilla/public-suffix-list.txt\\E" 19 | }, { 20 | "pattern":"\\Qnet/jpountz/util/win32/amd64/liblz4-java.so\\E" 21 | }, { 22 | "pattern":"\\Qorg/apache/http/client/version.properties\\E" 23 | }, { 24 | "pattern":"\\Qorg/h2/util/data.zip\\E" 25 | }, { 26 | "pattern":"\\Qpublic/css/tachyons.min.css\\E" 27 | }, { 28 | "pattern":"\\Qpublic/iconfont/material-icons.css\\E" 29 | }, { 30 | "pattern":"\\Qpublic/iconfont/material-icons.woff2\\E" 31 | }, { 32 | "pattern":"\\Qpublic/images/favicon.ico\\E" 33 | }, { 34 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.async.impl.dispatch.js.map\\E" 35 | }, { 36 | "pattern":"\\Qpublic/js/cljs-runtime/cljs.core.js.map\\E" 37 | }, { 38 | "pattern":"\\Qpublic/js/cljs-runtime/goog.async.nexttick.js.map\\E" 39 | }, { 40 | "pattern":"\\Qpublic/js/cljs-runtime/module$node_modules$react$cjs$react_development.js.map\\E" 41 | }, { 42 | "pattern":"\\Qpublic/js/cljs-runtime/module$node_modules$react_dom$cjs$react_dom_development.js.map\\E" 43 | }, { 44 | "pattern":"\\Qpublic/js/cljs-runtime/re_frame.events.js.map\\E" 45 | }, { 46 | "pattern":"\\Qpublic/js/cljs-runtime/re_frame.fx.js.map\\E" 47 | }, { 48 | "pattern":"\\Qpublic/js/cljs-runtime/re_frame.interceptor.js.map\\E" 49 | }, { 50 | "pattern":"\\Qpublic/js/cljs-runtime/re_frame.router.js.map\\E" 51 | }, { 52 | "pattern":"\\Qpublic/js/cljs-runtime/reagent.impl.batching.js.map\\E" 53 | }, { 54 | "pattern":"\\Qpublic/js/cljs-runtime/reagent.ratom.js.map\\E" 55 | }, { 56 | "pattern":"\\Qpublic/js/cljs-runtime/shadow.cljs.devtools.client.shared.js.map\\E" 57 | }, { 58 | "pattern":"\\Qpublic/js/cljs-runtime/skylobby.js.js.map\\E" 59 | }, { 60 | "pattern":"\\Qpublic/js/cljs-runtime/taoensso.sente.js.map\\E" 61 | }, { 62 | "pattern":"\\Qpublic/js/cljs-runtime/taoensso.timbre.appenders.core.js.map\\E" 63 | }, { 64 | "pattern":"\\Qpublic/js/main.js\\E" 65 | }, { 66 | "pattern":"\\Qsevenzipjbinding-platforms.properties\\E" 67 | }, { 68 | "pattern":"\\Qskylobby.version\\E" 69 | }, { 70 | "pattern":"java.base:\\Qsun/net/www/content-types.properties\\E" 71 | }]}, 72 | "bundles":[] 73 | } 74 | -------------------------------------------------------------------------------- /graal/clj/skylobby/client/gloss.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.client.gloss 2 | (:require 3 | [gloss.core :as gloss] 4 | [gloss.io :as gio] 5 | [manifold.stream :as s] 6 | [skylobby.util :as u]) 7 | (:import 8 | (java.nio ByteBuffer))) 9 | 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | 14 | ; https://springrts.com/dl/LobbyProtocol/ProtocolDescription.html 15 | (defn protocol [encoding] 16 | (gloss/compile-frame 17 | (gloss/delimited-frame 18 | ["\n"] 19 | (gloss/string (or encoding u/default-client-encoding))))) 20 | 21 | (def client-status-protocol 22 | (gloss/compile-frame 23 | (gloss/bit-map 24 | :prefix 1 25 | :bot 1 26 | :access 1 27 | :rank 3 28 | :away 1 29 | :ingame 1))) 30 | 31 | 32 | (defn wrap-duplex-stream 33 | [protocol s] 34 | (let [out (s/stream)] 35 | (s/connect 36 | (s/map #(gio/encode protocol %) out) 37 | s) 38 | (s/splice 39 | out 40 | (gio/decode-stream s protocol)))) 41 | 42 | ; https://springrts.com/dl/LobbyProtocol/ProtocolDescription.html#MYBATTLESTATUS:client 43 | (def default-battle-status 44 | {:ready true 45 | :ally 0 46 | :handicap 0 47 | :mode false 48 | :sync 2 ; unsynced 49 | :id 0 50 | :side 0}) 51 | 52 | (def default-client-status-str "0") 53 | 54 | 55 | (def battle-status-protocol 56 | (gloss/compile-frame 57 | (gloss/bit-map 58 | :prefix 5 59 | :side 3 60 | :sync 2 61 | :pad 4 62 | :handicap 7 63 | :mode 1 64 | :ally 4 65 | :id 4 66 | :ready 1 67 | :suffix 1))) 68 | 69 | 70 | (defn decode-client-status [status-str] 71 | (dissoc 72 | (gio/decode client-status-protocol 73 | (ByteBuffer/wrap 74 | (.array 75 | (.put 76 | (ByteBuffer/allocate 1) 77 | (Byte/parseByte status-str))))) 78 | :prefix)) 79 | 80 | 81 | (def default-client-status 82 | (decode-client-status default-client-status-str)) 83 | 84 | 85 | (defn encode-client-status [client-status] 86 | (str 87 | (.get 88 | ^ByteBuffer 89 | (gio/to-byte-buffer 90 | (gio/encode client-status-protocol 91 | (assoc 92 | (merge default-client-status client-status) 93 | :prefix false))) 94 | 0))) 95 | 96 | (defn decode-battle-status [status-str] 97 | (dissoc 98 | (gio/decode battle-status-protocol 99 | (ByteBuffer/wrap 100 | (.array 101 | (.putInt 102 | (ByteBuffer/allocate (quot Integer/SIZE Byte/SIZE)) 103 | (Integer/parseInt status-str))))) 104 | :prefix :pad :suffix)) 105 | 106 | (defn encode-battle-status [battle-status] 107 | (str 108 | (.getInt 109 | ^ByteBuffer 110 | (gio/to-byte-buffer 111 | (gio/encode battle-status-protocol 112 | (assoc 113 | (merge default-battle-status battle-status) 114 | :prefix 0 115 | :pad 0 116 | :suffix 0)))))) 117 | -------------------------------------------------------------------------------- /src/clj/skylobby/fx/event/direct.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.fx.event.direct 2 | (:require 3 | [clojure.set :refer [rename-keys]] 4 | [skylobby.direct :as direct] 5 | [skylobby.direct.client :as direct.client] 6 | [taoensso.sente :as sente] 7 | [taoensso.timbre :as log])) 8 | 9 | 10 | (set! *warn-on-reflection* false) 11 | 12 | 13 | (defn add-methods 14 | [multifn state-atom] ; TODO need to move event handler out of spring-lobby ns 15 | (defmethod multifn ::host 16 | [params] 17 | (swap! state-atom assoc-in [:login-error :direct-host] nil) 18 | (future 19 | (try 20 | (direct/host-direct-connect state-atom (rename-keys params {:direct-connect-password :password 21 | :direct-connect-port :port 22 | :direct-connect-username :username})) 23 | (catch Exception e 24 | (log/error e "Error starting direct connect server"))))) 25 | (defmethod multifn ::join 26 | [{:keys [direct-connect-ip direct-connect-port direct-connect-protocol direct-connect-username]}] 27 | (swap! state-atom assoc-in [:login-error :direct-client] nil) 28 | (future 29 | (try 30 | (let [server-key {:server-type :direct 31 | :protocol :skylobby 32 | :host false 33 | :hostname direct-connect-ip 34 | :port direct-connect-port 35 | :username direct-connect-username} 36 | ;; Serializtion format, must use same val for client + server: 37 | packer :edn 38 | {:keys [ch-recv] :as client} 39 | (sente/make-channel-socket-client! 40 | "/chsk" 41 | nil ; ?csrf-token 42 | { 43 | :host direct-connect-ip 44 | :port direct-connect-port 45 | :protcol (or direct-connect-protocol :http) 46 | :type :ws 47 | :packer packer 48 | :wrap-recv-evs? false}) 49 | msg-handler (direct.client/event-msg-handler state-atom server-key) 50 | client-close-fn (sente/start-client-chsk-router! ch-recv msg-handler) 51 | client-close-fn (fn [] 52 | ;(send-fn [::direct.client/close]) 53 | (client-close-fn)) 54 | data {:battle {:battle-id :direct 55 | :users {}} 56 | :battles {:direct {}} 57 | :client client 58 | :client-close-fn client-close-fn 59 | :username direct-connect-username}] 60 | (swap! state-atom 61 | (fn [state] 62 | (-> state 63 | (assoc-in [:by-server server-key] data) 64 | (assoc :selected-server-tab (str server-key)))))) 65 | (catch Exception e 66 | (log/error e "Error joining direct connect")))))) 67 | -------------------------------------------------------------------------------- /cryogen/themes/lotus/css/_menu.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | #menucont { 4 | #menu-checkbox { 5 | display: none; 6 | } 7 | 8 | .menutitle { 9 | display: none; 10 | } 11 | 12 | .menu { 13 | margin: 0; 14 | padding: 0; 15 | 16 | li { 17 | width: 100%; 18 | list-style-type: none; 19 | padding: 0; 20 | 21 | a { 22 | color: #FFF; 23 | width: 100%; 24 | margin: 0; 25 | font-weight: 300; 26 | text-transform: uppercase; 27 | line-height: 22px; 28 | text-decoration: none; 29 | letter-spacing: 2px; 30 | } 31 | 32 | &:hover { 33 | background: rgba(0, 0, 0, 0.2); 34 | } 35 | 36 | &.active { 37 | background: rgba(0, 0, 0, 0.2); 38 | 39 | a { 40 | font-weight: 600; 41 | } 42 | } 43 | 44 | .menu-item-text { 45 | padding: $default-gutter/2 $default-gutter; 46 | width: 100%; 47 | } 48 | } 49 | } 50 | } 51 | 52 | // From the awesome animate.css 53 | @keyframes fadeIn { 54 | from { 55 | opacity: 0; 56 | } 57 | 58 | to { 59 | opacity: 1; 60 | } 61 | } 62 | 63 | @media screen and (max-width: $mobile-breakpoint) { 64 | 65 | #menucont { 66 | #menu-checkbox { 67 | &:checked ~ .menu { 68 | display: block; 69 | margin-bottom: .5em; 70 | } 71 | } 72 | 73 | .menutitle { 74 | display: flex; 75 | align-items: center; 76 | 77 | padding: $default-gutter/2 $default-gutter; 78 | cursor: pointer; 79 | text-transform: uppercase; 80 | opacity: $default-opacity; 81 | 82 | .icon-menu { 83 | // color: currentColor; 84 | fill: currentColor; 85 | 86 | // margin: 0 0 0 2px; 87 | // font-size: $default-font-size * 1.2; 88 | height: $default-font-size; 89 | width: $default-font-size; 90 | } 91 | 92 | .menu-text { 93 | margin: 0 0 0 .8em; 94 | font-size: $default-font-size; 95 | font-weight: $font-weight-heavy; 96 | letter-spacing: 2px; 97 | line-height: 1; 98 | } 99 | } 100 | 101 | .menu { 102 | animation-name: fadeIn; 103 | animation-duration: 1s; 104 | animation-fill-mode: both; 105 | 106 | display: none; 107 | 108 | li { 109 | &.active { 110 | background: transparent; 111 | 112 | a, a:hover { 113 | font-weight: 600; 114 | color: #FFF; 115 | } 116 | } 117 | 118 | a { 119 | width: 100%; 120 | display: inline-block; 121 | margin: 0; 122 | line-height: 23px; 123 | color: rgba(255, 255, 255, 0.6); 124 | font-weight: 300; 125 | text-transform: uppercase; 126 | } 127 | 128 | .menu-item-text { 129 | padding: $default-gutter/4 $default-gutter; 130 | } 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/clj/skylobby/spads_test.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.spads-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [skylobby.spads :as spads])) 5 | 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | 10 | (deftest parse-spads-message 11 | (is (= nil 12 | (spads/parse-spads-message ""))) 13 | (is (= {:spads-message-type :vote-progress, 14 | :spads-parsed ["Vote in progress: \"start\" [y:6/7, n:0/6] (29s remaining)" 15 | "start" 16 | "6" 17 | "7" 18 | "0" 19 | "6" 20 | "" 21 | "29s"], 22 | :text "* Vote in progress: \"start\" [y:6/7, n:0/6] (29s remaining)"} 23 | (spads/parse-spads-message "* Vote in progress: \"start\" [y:6/7, n:0/6] (29s remaining)"))) 24 | (is (= {:spads-message-type :vote-progress, 25 | :spads-parsed ["Vote in progress: \"rehost\" [y:4/6(9), n:1/6(9), votes:5/9] (9s remaining)" 26 | "rehost" 27 | "4" 28 | "6(9)" 29 | "1" 30 | "6(9)" 31 | ", votes:5/9" 32 | "9s"], 33 | :text "* Vote in progress: \"rehost\" [y:4/6(9), n:1/6(9), votes:5/9] (9s remaining)"} 34 | (spads/parse-spads-message "* Vote in progress: \"rehost\" [y:4/6(9), n:1/6(9), votes:5/9] (9s remaining)"))) 35 | (is (= {:spads-message-type :vote-progress, 36 | :spads-parsed ["Vote in progress: \"set map Altored Divide Bar Remake 1.55\" [y:4/5, n:2/4(5)] (25s remaining)" 37 | "set map Altored Divide Bar Remake 1.55" 38 | "4" 39 | "5" 40 | "2" 41 | "4(5)" 42 | "" 43 | "25s"], 44 | :text "* Vote in progress: \"set map Altored Divide Bar Remake 1.55\" [y:4/5, n:2/4(5)] (25s remaining)"} 45 | (spads/parse-spads-message "* Vote in progress: \"set map Altored Divide Bar Remake 1.55\" [y:4/5, n:2/4(5)] (25s remaining)"))) 46 | (is (= {:spads-message-type :cancelling-vote 47 | :spads-parsed ["* Cancelling \"set autobalance on\" vote (command executed directly by user)" 48 | "set autobalance on" 49 | "command executed directly by user"] 50 | :text "* [teh]cluster1[06] * Cancelling \"set autobalance on\" vote (command executed directly by user)"} 51 | (spads/parse-spads-message "* [teh]cluster1[06] * Cancelling \"set autobalance on\" vote (command executed directly by user)")))) 52 | 53 | 54 | (deftest parse-command-message 55 | (is (= {:command "y" 56 | :vote :y} 57 | (spads/parse-command-message "!y"))) 58 | (is (= {:command "vote y" 59 | :vote :y} 60 | (spads/parse-command-message "!vote y")))) 61 | 62 | 63 | (deftest parse-relay-message 64 | (is (= {:on-behalf-of "skynet"} 65 | (spads/parse-relay-message " test")))) 66 | -------------------------------------------------------------------------------- /cryogen/src/cryogen/server.clj: -------------------------------------------------------------------------------- 1 | (ns cryogen.server 2 | (:require 3 | [clojure.string :as string] 4 | [compojure.core :refer [GET defroutes]] 5 | [compojure.route :as route] 6 | [ring.util.response :refer [redirect file-response]] 7 | [ring.util.codec :refer [url-decode]] 8 | [ring.server.standalone :as ring-server] 9 | [cryogen-core.watcher :refer [start-watcher! start-watcher-for-changes!]] 10 | [cryogen-core.plugins :refer [load-plugins]] 11 | [cryogen-core.compiler :refer [compile-assets-timed]] 12 | [cryogen-core.config :refer [resolve-config]] 13 | [cryogen-core.io :refer [path]])) 14 | 15 | (defn init [fast?] 16 | (println "Init: fast compile enabled = " (boolean fast?)) 17 | (load-plugins) 18 | (compile-assets-timed) 19 | (let [ignored-files (-> (resolve-config) :ignored-files)] 20 | (run! 21 | #(if fast? 22 | (start-watcher-for-changes! % ignored-files compile-assets-timed {}) 23 | (start-watcher! % ignored-files compile-assets-timed)) 24 | ["content" "themes"]))) 25 | 26 | (defn wrap-subdirectories 27 | [handler] 28 | (fn [request] 29 | (let [{:keys [clean-urls blog-prefix public-dest]} (resolve-config) 30 | req-uri (.substring (url-decode (:uri request)) 1) 31 | res-path (if (or (.endsWith req-uri "/") 32 | (.endsWith req-uri ".html") 33 | (-> (string/split req-uri #"/") 34 | last 35 | (string/includes? ".") 36 | not)) 37 | (condp = clean-urls 38 | :trailing-slash (path req-uri "index.html") 39 | :no-trailing-slash (if (or (= req-uri "") 40 | (= req-uri "/") 41 | (= req-uri 42 | (if (string/blank? blog-prefix) 43 | blog-prefix 44 | (.substring blog-prefix 1)))) 45 | (path req-uri "index.html") 46 | (path (str req-uri ".html"))) 47 | :dirty (path (str req-uri ".html"))) 48 | req-uri)] 49 | (or (file-response res-path {:root public-dest}) 50 | (handler request))))) 51 | 52 | (defroutes routes 53 | (GET "/" [] (redirect (let [config (resolve-config)] 54 | (path (:blog-prefix config) 55 | (when (= (:clean-urls config) :dirty) 56 | "index.html"))))) 57 | (route/files "/") 58 | (route/not-found "Page not found")) 59 | 60 | (def handler (wrap-subdirectories routes)) 61 | 62 | (defn serve 63 | "Entrypoint for running via tools-deps (clojure)" 64 | [{:keys [fast] :as opts}] 65 | (ring-server/serve 66 | handler 67 | (merge 68 | {:init (partial init fast)} 69 | opts 70 | {:open-browser? false}))) 71 | 72 | (defn -main [& args] 73 | (serve {:port 3000 74 | :fast ((set args) "fast")})) 75 | -------------------------------------------------------------------------------- /dev/clj/graal/repl.clj: -------------------------------------------------------------------------------- 1 | (ns repl 2 | (:require 3 | [cider.nrepl] 4 | [clojure.string :as string] 5 | [clojure.tools.cli :as cli] 6 | hashp.core 7 | [io.aviso.repl] 8 | [nrepl.cmdline] 9 | [reply.main] 10 | [taoensso.timbre :as timbre] 11 | [taoensso.timbre.appenders.community.rotor :as rotor] 12 | user) 13 | (:import 14 | (java.awt Desktop Desktop$Action))) 15 | 16 | 17 | (set! *warn-on-reflection* true) 18 | 19 | 20 | (def ^:private dev-log-path "repl.log") 21 | 22 | 23 | (defn log-to-file [] 24 | (println "Setting up log to" dev-log-path) 25 | (timbre/merge-config! 26 | {:min-level :info 27 | :appenders 28 | {:rotor (rotor/rotor-appender 29 | {:path dev-log-path 30 | :max-size 10000000 31 | :backlog 5})}})) 32 | 33 | (defn disable-print-log [] 34 | (timbre/merge-config! 35 | {:appenders 36 | {:println {:enabled? false}}})) 37 | 38 | 39 | (def middleware 40 | [;'cider.nrepl/cider-middleware 41 | 'cider.nrepl/wrap-clojuredocs 42 | 'cider.nrepl/wrap-classpath 43 | 'cider.nrepl/wrap-complete 44 | 'cider.nrepl/wrap-debug 45 | 'cider.nrepl/wrap-format 46 | 'cider.nrepl/wrap-info 47 | 'cider.nrepl/wrap-inspect 48 | 'cider.nrepl/wrap-macroexpand 49 | 'cider.nrepl/wrap-ns 50 | 'cider.nrepl/wrap-spec 51 | 'cider.nrepl/wrap-profile 52 | 'cider.nrepl/wrap-resource 53 | ;'cider.nrepl/wrap-refresh TODO re-render 54 | 'cider.nrepl/wrap-stacktrace 55 | 'cider.nrepl/wrap-test 56 | 'cider.nrepl/wrap-trace 57 | 'cider.nrepl/wrap-out 58 | 'cider.nrepl/wrap-undef 59 | 'cider.nrepl/wrap-version]) 60 | 61 | (def cli-options []) 62 | 63 | (defn browse-url [url] 64 | (try 65 | (let [desktop (Desktop/getDesktop)] 66 | (println "Browsing" url) 67 | (if (.isSupported desktop Desktop$Action/BROWSE) 68 | (.browse desktop (java.net.URI. url)) 69 | (let [runtime (Runtime/getRuntime) 70 | command ["xdg-open" url] ; https://stackoverflow.com/a/5116553 71 | ^"[Ljava.lang.String;" cmdarray (into-array String command)] 72 | (.exec runtime cmdarray nil nil)))) 73 | (catch Exception e 74 | (println "Error browsing url" url e)))) 75 | 76 | 77 | (defn -main [& args] 78 | (let [parsed (cli/parse-opts args cli-options)] 79 | (log-to-file) 80 | (disable-print-log) 81 | (future 82 | (nrepl.cmdline/-main 83 | "--middleware" 84 | (str "[" (string/join "," middleware) "]") 85 | ;"--interactive" 86 | "--color")) 87 | (user/init parsed) 88 | (browse-url "http://localhost:12345") 89 | (loop [] 90 | (when 91 | (try 92 | (println "Trying to start REPLy") 93 | (reply.main/launch-nrepl 94 | {:attach (slurp ".nrepl-port") 95 | :caught io.aviso.repl/pretty-pst 96 | :color true}) 97 | false 98 | (catch Exception _e 99 | (println "Error connecting REPLy, sleeping") 100 | (Thread/sleep 500) 101 | true)) 102 | (recur))) 103 | (System/exit 0))) 104 | -------------------------------------------------------------------------------- /src/clj/skylobby/download/butler.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.download.butler 2 | (:require 3 | [cheshire.core :as json] 4 | [clj-http.client :as clj-http] 5 | [clojure.java.io :as io] 6 | [skylobby.fs :as fs] 7 | [skylobby.http :as http] 8 | [taoensso.timbre :as log])) 9 | 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | 14 | ; https://github.com/gajop/spring-launcher/blob/master/src/nextgen_downloader.js 15 | 16 | 17 | (def pkg-url "https://content.spring-launcher.com/pkg") 18 | (def fallback-url "https://spring-launcher.ams3.digitaloceanspaces.com/pkg") 19 | 20 | (def api-backend "https://backend.spring-launcher.com/api") 21 | 22 | 23 | (defn pkg-dir [root] 24 | (fs/file root "pkgs")) 25 | 26 | 27 | (defn version-from-spring-name [spring-name] 28 | (->> (clj-http/post 29 | (str api-backend "/versions/from-springname/") 30 | {:body (json/generate-string {:springName spring-name}) 31 | :content-type :json 32 | :accept :json 33 | :as :json}) 34 | :body)) 35 | 36 | ; {:nextgenName "beyond-all-reason/Beyond-All-Reason@main:27"} 37 | 38 | 39 | (defn butler-download-url 40 | ([] 41 | (let [platform (if (fs/windows?) "windows-amd64" 42 | "linux-amd64")] 43 | (butler-download-url platform))) 44 | ([platform] 45 | (str "https://broth.itch.ovh/butler/" platform "/LATEST/archive/default"))) 46 | 47 | (defn get-butler [state-atom] 48 | (let [dest-file (fs/file (fs/download-dir) "butler.zip")] 49 | @(http/download-file state-atom (butler-download-url) dest-file) 50 | (fs/extract-7z-fast dest-file (fs/file (fs/download-dir) "butler")))) 51 | 52 | 53 | (defn download-with-butler [url] 54 | (future 55 | (try 56 | (let [butler-file (fs/file (fs/download-dir) "butler" (fs/executable "butler")) 57 | _ (fs/set-executable butler-file) 58 | tmp-dest (fs/canonical-path (fs/file (fs/download-dir) "butler-tmp")) 59 | command [(fs/canonical-path butler-file) 60 | "-j" "-v" "dl" url tmp-dest] 61 | runtime (Runtime/getRuntime)] 62 | (log/info "Running '" command "'") 63 | (let [^"[Ljava.lang.String;" cmdarray (into-array String command) 64 | ^"[Ljava.lang.String;" envp nil 65 | ^java.lang.Process process (.exec runtime cmdarray envp ^java.io.File (fs/app-root))] 66 | (future 67 | (with-open [^java.io.BufferedReader reader (io/reader (.getInputStream process))] 68 | (loop [] 69 | (if-let [line (.readLine reader)] 70 | (do 71 | (log/info "(butler" url "out)" line) 72 | (recur)) 73 | (log/info "butler" url "stdout stream closed"))))) 74 | (future 75 | (with-open [^java.io.BufferedReader reader (io/reader (.getErrorStream process))] 76 | (loop [] 77 | (if-let [line (.readLine reader)] 78 | (do 79 | (log/info "(butler" url "err)" line) 80 | (recur)) 81 | (log/info "butler" url "stderr stream closed"))))) 82 | (.waitFor process))) 83 | (catch Exception e 84 | (log/error e "Error downloading" url))))) 85 | -------------------------------------------------------------------------------- /graal/clj/skylobby/config.clj: -------------------------------------------------------------------------------- 1 | (ns skylobby.config 2 | (:require 3 | [clojure.edn :as edn] 4 | [clojure.java.io :as io] 5 | [clojure.pprint :refer [pprint]] 6 | [skylobby.fs :as fs] 7 | [taoensso.timbre :as log]) 8 | (:import 9 | (java.io File) 10 | (java.net URL))) 11 | 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | 16 | ; https://github.com/clojure/clojure/blob/28efe345d5e995dc152a0286fb0be81443a0d9ac/src/clj/clojure/instant.clj#L274-L279 17 | (defn- read-file-tag [cs] 18 | (io/file cs)) 19 | (defn- read-url-tag [spec] 20 | (URL. spec)) 21 | 22 | 23 | ; https://github.com/clojure/clojure/blob/0754746f476c4ddf6a6b699d9547830b2fdad17c/src/clj/clojure/core.clj#L7755-L7761 24 | (def custom-readers 25 | {'spring-lobby/java.io.File #'skylobby.config/read-file-tag 26 | 'spring-lobby/java.net.URL #'skylobby.config/read-url-tag}) 27 | 28 | 29 | ; https://stackoverflow.com/a/23592006/984393 30 | (defmethod print-method File [f ^java.io.Writer w] 31 | (.write w (str "#spring-lobby/java.io.File " (pr-str (fs/canonical-path f))))) 32 | (defmethod print-method URL [url ^java.io.Writer w] 33 | (.write w (str "#spring-lobby/java.net.URL " (pr-str (str url))))) 34 | 35 | 36 | (defn slurp-edn 37 | "Returns data loaded from a .edn file in this application's root directory." 38 | [edn-filename] 39 | (try 40 | (let [config-file (fs/config-file edn-filename)] 41 | (log/info "Slurping config edn from" config-file) 42 | (when (fs/exists? config-file) 43 | (let [data (->> config-file slurp (edn/read-string {:readers custom-readers}))] 44 | (if (map? data) 45 | (do 46 | (try 47 | (log/info "Backing up config file that we could parse") 48 | (fs/copy config-file (fs/config-file (str edn-filename ".known-good"))) 49 | (catch Exception e 50 | (log/error e "Error backing up config file"))) 51 | data) 52 | (do 53 | (log/warn "Config file data from" edn-filename "is not a map") 54 | {}))))) 55 | (catch Exception e 56 | (log/warn e "Exception loading app edn file" edn-filename) 57 | (try 58 | (log/info "Copying bad config file for debug") 59 | (fs/copy (fs/config-file edn-filename) (fs/config-file (str edn-filename ".debug"))) 60 | (catch Exception e 61 | (log/warn e "Exception copying bad edn file" edn-filename))) 62 | {}))) 63 | 64 | (defn spit-edn 65 | "Writes the given data as edn to the given file in the application directory." 66 | ([data filename] 67 | (spit-edn data filename nil)) 68 | ([data filename {:keys [pretty]}] 69 | (let [output (if pretty 70 | (with-out-str (pprint (if (map? data) 71 | (into (sorted-map) data) 72 | data))) 73 | (pr-str data)) 74 | parsable (try 75 | (edn/read-string {:readers custom-readers} output) 76 | true 77 | (catch Exception e 78 | (log/error e "Config EDN for" filename "does not parse, keeping old file"))) 79 | file (fs/config-file (if parsable 80 | filename 81 | (str filename ".bad")))] 82 | (fs/make-parent-dirs file) 83 | (log/info "Spitting edn to" file) 84 | (spit file output)))) 85 | -------------------------------------------------------------------------------- /cryogen/themes/blue/css/screen.css: -------------------------------------------------------------------------------- 1 | h1, h2, h3, h4, h5, h6 { 2 | font-family: 'Alegreya'; 3 | } 4 | 5 | body { 6 | color: #333; 7 | background-color: #f2f2f2; 8 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 9 | font-size: 16px; 10 | } 11 | 12 | .container { 13 | max-width: 1000px; 14 | } 15 | 16 | .right { 17 | float: right; 18 | text-align: right; 19 | } 20 | 21 | .navbar { 22 | border-radius: 0; 23 | box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.3); 24 | } 25 | 26 | .navbar-default { 27 | background-color: #428bca; 28 | border: none; 29 | } 30 | 31 | .navbar-default .navbar-brand { 32 | color: #fff; 33 | font-family: 'Alegreya'; 34 | } 35 | 36 | .navbar-default .navbar-brand:hover { 37 | color: #fff; 38 | } 39 | 40 | .navbar-default .navbar-nav li a { 41 | color: #fff; 42 | } 43 | 44 | .navbar-default .navbar-nav li a:hover { 45 | color: #fff; 46 | background-color: #3d80ba; 47 | } 48 | 49 | .navbar-default .navbar-nav .active a { 50 | color: #fff; 51 | background-color: #3d80ba; 52 | } 53 | 54 | .navbar-default .navbar-toggle:hover{ 55 | background-color: #3d80ba; 56 | } 57 | 58 | .navbar-default .navbar-toggle .icon-bar { 59 | background-color: #fff; 60 | } 61 | 62 | #sidebar { 63 | margin-left: 15px; 64 | margin-top: 50px; 65 | } 66 | 67 | #content { 68 | background-color: #fff; 69 | border-radius: 3px; 70 | box-shadow: 0 0 0 0,0 6px 12px rgba(34,34,34,0.1); 71 | } 72 | 73 | #content img { 74 | max-width: 100%; 75 | height: auto; 76 | } 77 | 78 | footer { 79 | font-size: 14px; 80 | text-align: center; 81 | padding-top: 75px; 82 | padding-bottom: 30px; 83 | } 84 | 85 | blockquote footer { 86 | text-align: left; 87 | padding-top: 0px; 88 | padding-bottom: 0px; 89 | } 90 | 91 | #post-tags { 92 | margin-top: 30px; 93 | } 94 | 95 | #prev-next { 96 | padding: 15px 0; 97 | } 98 | 99 | .post-header { 100 | margin-bottom: 20px; 101 | } 102 | .post-header h2 { 103 | font-size: 32px; 104 | } 105 | 106 | #post-meta { 107 | font-size: 14px; 108 | color: rgba(0,0,0,0.4) 109 | } 110 | 111 | #page-header { 112 | border-bottom: 1px solid #dbdbdb; 113 | margin-bottom: 20px; 114 | } 115 | #page-header h2 { 116 | font-size: 32px; 117 | } 118 | 119 | pre { 120 | overflow-x: auto; 121 | } 122 | pre code { 123 | display: block; 124 | padding: 0.5em; 125 | overflow-wrap: normal; 126 | white-space: pre; 127 | } 128 | 129 | code { 130 | color: #428bca; 131 | } 132 | 133 | pre, code, .hljs { 134 | background-color: #f7f9fd; 135 | } 136 | 137 | @media (min-width: 768px) { 138 | .navbar { 139 | min-height: 70px; 140 | } 141 | .navbar-nav>li>a { 142 | padding: 30px 20px; 143 | } 144 | .navbar-default .navbar-brand { 145 | font-size: 36px; 146 | padding: 25px 15px; 147 | } 148 | #content{ 149 | margin-top: 30px; 150 | padding: 30px 40px; 151 | } 152 | } 153 | 154 | @media (max-width: 767px) { 155 | body{ 156 | font-size: 14px; 157 | } 158 | .navbar-default .navbar-brand { 159 | font-size: 30px; 160 | } 161 | #content{ 162 | padding: 15px; 163 | } 164 | #post-meta .right { 165 | float:left; 166 | text-align: left; 167 | } 168 | } 169 | --------------------------------------------------------------------------------