430 Commits

Author SHA1 Message Date
Renovate Bot
b0c427b086 chore(deps): update dependency unified to v11.0.5 2024-09-09 19:30:38 +00:00
d9b07c3e1b API: oops wrong Sentry depend
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m31s
2024-04-27 03:21:22 -04:00
80df977047 Merge remote-tracking branch 'origin/master'
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 31s
2024-04-27 03:19:50 -04:00
526d68763f API: Capture unhandled exceptions with Sentry 2024-04-27 03:19:43 -04:00
72a2a4bb3a Merge pull request 'fix(deps): update dependency @sentry/nextjs to v7.112.2' (#25) from renovate/sentry-javascript-monorepo into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m27s
Reviewed-on: #25
2024-04-27 00:16:38 -07:00
855b040e8f API: Add Sentry
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m48s
2024-04-27 03:08:22 -04:00
Renovate Bot
31f5ca464d fix(deps): update dependency @sentry/nextjs to v7.112.2 2024-04-27 07:04:13 +00:00
e0c92ca95a Add Sentry (:
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m18s
2024-04-27 02:54:59 -04:00
57dc61f745 Frontend: Make the Mojang status page more responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m13s
2024-04-26 22:50:55 -04:00
b06cbdddda Frontend: Fix footer links not working 2024-04-26 22:44:58 -04:00
5292da63b5 Frontend: Make the server search result more responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m23s
2024-04-26 21:15:13 -04:00
39407ac8b0 Frontend: Show the country of a server in the result 2024-04-26 21:12:12 -04:00
2e319488a9 Frontend: Make the Mojang status page more responsive 2024-04-26 20:58:31 -04:00
490b04ba61 Frontend: Make the navbar more responsive 2024-04-26 20:55:49 -04:00
531c18626a Frontend: Improve mobile support 2024-04-26 20:53:25 -04:00
1570ade4c0 Fix rel including undefined 2024-04-26 20:12:37 -04:00
4675708a8b oops, fix new tab prop not working properly 2024-04-26 20:03:54 -04:00
295081431b Remove weird white spots in the dark wool transition 2024-04-26 20:02:57 -04:00
01743d2499 Custom link component & license header 2024-04-26 19:56:07 -04:00
7e7ac42b8e Remove .fleet, oops
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 32s
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 56s
2024-04-26 19:30:04 -04:00
08acdaef03 Java SDK: Add DNS records to MC servers
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 29s
2024-04-26 17:22:20 -04:00
ad215b7b72 Update .gitignore files
All checks were successful
Publish JS SDK / docker (push) Successful in 20s
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m23s
2024-04-26 17:01:38 -04:00
9d594a8c37 JS SDK: Add Geo and query data to servers
Some checks failed
Publish JS SDK / docker (push) Failing after 18s
2024-04-26 17:00:40 -04:00
856e445182 Bump JS SDK ver 2024-04-26 17:00:22 -04:00
ae94af5980 Import cleanup
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m42s
2024-04-26 16:41:54 -04:00
00a9dc0ce8 Prevent logins 2024-04-26 15:13:09 -04:00
fce1fa24b1 Update README.md 2024-04-26 15:08:41 -04:00
a2615ac96a Fix error and update to Java 17 2024-04-26 15:08:23 -04:00
1962110dbd Spigot -> Velocity 2024-04-26 14:45:52 -04:00
758a3bf0aa Merge pull request 'fix(deps): update dependency remote-mdx to ^0.0.7' (#22) from renovate/remote-mdx-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m36s
Reviewed-on: #22
2024-04-26 11:03:56 -07:00
7abd00a692 Merge pull request 'chore(deps): update dependency org.apache.maven.plugins:maven-assembly-plugin to v3.7.1' (#24) from renovate/org.apache.maven.plugins-maven-assembly-plugin-3.x into master
Reviewed-on: #24
2024-04-26 11:03:50 -07:00
e4c92cc0e0 Delete .gitea/workflows/deploy-demo-plugin.yml 2024-04-26 11:03:11 -07:00
Renovate Bot
f9614bacf7 chore(deps): update dependency org.apache.maven.plugins:maven-assembly-plugin to v3.7.1 2024-04-26 18:02:52 +00:00
Renovate Bot
debc8a43bf fix(deps): update dependency remote-mdx to ^0.0.7 2024-04-26 18:02:49 +00:00
6d3418e16c Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 33s
2024-04-26 11:00:21 -07:00
bb9a3044a7 Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 21s
2024-04-26 10:58:37 -07:00
d7076c6fa6 Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 22s
2024-04-26 10:57:48 -07:00
f79d42a45e Build demo plugin jar to /jars
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 35s
2024-04-26 13:55:59 -04:00
58045b19e1 Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 32s
2024-04-26 10:40:58 -07:00
0028f27c20 Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 33s
2024-04-26 10:39:50 -07:00
83aee00a72 Merge pull request 'chore(deps): update rexlmanu/pterodactyl-upload-action action to v2.4' (#21) from renovate/rexlmanu-pterodactyl-upload-action-2.x into master
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 1m36s
Reviewed-on: #21
2024-04-26 10:36:01 -07:00
fed80a0aa7 Merge pull request 'fix(deps): update dependency lucide-react to ^0.376.0' (#23) from renovate/lucide-react-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m30s
Reviewed-on: #23
2024-04-26 10:35:36 -07:00
Renovate Bot
f70503f80b fix(deps): update dependency lucide-react to ^0.376.0 2024-04-26 17:03:24 +00:00
Renovate Bot
a615239386 chore(deps): update rexlmanu/pterodactyl-upload-action action to v2.4 2024-04-25 22:07:36 +00:00
0cf97926ea Update demo plugin workflow
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 39s
2024-04-25 17:44:36 -04:00
acb91cc829 Update demo plugin workflow
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 39s
2024-04-25 17:42:53 -04:00
790beafce4 Update demo plugin workflow
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 35s
2024-04-25 17:21:29 -04:00
211bfd0d2b Update demo plugin workflow
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 37s
2024-04-25 17:17:25 -04:00
bb416a8734 Update demo plugin workflow
Some checks failed
Deploy Demo Plugin / docker (17, 3.8.5) (push) Failing after 6s
2024-04-25 17:16:10 -04:00
2be04ec1d6 Try absolute dir for the remote
Some checks failed
Deploy Demo Plugin / docker (17, 3.8.5) (push) Failing after 39s
2024-04-25 17:07:18 -04:00
bbeeed8f84 Merge
Some checks failed
Deploy Demo Plugin / docker (17, 3.8.5) (push) Failing after 58s
2024-04-25 17:04:53 -04:00
4e2e0f328c Update demo plugin workflow 2024-04-25 17:03:40 -04:00
16b295a784 Delete .gitea/workflows/deploy-demo-plugin.yml 2024-04-24 17:46:43 -07:00
dbd6e825f1 Update .gitea/workflows/deploy-demo-plugin.yml
Some checks failed
Deploy Demo Plugin / docker (17, 3.8.5) (push) Failing after 1m1s
2024-04-24 17:25:00 -07:00
a43c40d88f Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 1m19s
2024-04-24 17:22:46 -07:00
2783396446 Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 55s
2024-04-24 17:12:08 -07:00
494266d2ea Update .gitea/workflows/deploy-demo-plugin.yml
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 1m19s
2024-04-24 17:03:52 -07:00
4069932f0d Push to FTP for demo plugin
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 58s
2024-04-24 19:43:08 -04:00
1a07fbedb6 Build project without version 2024-04-24 19:40:17 -04:00
0655c0b651 Update MOTD msgs 2024-04-24 19:39:18 -04:00
2494d9ab45 Add demo plugin publish workflow
All checks were successful
Deploy Demo Plugin / docker (17, 3.8.5) (push) Successful in 38s
2024-04-24 19:37:19 -04:00
8b332ab400 Make Maven shut up
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 59s
2024-04-24 19:22:00 -04:00
477ac04d91 Fix missing main class in build jar
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 1m3s
2024-04-24 19:21:17 -04:00
104dc11e35 err logging
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 55s
2024-04-24 19:16:04 -04:00
4fbc977034 Fix output jar name
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 1m4s
2024-04-24 19:12:47 -04:00
409b273edc Don't use this
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 1m4s
2024-04-24 19:10:50 -04:00
f943ca54fb Add deploy bot workflow
Some checks failed
Deploy Discord Bot / docker (17, 3.8.5) (push) Failing after 38s
2024-04-24 19:05:59 -04:00
a683b7225e Add Dockerfile 2024-04-24 19:04:53 -04:00
6c8d486052 Add the server lookup command 2024-04-24 19:01:07 -04:00
1474598a1a Update player embed 2024-04-24 18:09:33 -04:00
0ba927b7d1 Discord player lookups 2024-04-24 18:01:08 -04:00
e6a12c3675 Discord bot project
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 34s
2024-04-24 16:05:39 -04:00
f9bd49c4bb Fallback to null for addr not found in MaxMind
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m43s
2024-04-24 15:42:44 -04:00
43d1ea9ddd Fix server Geo data not being fetched for numeric ips 2024-04-24 15:42:28 -04:00
77e5daa375 Add #getMojangStatus to the Java SDK
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 33s
2024-04-24 15:19:52 -04:00
f08ff7602f Merge pull request 'chore(deps): update nextjs monorepo to v14.2.3' (#20) from renovate/nextjs-monorepo into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m25s
Reviewed-on: #20
2024-04-24 11:48:14 -07:00
d929d948ab Add server responses
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 30s
2024-04-24 14:47:47 -04:00
10669f1b4a Add Minecraft server lookups to the Java SDK 2024-04-24 14:29:37 -04:00
Renovate Bot
ffa65b8ee4 chore(deps): update nextjs monorepo to v14.2.3 2024-04-24 18:03:09 +00:00
cf5ee56b64 Complete the player response 2024-04-24 13:56:32 -04:00
6b38e9f301 Merge pull request 'fix(deps): update dependency lucide-react to ^0.373.0' (#19) from renovate/lucide-react-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m33s
Reviewed-on: #19
2024-04-24 10:31:35 -07:00
cd7c178250 Merge pull request 'chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.3' (#18) from renovate/org.apache.maven.plugins-maven-shade-plugin-3.x into master
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 45s
Reviewed-on: #18
2024-04-24 10:31:29 -07:00
Renovate Bot
72bf0cdcd0 fix(deps): update dependency lucide-react to ^0.373.0 2024-04-24 09:02:51 +00:00
Renovate Bot
fecd1bfade chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.3 2024-04-23 20:02:45 +00:00
d35443db33 Update demo server version name 2024-04-23 11:56:55 -04:00
40526cd0f8 Merge pull request 'fix(deps): update dependency com.comphenix.protocol:protocollib to v5' (#17) from renovate/com.comphenix.protocol-protocollib-5.x into master
Reviewed-on: #17
2024-04-23 08:40:14 -07:00
Renovate Bot
2f94355398 fix(deps): update dependency com.comphenix.protocol:protocollib to v5 2024-04-23 08:02:28 +00:00
51dcc75a86 Update .gitea/workflows/publish-java-sdk.yml
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 25s
2024-04-23 00:57:43 -07:00
d9c09a1aae Javadoc stylesheet
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 24s
2024-04-23 03:56:38 -04:00
0a530413c4 Update pom.xml
All checks were successful
Publish Java SDK / docker (17, 3.8.5) (push) Successful in 32s
2024-04-23 03:55:56 -04:00
9dc5ad029b Merge remote-tracking branch 'origin/master'
Some checks failed
Publish Java SDK / docker (17, 3.8.5) (push) Failing after 46s
2024-04-23 03:53:39 -04:00
ab7f6162fb Update README.md 2024-04-23 00:53:36 -07:00
743f789a7f Merge remote-tracking branch 'origin/master' 2024-04-23 03:43:41 -04:00
7b1745822d Add Java SDK workflow 2024-04-23 03:43:20 -04:00
e21db5c876 Merge pull request 'fix(deps): update dependency remote-mdx to ^0.0.5' (#15) from renovate/remote-mdx-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m10s
Reviewed-on: #15
2024-04-23 00:31:32 -07:00
8e43044877 Java SDK base 2024-04-23 03:29:09 -04:00
Renovate Bot
a4fad0af0a fix(deps): update dependency remote-mdx to ^0.0.5 2024-04-23 07:04:04 +00:00
88705f363b Fix MaxMind NPE
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 26s
2024-04-23 01:36:33 -04:00
db88533eed me.braydon -> cc.restfulmc 2024-04-23 01:34:43 -04:00
68c932c39b Move tests
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Has been cancelled
2024-04-23 01:33:10 -04:00
0f9bea22f5 me.braydon -> cc.restfulmc
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Has been cancelled
2024-04-23 01:32:42 -04:00
10ab624d63 Add docker-compose.yml 2024-04-22 22:26:55 -07:00
53f66cd7e4 Merge pull request 'chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.2' (#13) from renovate/org.apache.maven.plugins-maven-shade-plugin-3.x into master
Reviewed-on: #13
2024-04-22 22:16:42 -07:00
a22c6c16be Merge pull request 'chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.13.0' (#14) from renovate/org.apache.maven.plugins-maven-compiler-plugin-3.x into master
Reviewed-on: #14
2024-04-22 22:16:38 -07:00
4b55674a98 Fallback to default icon for any errors whilst getting a server's favicon
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m41s
2024-04-23 01:09:39 -04:00
Renovate Bot
b359200a18 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.13.0 2024-04-23 05:03:09 +00:00
Renovate Bot
9ec0c95e6d chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.5.2 2024-04-23 05:03:07 +00:00
8456829c30 Add README.md 2024-04-23 01:00:35 -04:00
cc67bfd054 Add demo Spigot plugin 2024-04-23 00:58:35 -04:00
2261bde6cb DNS records shouldnt be NonNull
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m45s
2024-04-22 23:44:19 -04:00
52ff61c554 Merge remote-tracking branch 'origin/master'
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m34s
2024-04-22 23:14:34 -04:00
253c889b90 ignore default license 2024-04-22 23:14:26 -04:00
549e46c978 Update .gitea/workflows/deploy-api.yml
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 1m35s
2024-04-22 20:12:37 -07:00
023395ea69 Merge remote-tracking branch 'origin/master'
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Has been cancelled
2024-04-22 23:12:11 -04:00
90aea4a9cc yes 2024-04-22 23:12:02 -04:00
8f297aef26 Update .gitea/workflows/deploy-api.yml
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 22s
2024-04-22 20:10:50 -07:00
7b602a0f21 Fix test errs
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 26s
2024-04-22 23:07:46 -04:00
fb14eef179 Fix test check?
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 1m32s
2024-04-22 23:01:31 -04:00
81886fa097 Don't download MaxMind dbs in tests
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 23s
2024-04-22 22:59:04 -04:00
f3a57dc8d9 Geo location lookup
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 46s
2024-04-22 22:47:49 -04:00
1392d82480 Cleanup tasks 2024-04-22 21:48:31 -04:00
02695ce7a2 Add demo server
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m22s
2024-04-22 21:17:26 -04:00
5c32804114 Re-enable server cache
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m45s
2024-04-22 20:57:56 -04:00
1a4929d8b5 Identify server software if querying is enabled 2024-04-22 20:57:33 -04:00
c689434ec4 Add Java server querying! (:
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Has been cancelled
2024-04-22 20:49:35 -04:00
345e1532a4 Prevent Java versions from including unknown
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m44s
2024-04-22 15:36:45 -04:00
4d575fdf2e Add another Mojang server 2024-04-22 15:35:51 -04:00
867b9b263a docs view on git tooltip
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m6s
2024-04-22 02:30:42 -04:00
491295f5bd Stop using vanilla html <img />
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m8s
2024-04-22 02:27:13 -04:00
2eb9286dcf Run the footer on the client
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m8s
2024-04-22 02:06:05 -04:00
2ef6f6e4e2 Add copyright to the footer 2024-04-22 02:05:33 -04:00
eda3045cf4 Mojang page tooltips 2024-04-22 01:47:03 -04:00
8b31904095 Add feedback to search buttons
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m7s
2024-04-22 01:40:03 -04:00
58a97708fd Tooltips in a few more places
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 30s
2024-04-22 01:12:56 -04:00
3c912a4591 Add recommendation tooltips 2024-04-22 01:04:06 -04:00
f021eb4809 Add simple tooltip component 2024-04-22 01:03:52 -04:00
5d2136ce5a Disallow label selection 2024-04-22 00:49:20 -04:00
a33c1681bd Add server recommendations
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m25s
2024-04-22 00:46:47 -04:00
e50e6553c8 Fix the position of the server lookup form
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m32s
2024-04-22 00:04:47 -04:00
097a5d74fe Do some refactoring
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m10s
2024-04-21 23:58:56 -04:00
fb0b82edd3 Add code dialogs 2024-04-21 23:55:38 -04:00
060e991741 Unused code 2024-04-21 23:16:05 -04:00
8642740f90 Fix imports, and add a code highlighter component 2024-04-21 23:11:15 -04:00
17094d7cec Update doc titles 2024-04-21 22:55:15 -04:00
fe34001790 Custom doc embeds
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m8s
2024-04-21 22:24:42 -04:00
8ea4c20137 Make docs responsive on mobile
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 11m36s
2024-04-21 22:03:53 -04:00
21354da41e Slight animation change to the docs search dialog 2024-04-21 21:27:44 -04:00
183c61cfdc Show lang icons
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m7s
2024-04-21 21:23:26 -04:00
538bfc4d75 Make the lang text darker 2024-04-21 21:11:18 -04:00
132f45eec8 Show lang used in code blocks
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m57s
2024-04-21 21:07:59 -04:00
577851c69b Add code syntax highlighting
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m21s
2024-04-21 20:55:42 -04:00
986f3b8f02 Cleanup 2024-04-21 20:26:29 -04:00
b8cecee57e Make the KBD look better
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m2s
2024-04-21 20:20:30 -04:00
c469b2d5cf Add a keybind for the docs search dialog
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 20:15:41 -04:00
280df436d2 Fix sidebar padding 2024-04-21 20:04:26 -04:00
6963085295 Update search ui
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m52s
2024-04-21 19:51:30 -04:00
349747d555 Add searching to the docs
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 2m52s
2024-04-21 18:14:16 -04:00
0413e0f448 Indent lists
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 14:46:49 -04:00
6f36c8bc4e Custom error page
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m1s
2024-04-21 13:51:31 -04:00
335ed10a53 Fix the docs sidebar active link status not properly working
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m1s
2024-04-21 13:45:26 -04:00
3ea7e8c70e Space out markdown headings more
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m2s
2024-04-21 13:41:22 -04:00
e1e02ae0f4 Docs sidebar
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m2s
2024-04-21 13:35:12 -04:00
8f6f5094bc Make docs more responsive 2024-04-21 13:31:12 -04:00
75fabe648e Make the nav logo slightly smaller
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 12:40:30 -04:00
ee6a20054c Make the footer more mobile responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 12:35:37 -04:00
e8000a1fbc Make the nav and hero more mobile responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m1s
2024-04-21 12:33:30 -04:00
e7bdfe1f65 Make the docs page more responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m12s
2024-04-21 12:18:34 -04:00
0f77b9aa68 View on git button for docs 2024-04-21 12:14:03 -04:00
9769149689 UID is used
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m0s
2024-04-21 11:10:30 -04:00
a31dcc7618 Dockerfile yarn -> bun
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 50s
2024-04-21 11:08:52 -04:00
42f9e7090f fix?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m58s
2024-04-21 02:20:07 -04:00
f65cc4e804 fix?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m5s
2024-04-21 02:03:43 -04:00
0a4f00866d pls
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m52s
2024-04-21 01:55:55 -04:00
47d2982666 use vanilla table
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m4s
2024-04-21 01:46:34 -04:00
14122b0267 Update home.md
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m5s
2024-04-21 01:44:06 -04:00
53c19d8e48 will this fix table parsing?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m51s
2024-04-21 01:38:36 -04:00
0dac73bff4 add lock
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m49s
2024-04-21 01:27:23 -04:00
62d89def61 fix table parse err?
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
2024-04-21 01:26:56 -04:00
4cd88be2ad now?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 01:19:15 -04:00
4372832196 Force static doc pages
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m5s
2024-04-21 01:17:52 -04:00
ec332842e7 fix?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 01:16:15 -04:00
0ccfd5ea22 Cleanup the table
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m2s
2024-04-21 01:14:10 -04:00
0d77a2e219 undefined check for table
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-21 01:10:17 -04:00
e1acb73805 fix spacing
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m5s
2024-04-21 01:07:27 -04:00
e9f30885b0 Begin on the docs page design
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m15s
2024-04-21 01:01:52 -04:00
534e67b6f0 Fix markdown highlighting 2024-04-20 21:42:31 -04:00
5e67ce1f34 Make markdown work recursively
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 2m53s
2024-04-20 18:07:13 -04:00
a665d5d0ba Update ping icon size
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 59s
2024-04-20 15:27:35 -04:00
74392b6288 .
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 11s
2024-04-20 15:20:32 -04:00
7afd38bcc2 Update server page theme color
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
2024-04-20 15:20:21 -04:00
9e60a7d8c5 Update server page embed color
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m49s
2024-04-20 14:26:29 -04:00
b1d2e895ee Cleanup server page 2024-04-20 14:19:26 -04:00
c286ae4a21 Cleanup player page 2024-04-20 14:18:52 -04:00
b26718df8b Player embed colors
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m5s
2024-04-20 13:59:40 -04:00
bc769651c3 oops, forgot the font
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 59s
2024-04-20 13:44:22 -04:00
f308456a4a Update media
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 19s
2024-04-20 13:43:14 -04:00
8bda711215 Update the style of the server result 2024-04-20 13:34:47 -04:00
9f2dc1d4ed Update Minecraft font 2024-04-20 13:04:34 -04:00
1a96d6a522 Update server background image
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m10s
2024-04-20 12:46:03 -04:00
791f88e11b oops lol
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 36s
2024-04-20 01:58:13 -04:00
e4176a4dbe compile docs?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 57s
2024-04-20 01:55:54 -04:00
e4d05f5104 .md file support
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 33s
2024-04-20 01:47:53 -04:00
02b1eea17b Update markdown docs path
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
2024-04-20 01:46:04 -04:00
e92622c023 sharp! 2024-04-20 01:45:55 -04:00
a8cf72e981 Docs page
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m40s
2024-04-20 01:33:33 -04:00
ab05ad8a3e Use the Minecraft font for the server response 2024-04-19 18:25:53 -04:00
9a78a6988b Disablt auto complete in field inputs 2024-04-19 18:18:27 -04:00
98adb3d22d oops, fix build err
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m0s
2024-04-19 11:36:31 -04:00
d46fd443d4 Embed color test
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 29s
2024-04-19 11:33:29 -04:00
4af63c65a0 Add a pull request template 2024-04-19 11:09:17 -04:00
8cdd990938 Move issue template to root dir
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m4s
2024-04-19 11:03:17 -04:00
408ad7d7b7 Add issue template 2024-04-19 10:57:19 -04:00
c6f118d629 yes
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m1s
2024-04-19 10:55:08 -04:00
1244b6d142 Cleanup and center the links in the navbar
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
2024-04-19 10:54:56 -04:00
4bccec1f5d Update API/src/main/resources/public/index.html
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 27s
2024-04-19 07:15:03 -07:00
2f1a16f3f2 Use the same theme color as the frontend
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Has been cancelled
2024-04-19 07:14:54 -07:00
c11383107c Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Publish JS SDK / docker (push) Failing after 20s
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 35s
2024-04-19 10:11:33 -04:00
f5c593a95f Update JS SDK workflow 2024-04-19 10:11:25 -04:00
b3db5c5081 Update Frontend workflow 2024-04-19 10:08:55 -04:00
c758314d97 Update API/README.md 2024-04-19 07:06:28 -07:00
0d9c5cbfc6 Update API/README.md 2024-04-19 07:06:15 -07:00
2edbacd55c Update API workflow
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m45s
2024-04-19 10:03:12 -04:00
3194acfc83 Merge pull request 'fix(deps): update dependency lucide-react to ^0.372.0' (#11) from renovate/lucide-react-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m38s
Reviewed-on: #11
2024-04-19 06:55:05 -07:00
Renovate Bot
0d5e185ffd fix(deps): update dependency lucide-react to ^0.372.0 2024-04-19 09:02:21 +00:00
6fa05990c6 Update some errors
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m0s
2024-04-18 20:42:35 -04:00
bf1c1af0e5 Fix not being able to click the link on the docs page
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m0s
2024-04-18 20:31:47 -04:00
e3e8df6953 Cant click the server status 2024-04-18 20:31:00 -04:00
507ae5c413 Center the footer disclaimer
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 59s
2024-04-18 20:29:58 -04:00
2aed687713 oops, add missing assets
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m0s
2024-04-18 20:19:50 -04:00
f7dea28738 Open Mojang status endpoints in a new URL 2024-04-18 20:18:33 -04:00
b08cb6efb6 license
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m5s
2024-04-18 20:14:13 -04:00
f1cd72e8b3 wow a footer!
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
2024-04-18 20:12:25 -04:00
ab56798ae9 Merge pull request 'fix(deps): update dependency org.springframework.boot:spring-boot-starter-parent to v3.2.5' (#10) from renovate/spring-boot into master
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m41s
Reviewed-on: #10
2024-04-18 13:45:38 -07:00
5e086c17b8 Make GH stars a client component
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m3s
2024-04-18 16:44:25 -04:00
817493bfd4 Fix raw json positioning for the player result 2024-04-18 16:34:42 -04:00
Renovate Bot
28e28c8ade fix(deps): update dependency org.springframework.boot:spring-boot-starter-parent to v3.2.5 2024-04-18 19:02:05 +00:00
c4d915095c x.x
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m14s
2024-04-18 14:06:19 -04:00
a57e95806e Fix card spacing
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 45s
2024-04-18 14:04:54 -04:00
618d523ca2 Make the /mojang page dynamic
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 37s
2024-04-18 14:02:42 -04:00
bacdc9319f Add the Mojang status page
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m58s
2024-04-18 13:58:21 -04:00
cb0280f817 Update the JS SDK to support the new layout of the /mojang/status endpoint
All checks were successful
Publish JS SDK / docker (push) Successful in 21s
2024-04-18 13:18:00 -04:00
fd16430307 Update the return json of /mojang/status in the API
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 2m14s
2024-04-18 13:09:23 -04:00
979018b508 .
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m9s
2024-04-18 12:51:45 -04:00
6e21847c3b Make the hero component spacing smaller 2024-04-18 12:42:47 -04:00
e104bc8728 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m17s
2024-04-18 12:34:50 -04:00
e38b9ff539 Add a background to the hero 2024-04-18 12:34:47 -04:00
038766b8b1 Merge pull request 'fix(deps): update dependency lucide-react to ^0.371.0' (#9) from renovate/lucide-react-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m41s
Reviewed-on: #9
2024-04-18 08:47:52 -07:00
Renovate Bot
9332bbde13 fix(deps): update dependency lucide-react to ^0.371.0 2024-04-18 08:03:11 +00:00
727a8f801f Update Frontend/config.json
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m9s
2024-04-17 22:04:56 -07:00
4da56be833 Merge pull request 'chore(deps): update nextjs monorepo to v14.2.2' (#8) from renovate/nextjs-monorepo into master
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Has been cancelled
Reviewed-on: #8
2024-04-17 22:02:28 -07:00
Renovate Bot
4bfeb88989 chore(deps): update nextjs monorepo to v14.2.2 2024-04-18 05:02:07 +00:00
8bcc5aa352 Player and Server page embeds
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m11s
2024-04-18 00:38:38 -04:00
46a0869b85 Format server player count in embed with commas
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m11s
2024-04-18 00:36:50 -04:00
cb75a3a4c8 make toasts look better (:
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m2s
2024-04-18 00:31:45 -04:00
bf3a09afd4 oops
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m19s
2024-04-18 00:12:13 -04:00
4b5c8d1d89 Don't move toasts to the top for smaller screens
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m24s
2024-04-18 00:09:40 -04:00
d1d78ef71b Add toasts to copy buttons
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m41s
2024-04-18 00:04:33 -04:00
7c9ba6d151 Fix naming for server platform in embeds
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m10s
2024-04-17 23:52:48 -04:00
a20b8008f4 Server embeds
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m12s
2024-04-17 23:47:42 -04:00
4494bd74ec fix centering 2024-04-17 23:40:38 -04:00
56563802be server route!
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m59s
2024-04-17 23:34:13 -04:00
367c974cb3 Fix copy buttons in context menu
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m10s
2024-04-17 20:54:15 -04:00
b52f9d1d38 Fix build error
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m28s
2024-04-17 20:47:45 -04:00
a1dfa6b6fa Updates
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 1m55s
2024-04-17 20:37:49 -04:00
9905673b1e Fix badge color
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m8s
2024-04-17 19:44:59 -04:00
9dbe9632c0 Raw Json button
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m10s
2024-04-17 19:43:35 -04:00
69ce0fc469 Update the docs page
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m8s
2024-04-17 19:19:52 -04:00
c9f15011af Make the 404 page responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m13s
2024-04-17 19:16:49 -04:00
cb6e5dc794 Disable the footer for now, it isn't done 2024-04-17 19:14:40 -04:00
0610a6acfa warning fixes 2024-04-17 19:14:10 -04:00
6790083006 oops forgot these lol 2024-04-17 19:06:02 -04:00
b560341068 Changes 2024-04-17 19:04:15 -04:00
6a9ec1fe9f Show GitHub star button on smaller devices
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m47s
2024-04-17 18:04:32 -04:00
2f419c1754 Make the nav and landing page better on mobile
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m5s
2024-04-17 17:50:05 -04:00
fa2a2dd8d8 Stats counter
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m26s
2024-04-17 17:34:07 -04:00
eadcebab4d Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC 2024-04-17 13:48:11 -04:00
4dfc081305 Fix the Get Started button in the hero 2024-04-17 13:45:12 -04:00
7dd5adfffb Merge pull request 'fix(deps): update dependency lucide-react to ^0.370.0' (#7) from renovate/lucide-react-0.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m41s
Reviewed-on: #7
2024-04-17 10:30:51 -07:00
Renovate Bot
2de5aa5581 fix(deps): update dependency lucide-react to ^0.370.0 2024-04-17 15:02:24 +00:00
132b3e0315 Make the /player page responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m9s
2024-04-17 10:55:10 -04:00
6d728dcb10 Add CONTRIBUTING.md 2024-04-17 10:19:01 -04:00
87d6e0ab3b Fix the navbar being off center
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m7s
2024-04-17 09:43:14 -04:00
eff8af532a Fix spacing for the star on GH button 2024-04-17 09:35:22 -04:00
05131b614d docs metadata 2024-04-17 09:33:02 -04:00
b6d59b9598 Merge pull request 'chore(deps): update dependency eslint to v9' (#6) from renovate/eslint-9.x into master
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 2m48s
Reviewed-on: #6
2024-04-17 06:23:11 -07:00
Renovate Bot
ca343f22c6 chore(deps): update dependency eslint to v9 2024-04-17 04:02:32 +00:00
ab495cd471 embed color
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 57s
2024-04-16 23:31:15 -04:00
ae4ece4ee5 does this work?
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 58s
2024-04-16 23:28:12 -04:00
54c116b85c Embed updates
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 57s
2024-04-16 23:25:41 -04:00
d5187aff6c robots 2024-04-16 23:24:02 -04:00
f4f51b8912 Fix the GitHub star button
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 56s
2024-04-16 23:15:09 -04:00
51b8d763c1 embed test 2024-04-16 23:15:00 -04:00
45f38396c6 Update player search form
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m6s
2024-04-16 23:10:19 -04:00
b89fd74515 Update for new domain 2024-04-16 22:59:16 -04:00
490ba046ef Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m25s
2024-04-16 20:53:07 -04:00
03df9aabdf Fix URL on the frontend 2024-04-16 20:52:59 -04:00
9b87a84215 Update .gitea/workflows/deploy-frontend.yml
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 3m15s
2024-04-16 17:24:57 -07:00
21f31c0f1d updates to the frontend
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 18s
2024-04-16 20:23:09 -04:00
0216662767 Updates to the frontend 2024-04-16 20:22:57 -04:00
c7d0f31852 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
All checks were successful
Publish JS SDK / docker (push) Successful in 23s
2024-04-16 20:04:44 -04:00
198d8cf1c1 fix npm publish error for the JS SDK 2024-04-16 20:04:31 -04:00
c707b0d3ac Update API deploy workflow
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m37s
2024-04-16 16:55:40 -07:00
a503e814f9 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Publish JS SDK / docker (push) Failing after 20s
2024-04-16 19:53:40 -04:00
bfd6a93229 Version bump 2024-04-16 19:53:28 -04:00
3431e7fe03 Change the endpoint URL in the JS SDK 2024-04-16 19:53:08 -04:00
0972e6d102 Update API/README.md
Some checks failed
Deploy API / docker (17, 3.8.5) (push) Failing after 1m39s
2024-04-16 16:46:41 -07:00
83b2a96490 show players! 2024-04-16 18:27:56 -04:00
6dbbc50610 Add BODY_FLAT skin part to the JS SDK
Some checks failed
Publish JS SDK / docker (push) Failing after 27s
2024-04-16 18:11:56 -04:00
0fb320c076 Fix enums in the JS SDK
Some checks failed
Publish JS SDK / docker (push) Failing after 17s
2024-04-16 17:17:20 -04:00
3beccf01c8 Update src
Some checks failed
Publish JS SDK / docker (push) Failing after 16s
2024-04-16 16:56:38 -04:00
c6259e3c8d code! 2024-04-16 16:40:25 -04:00
3588b5f93c Remove extra slash in web requests 2024-04-16 14:17:34 -04:00
a90299fae7 JS SDK ver bump 2024-04-16 14:17:18 -04:00
7fe1b51973 Make mobile responsiveness better for the landing page 2024-04-16 10:54:30 -04:00
abc35e4b21 Make the hero mobile responsive 2024-04-16 10:52:55 -04:00
86f71ad0c1 Fix the nav overlaying other elements 2024-04-16 10:52:44 -04:00
10b12a60da Move featured items to the config, and make it more responsive 2024-04-16 10:48:32 -04:00
8195239251 Open GitHub link in new page 2024-04-16 10:26:19 -04:00
6dca925ee4 Not found page 2024-04-16 10:26:01 -04:00
31806a6d58 Fix layout error 2024-04-16 10:25:50 -04:00
8f2cd92e62 update creeper 2024-04-16 10:25:37 -04:00
c367fc7474 Docs page 2024-04-16 10:13:20 -04:00
b5251cbf59 creeper! 2024-04-16 10:13:13 -04:00
7465ba6175 More content 2024-04-16 09:59:20 -04:00
750c4cbc63 wtf was i doing lmao 2024-04-16 08:42:14 -04:00
3d48b9fd26 Fix warning with MC button 2024-04-16 07:48:53 -04:00
ec393c0f64 Scrollbar 2024-04-16 07:48:42 -04:00
7958fb3c21 More landing page content 2024-04-15 23:24:50 -04:00
061b7df8b1 Make the navbar responsive 2024-04-15 22:53:23 -04:00
18c298376a Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC 2024-04-15 22:32:00 -04:00
d29004f673 Get a better style down 2024-04-15 22:31:55 -04:00
0072dc0362 Frontend base 2024-04-15 18:00:47 -04:00
158b3ec69e Update .gitea/workflows/publish-js-sdk.yml
Some checks failed
Publish JS SDK / docker (push) Failing after 16s
2024-04-15 13:32:01 -07:00
12cf444578 Add Frontend
Some checks failed
Publish JS SDK / docker (push) Failing after 17s
2024-04-15 16:16:08 -04:00
01a55e63b4 Bump version
All checks were successful
Publish JS SDK / docker (push) Successful in 19s
2024-04-15 16:04:35 -04:00
90e170b577 Add HttpMethod enum
Some checks failed
Publish JS SDK / docker (push) Failing after 19s
2024-04-15 15:46:53 -04:00
75316d5f6b Fix Mojang tests
All checks were successful
Publish JS SDK / docker (push) Successful in 21s
2024-04-15 11:25:42 -04:00
9cf2f10ee9 Export enums
Some checks failed
Publish JS SDK / docker (push) Failing after 19s
2024-04-15 11:24:03 -04:00
77bff8067e Export server platform
All checks were successful
Publish JS SDK / docker (push) Successful in 32s
2024-04-15 11:10:05 -04:00
9be372e3e0 JS SDK version bump 2024-04-15 11:08:10 -04:00
053bfedfa1 JS SDK version bump
All checks were successful
Publish JS SDK / docker (push) Successful in 48s
2024-04-15 11:06:00 -04:00
fc382fad27 Fix skin part not being exported
Some checks failed
Publish JS SDK / docker (push) Has been cancelled
2024-04-15 11:05:31 -04:00
bb81be1e28 Fix tests not running
All checks were successful
Publish JS SDK / docker (push) Successful in 26s
2024-04-15 10:38:35 -04:00
3b9cf6f818 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Publish JS SDK / docker (push) Failing after 18s
2024-04-15 10:32:56 -04:00
cacf1dc4cc oops, missed some files 2024-04-15 10:32:53 -04:00
a51528616d Fix the build not including all types 2024-04-15 10:32:44 -04:00
9f1ce626df JS SDK version bump 2024-04-15 10:04:02 -04:00
1277644bcb Update README.md 2024-04-15 06:50:13 -07:00
ffe005d995 Merge remote-tracking branch 'origin/master'
All checks were successful
Deploy API / docker (17, 3.8.5) (push) Successful in 1m34s
2024-04-15 09:48:30 -04:00
3365d01c8e Add README.md to API 2024-04-15 09:48:22 -04:00
2214636c47 Update JS-SDK/README.md
Some checks failed
Publish JS SDK / docker (push) Failing after 22s
2024-04-15 06:45:57 -07:00
74a3305ddd Add server and Mojang tests
Some checks failed
Publish JS SDK / docker (push) Failing after 19s
2024-04-15 09:43:40 -04:00
3171346d4b Fix #isMojangBlocked not working 2024-04-15 09:40:19 -04:00
fbbb4e1483 Do proper tests
All checks were successful
Publish JS SDK / docker (push) Successful in 19s
2024-04-15 09:29:39 -04:00
834ae9df44 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Publish JS SDK / docker (push) Failing after 21s
2024-04-15 09:12:53 -04:00
3d409dc661 Update imports to use paths 2024-04-15 09:12:41 -04:00
cad22ad30a Update README.md 2024-04-15 05:56:53 -07:00
9139972955 Update README.md 2024-04-15 05:56:06 -07:00
6322af94bb Fix build error?
All checks were successful
Publish JS SDK / docker (push) Successful in 30s
2024-04-15 08:52:10 -04:00
d1365ae261 Cleanup
Some checks failed
Publish JS SDK / docker (push) Failing after 32s
2024-04-15 08:49:09 -04:00
3e42ed414a Add #getSkinPart and #getJavaServerFavicon 2024-04-15 08:48:57 -04:00
d834badb78 Use an enum for server platforms 2024-04-15 07:56:05 -04:00
baac1391ee Merge remote-tracking branch 'origin/master' 2024-04-15 07:52:47 -04:00
a218dc7ec3 oops, wasn't properly resolving the array buffer in web requests 2024-04-15 07:51:20 -04:00
1eb4a9ec0e Fix server types 2024-04-15 07:49:00 -04:00
bf9fa6582f JS minor version bump 2024-04-15 07:47:07 -04:00
b07d8c0888 Add better support to web requests 2024-04-15 07:46:45 -04:00
aa3813cb34 Update README.md 2024-04-15 04:30:24 -07:00
cf345c3410 Update workflow file name
Some checks failed
Publish JS SDK / docker (push) Failing after 18s
2024-04-15 07:28:47 -04:00
76201edd92 Lib -> JS-SDK
Some checks failed
Publish JS SDK / docker (push) Failing after 18s
2024-04-15 07:20:56 -04:00
1c1ea04cf0 Lib cleanup
Some checks failed
Deploy Lib / docker (push) Failing after 19s
2024-04-15 07:16:06 -04:00
49a6e9b7b0 Update lib README.md
Some checks failed
Deploy Lib / docker (push) Failing after 19s
2024-04-14 17:53:50 -04:00
4c614525b0 Update lib README.md
Some checks failed
Deploy Lib / docker (push) Has been cancelled
2024-04-14 17:52:45 -04:00
6c98d1e862 Update package name
All checks were successful
Deploy Lib / docker (push) Successful in 52s
Deploy API / docker (17, 3.8.5) (push) Successful in 54s
2024-04-14 17:31:27 -04:00
b087666afb Cleanup the deploy api workflow 2024-04-14 17:29:45 -04:00
67d6df8916 finally omg
Some checks failed
Deploy Lib / docker (push) Failing after 54s
2024-04-14 17:27:27 -04:00
dfe99c1acf fix auth?
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 53s
2024-04-14 17:25:38 -04:00
4b3e478329 ???????
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 54s
2024-04-14 17:23:52 -04:00
42cfab11f2 test
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 53s
2024-04-14 17:21:47 -04:00
92a609ece5 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Has been cancelled
2024-04-14 17:18:42 -04:00
6a561a8000 will this work? 2024-04-14 17:18:40 -04:00
39556c2a8d Update README.md 2024-04-14 14:15:06 -07:00
96cbbc1632 Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m7s
2024-04-14 17:12:41 -04:00
f4df437fc7 debug x.x 2024-04-14 17:12:39 -04:00
57bfc1b9da Update README.md 2024-04-14 14:09:22 -07:00
07ab78fd08 specify custom registry
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 38s
2024-04-14 17:07:47 -04:00
f4b8d5708f diff env var
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 46s
2024-04-14 17:04:04 -04:00
6c4ee55b95 Preserve whitespace in HTML
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m31s
2024-04-14 17:01:27 -04:00
d1b33d3d6e Ensure public access
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 47s
2024-04-14 16:57:29 -04:00
549bbc864e will it publish now???
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m0s
2024-04-14 16:54:36 -04:00
d49c9b20e4 Update deploy lib workflow
Some checks failed
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m0s
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m4s
2024-04-14 16:51:19 -04:00
7ad3c0f1f3 Update deploy api workflow 2024-04-14 16:49:34 -04:00
e3b88ec32e why tf doesnt this cache
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m3s
2024-04-14 16:48:15 -04:00
4c0346ab3e don't setup bun each time
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 8s
2024-04-14 16:43:35 -04:00
13d0a344b9 Fix publish auth err?
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Has been cancelled
2024-04-14 16:42:43 -04:00
7ebeb39cc3 oops, fix build error
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 52s
2024-04-14 16:38:41 -04:00
acae5dbcd6 Disable tests, not done yet
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m31s
2024-04-14 16:35:55 -04:00
2d58908626 Install depends
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m28s
2024-04-14 16:33:44 -04:00
50b5c8ba4b Cleanup the lib
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 1m29s
2024-04-14 16:29:29 -04:00
6e096554d8 fixed workflow not working
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0) (push) Failing after 3m2s
2024-04-14 16:22:40 -04:00
71cbf6ef1b now? 2024-04-14 16:22:08 -04:00
ae4079b6d8 Update deploy lib workflow 2024-04-14 16:21:33 -04:00
7e98b68db0 dont need this 2024-04-14 16:20:32 -04:00
e8b365b6f0 Run tests 2024-04-14 16:19:18 -04:00
d45b26fe09 Add deploy-lib workflow
Some checks failed
Deploy Lib / docker (ubuntu-latest, 2.44.0, 20.x) (push) Has been cancelled
2024-04-14 16:15:47 -04:00
13c0a76e97 DNS Record types 2024-04-14 16:10:45 -04:00
fe00e7d50b Add #getMojangServerStatus 2024-04-14 16:06:42 -04:00
18293cc579 Add #isMojangBlocked 2024-04-14 15:56:45 -04:00
499663aa64 Add Minecraft server lookup support to the lib 2024-04-14 15:53:36 -04:00
abccde5896 Update Player type 2024-04-14 14:35:55 -04:00
1968f835fd functional lib 2024-04-14 14:03:02 -04:00
9f0d85cf5b oops ignore this 2024-04-14 13:35:22 -04:00
c0ce86faf6 oops, ignore this 2024-04-14 13:35:14 -04:00
eabea2546b Merge branch 'master' of https://git.rainnny.club/Rainnny/RESTfulMC 2024-04-14 13:34:37 -04:00
9751a21c9f Make basic Lib 2024-04-14 13:34:34 -04:00
4777d7d5eb Fix a bug with some Bedrock servers missing data in the token
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m40s
2024-04-14 09:23:17 -07:00
4721b46ce9 Update API/Dockerfile
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m39s
2024-04-14 09:16:09 -07:00
27c83a55ba Update API/.gitignore
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m31s
2024-04-14 08:54:33 -07:00
7ab210edb7 Move to monorepo layout
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 26s
2024-04-14 11:26:54 -04:00
348f7fa1eb Fix failed unit tests
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m34s
2024-04-13 13:27:47 -04:00
3f1290fd07 Change response for Minecraft servers
Some checks failed
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Failing after 25s
2024-04-13 13:26:03 -04:00
d6a21fd5a3 Fix internal server err
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m42s
2024-04-13 13:05:46 -04:00
9c4a39641d Run #fetchMojangServerStatuses in parallel 2024-04-13 13:04:43 -04:00
b800badcc3 Add Unit testing for the Mojang route
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m33s
2024-04-13 11:51:52 -04:00
6c6b9349f2 Add Mojang status route
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m35s
2024-04-13 11:39:51 -04:00
6d112ac658 Fix server tests failing
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m30s
2024-04-12 02:59:19 -04:00
f01c208a9b Remove unused depends
Some checks failed
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Failing after 31s
2024-04-12 02:56:47 -04:00
5099ad9dfe Add a legacy field to players
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m26s
2024-04-11 09:21:16 -04:00
a03bac8f1d Allow for Java user profiles to be signed
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m27s
2024-04-11 09:09:25 -04:00
655ee50a21 Cache skin image data 2024-04-11 08:56:42 -04:00
256 changed files with 12566 additions and 438 deletions

View File

@ -1,24 +1,20 @@
name: Deploy App
name: Deploy API
on:
push:
branches: ["master"]
paths-ignore:
- .gitignore
- README.md
- LICENSE
- docker-compose.yml
- postman_collection.json
paths: [".gitea/workflows/deploy-api.yml", "API/**", "!API/README.md"]
jobs:
docker:
strategy:
matrix:
arch: ["ubuntu-latest"]
git-version: ["2.44.0"]
java-version: ["17"]
maven-version: ["3.8.5"]
runs-on: ${{ matrix.arch }}
runs-on: "ubuntu-latest"
defaults:
run:
working-directory: "./API"
# Steps to run
steps:
@ -48,5 +44,5 @@ jobs:
- name: Deploy to Dokku
uses: dokku/github-action@master
with:
git_remote_url: "ssh://dokku@10.10.3.28:22/restfulmc"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
git_remote_url: "ssh://dokku@10.10.3.28:22/restfulmc-api"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

View File

@ -0,0 +1,38 @@
name: Deploy Discord Bot
on:
push:
branches: ["master"]
paths: [".gitea/workflows/deploy-bot.yml", "DiscordBot/**", "!DiscordBot/README.md"]
jobs:
docker:
strategy:
matrix:
java-version: ["17"]
maven-version: ["3.8.5"]
runs-on: "ubuntu-latest"
defaults:
run:
working-directory: "./DiscordBot"
# Steps to run
steps:
# Checkout the repo
- name: Checkout
uses: actions/checkout@v4
# Setup Java and Maven
- name: Set up JDK and Maven
uses: s4u/setup-maven-action@v1.12.0
with:
java-version: ${{ matrix.java-version }}
distribution: "zulu"
maven-version: ${{ matrix.maven-version }}
# Deploy to Dokku
- name: Deploy to Dokku
uses: dokku/github-action@master
with:
git_remote_url: "ssh://dokku@10.10.3.28:22/restfulmc-bot"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

View File

@ -0,0 +1,32 @@
name: Deploy Frontend
on:
push:
branches: ["master"]
paths: [".gitea/workflows/deploy-frontend.yml", "Frontend/**", "!Frontend/README.md"]
jobs:
docker:
strategy:
matrix:
java-version: ["17"]
maven-version: ["3.8.5"]
runs-on: "ubuntu-latest"
defaults:
run:
working-directory: "./Frontend"
# Steps to run
steps:
# Checkout the repo
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Deploy to Dokku
- name: Deploy to Dokku
uses: dokku/github-action@master
with:
git_remote_url: "ssh://dokku@10.10.3.28:22/restfulmc-web"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

View File

@ -0,0 +1,53 @@
name: Publish Java SDK
on:
push:
branches: ["master"]
paths:
[".gitea/workflows/publish-java-sdk.yml", "Java-SDK/**", "!Java-SDK/README.md"]
jobs:
docker:
strategy:
matrix:
java-version: ["17"]
maven-version: ["3.8.5"]
runs-on: "ubuntu-latest"
defaults:
run:
working-directory: "./Java-SDK"
# Steps to run
steps:
# Checkout the repo
- name: Checkout
uses: actions/checkout@v4
# Setup Java and Maven
- name: Set up JDK and Maven
uses: s4u/setup-maven-action@v1.12.0
with:
java-version: ${{ matrix.java-version }}
distribution: "zulu"
maven-version: ${{ matrix.maven-version }}
# Configure Maven settings
- name: Maven Settings
uses: s4u/maven-settings-action@v3.0.0
with:
servers: |-
[
{
"id": "rainnny-repo-public",
"username": "${{ secrets.PRIVATE_MAVEN_USER }}",
"password": "${{ secrets.PRIVATE_MAVEN_PASS }}"
}
]
# Build the project
- name: Maven Build
run: mvn clean package -e -T6C
# Publish to Maven
- name: Publish to Maven
run: mvn deploy -Pgen-javadocs -B -Dstyle.color=always --update-snapshots -e -T6C

View File

@ -0,0 +1,38 @@
name: Publish JS SDK
on:
push:
branches: ["master"]
paths:
[".gitea/workflows/publish-js-sdk.yml", "JS-SDK/**", "!JS-SDK/README.md"]
jobs:
docker:
runs-on: "ubuntu-latest"
defaults:
run:
working-directory: "./JS-SDK"
# Steps to run
steps:
# Checkout the repo
- name: Checkout
uses: actions/checkout@v4
# Setup Bun
- name: Setup Bun
uses: oven-sh/setup-bun@v1
# Install Dependencies
- name: Install Dependencies
run: bun install --frozen-lockfile
# Run Tests
- name: Run Tests
run: bun test
# Publish to NPM
- name: Publish to NPM
run: npm publish
env:
NPM_TOKEN: ${{ secrets.PUBLISH_NPM_TOKEN }}

View File

@ -1,4 +1,3 @@
### Primary template
*.class
*.log
*.ctxt
@ -27,4 +26,4 @@ crashlytics.properties
crashlytics-build.properties
fabric.properties
git.properties
pom.xml.versionsBackup
pom.xml.versionsBackup

View File

@ -7,7 +7,7 @@ WORKDIR /home/container
COPY . .
# Build the app
RUN mvn clean package -DskipTests
RUN mvn clean package -q -DskipTests -T4C
# Exposting on port 80 so we can
# access via a reverse proxy for Dokku

17
API/README.md Normal file
View File

@ -0,0 +1,17 @@
[![Deploy Workflow](https://git.rainnny.club/Rainnny/RESTfulMC/actions/workflows/deploy-api.yml/badge.svg)](./actions?workflow=deploy-api.yml)
# RESTfulMC Springboot API
The springboot API backend for RESTfulMC.
## Swagger API
View the [Swagger API Docs](https://api.restfulmc.cc/swagger-ui.html) for easy testing!
![Swagger Logo](https://cdn.rainnny.club/UoeVQF7wYGg4.png)
---
## YourKit
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications.
YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor).
![Yourkit Logo](https://www.yourkit.com/images/yklogo.png)

View File

@ -6,12 +6,12 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<version>3.2.5</version>
<relativePath/>
</parent>
<!-- Project Details -->
<groupId>me.braydon</groupId>
<groupId>cc.restfulmc</groupId>
<artifactId>RESTfulMC</artifactId>
<version>1.0.0</version>
<description>A simple, yet useful RESTful API for Minecraft utilizing Springboot.</description>
@ -107,6 +107,12 @@
<version>1.20-R0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-archiver</artifactId>
<version>4.9.2</version>
<scope>compile</scope>
</dependency>
<!-- DNS Lookup -->
<dependency>
@ -116,17 +122,11 @@
<scope>compile</scope>
</dependency>
<!-- 3D Rendering 🧑🏼🔫 -->
<!-- GeoIP -->
<dependency>
<groupId>java3d</groupId>
<artifactId>j3d-core</artifactId>
<version>1.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>java3d</groupId>
<artifactId>j3d-core-utils</artifactId>
<version>1.3.1</version>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>4.2.0</version>
<scope>compile</scope>
</dependency>
@ -138,6 +138,14 @@
<scope>compile</scope>
</dependency>
<!-- Sentry -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
<version>7.8.0</version>
<scope>compile</scope>
</dependency>
<!-- Tests -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc;
package cc.restfulmc.api;
import lombok.NonNull;
import lombok.SneakyThrows;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
@ -97,7 +97,7 @@ public final class ColorUtils {
nextIsColor = false;
continue;
}
builder.append(character); // Append the char...
builder.append(character == ' ' ? "&nbsp;" : character); // Append the char...
}
return builder.toString();
}

View File

@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import cc.restfulmc.api.model.dns.impl.ARecord;
import cc.restfulmc.api.model.dns.impl.SRVRecord;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import me.braydon.mc.model.dns.impl.ARecord;
import me.braydon.mc.model.dns.impl.SRVRecord;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import lombok.experimental.UtilityClass;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.Getter;
import lombok.experimental.UtilityClass;
@ -35,8 +35,9 @@ public final class EnvironmentUtils {
* Is the app running in a production environment?
*/
@Getter private static final boolean production;
static { // Are we running on production?
String env = System.getenv("APP_ENV");
production = env != null && (env.equals("production"));
static {
// Are we running on production?
String appEnv = System.getenv("APP_ENV");
production = appEnv != null && (appEnv.equals("production"));
}
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import net.jodah.expiringmap.ExpirationPolicy;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull;

View File

@ -21,14 +21,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
/**
* @author Braydon
@ -65,4 +68,19 @@ public final class ImageUtils {
graphics.dispose();
return flipped;
}
/**
* Get the byte array from the given image.
*
* @param image the image to extract from
* @return the byte array of the image
*/
@SneakyThrows
public static byte[] toByteArray(@NonNull BufferedImage image) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ImageIO.write(image, "png", outputStream);
outputStream.flush();
return outputStream.toByteArray();
}
}
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.Getter;
import lombok.NonNull;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
@ -36,7 +36,7 @@ import java.util.regex.Pattern;
@UtilityClass
public final class MiscUtils {
private static final Pattern USERNAME_REGEX = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
private static final List<String> WHITELISTED_NAMES = Arrays.asList("8", "g");
private static final List<String> WHITELISTED_USERNAMES = Arrays.asList("8", "g");
/**
* Check if the given username is a valid.
@ -45,9 +45,6 @@ public final class MiscUtils {
* @return whether the username is valid
*/
public static boolean isUsernameValid(@NonNull String username) {
if (WHITELISTED_NAMES.contains(username.toLowerCase())) { // Name is whitelisted
return true;
}
return USERNAME_REGEX.matcher(username).matches();
return WHITELISTED_USERNAMES.contains(username.toLowerCase()) || USERNAME_REGEX.matcher(username).matches();
}
}

View File

@ -0,0 +1,107 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Represents a service provided by Mojang.
*
* @author Braydon
*/
@AllArgsConstructor @Getter @ToString
public enum MojangServer {
SESSION("Session Server", "https://sessionserver.mojang.com"),
API("Mojang API", "https://api.mojang.com"),
TEXTURES("Textures Server", "https://textures.minecraft.net"),
ASSETS("Assets Server", "https://assets.mojang.com"),
LIBRARIES("Libraries Server", "https://libraries.minecraft.net"),
SERVICES("Minecraft Services", "https://api.minecraftservices.com");
private static final int STATUS_TIMEOUT = 7000;
/**
* The name of this server.
*/
@NonNull private final String name;
/**
* The endpoint of this service.
*/
@NonNull private final String endpoint;
/**
* Ping this service and get the status of it.
*
* @return the service status
*/
@NonNull
public Status getStatus() {
try {
InetAddress address = InetAddress.getByName(endpoint.substring(8));
long before = System.currentTimeMillis();
if (address.isReachable(STATUS_TIMEOUT)) {
// The time it took to reach the host is 75% of
// the timeout, consider it to be degraded.
if ((System.currentTimeMillis() - before) > STATUS_TIMEOUT * 0.75D) {
return Status.DEGRADED;
}
return Status.ONLINE;
}
} catch (UnknownHostException ex) {
ex.printStackTrace();
} catch (IOException ignored) {
// We can safely ignore any errors, we're simply checking
// if the host is reachable, if it's not, it's offline.
}
return Status.OFFLINE;
}
/**
* The status of a service.
*/
public enum Status {
/**
* The service is online and accessible.
*/
ONLINE,
/**
* The service is online, but is experiencing degraded performance.
*/
DEGRADED,
/**
* The service is offline and inaccessible.
*/
OFFLINE
}
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common;
package cc.restfulmc.api.common;
import lombok.NonNull;
import lombok.experimental.UtilityClass;

View File

@ -0,0 +1,101 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common.packet;
import java.util.ArrayList;
/**
* @author Braydon
*/
public abstract class JavaQueryPacket extends UDPPacket {
protected static byte[] MAGIC = { (byte) 0xFE, (byte) 0xFD };
protected final byte[] padArrayEnd(byte[] array, int amount) {
byte[] result = new byte[array.length + amount];
System.arraycopy(array, 0, result, 0, array.length);
for (int i = array.length; i < result.length; i++) {
result[i] = 0;
}
return result;
}
protected final byte[] intToBytes(int input) {
return new byte[] {
(byte) (input >>> 24 & 0xFF),
(byte) (input >>> 16 & 0xFF),
(byte) (input >>> 8 & 0xFF),
(byte) (input & 0xFF)
};
}
protected final byte[] trim(byte[] arr) {
int begin = 0, end = arr.length;
for (int i = 0; i < arr.length; i++) { // find the first non-null byte{
if (arr[i] != 0) {
begin = i;
break;
}
}
for (int i = arr.length - 1; i >= 0; i--) { //find the last non-null byte
if (arr[i] != 0) {
end = i;
break;
}
}
return subarray(arr, begin, end);
}
protected final byte[] subarray(byte[] in, int a, int b) {
if (b - a > in.length) {
return in;
}
byte[] out = new byte[(b - a) + 1];
if (b + 1 - a >= 0) {
System.arraycopy(in, a, out, 0, b + 1 - a);
}
return out;
}
protected final byte[][] split(byte[] input) {
ArrayList<byte[]> temp = new ArrayList<>();
int index_cache = 0;
for (int i = 0; i < input.length; i++) {
if (input[i] == 0x00) {
byte[] b = subarray(input, index_cache, i - 1);
temp.add(b);
index_cache = i + 1;//note, this is the index *after* the null byte
}
}
//get the remaining part
if (index_cache != 0) { //prevent duplication if there are no null bytes
byte[] b = subarray(input, index_cache, input.length - 1);
temp.add(b);
}
byte[][] output = new byte[temp.size()][input.length];
for (int i = 0; i < temp.size(); i++) {
output[i] = temp.get(i);
}
return output;
}
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet;
package cc.restfulmc.api.common.packet;
import lombok.NonNull;
@ -36,7 +36,7 @@ import java.io.IOException;
* @author Braydon
* @see <a href="https://wiki.vg/Protocol">Protocol Docs</a>
*/
public abstract class MinecraftJavaPacket {
public abstract class TCPPacket {
/**
* Process this packet.
*

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet;
package cc.restfulmc.api.common.packet;
import lombok.NonNull;
@ -35,12 +35,12 @@ import java.net.DatagramSocket;
* @author Braydon
* @see <a href="https://wiki.vg/Raknet_Protocol">Protocol Docs</a>
*/
public interface MinecraftBedrockPacket {
public abstract class UDPPacket {
/**
* Process this packet.
*
* @param socket the socket to process the packet for
* @throws IOException if an I/O error occurs
*/
void process(@NonNull DatagramSocket socket) throws IOException;
public abstract void process(@NonNull DatagramSocket socket) throws IOException;
}

View File

@ -21,10 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet.impl.bedrock;
package cc.restfulmc.api.common.packet.impl.bedrock;
import cc.restfulmc.api.common.packet.UDPPacket;
import lombok.NonNull;
import me.braydon.mc.common.packet.MinecraftBedrockPacket;
import java.io.IOException;
import java.net.DatagramPacket;
@ -40,7 +40,7 @@ import java.nio.ByteOrder;
* @author Braydon
* @see <a href="https://wiki.vg/Raknet_Protocol#Unconnected_Ping">Protocol Docs</a>
*/
public final class BedrockPacketUnconnectedPing implements MinecraftBedrockPacket {
public final class BedrockUnconnectedPingPacket extends UDPPacket {
private static final byte ID = 0x01; // The ID of the packet
private static final byte[] MAGIC = { 0, -1, -1, 0, -2, -2, -2, -2, -3, -3, -3, -3, 18, 52, 86, 120 };

View File

@ -21,12 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet.impl.bedrock;
package cc.restfulmc.api.common.packet.impl.bedrock;
import cc.restfulmc.api.common.packet.UDPPacket;
import cc.restfulmc.api.model.server.BedrockMinecraftServer;
import lombok.Getter;
import lombok.NonNull;
import me.braydon.mc.common.packet.MinecraftBedrockPacket;
import me.braydon.mc.model.server.BedrockMinecraftServer;
import java.io.IOException;
import java.net.DatagramPacket;
@ -37,13 +37,13 @@ import java.nio.charset.StandardCharsets;
/**
* This packet is sent by the server to the client in
* response to the {@link BedrockPacketUnconnectedPing}.
* response to the {@link BedrockUnconnectedPingPacket}.
*
* @author Braydon
* @see <a href="https://wiki.vg/Raknet_Protocol#Unconnected_Pong">Protocol Docs</a>
*/
@Getter
public final class BedrockPacketUnconnectedPong implements MinecraftBedrockPacket {
public final class BedrockUnconnectedPongPacket extends UDPPacket {
private static final byte ID = 0x1C; // The ID of the packet
/**

View File

@ -21,12 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet.impl.java;
package cc.restfulmc.api.common.packet.impl.java.tcp;
import cc.restfulmc.api.common.packet.TCPPacket;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.ToString;
import me.braydon.mc.common.packet.MinecraftJavaPacket;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@ -41,7 +41,7 @@ import java.io.IOException;
* @see <a href="https://wiki.vg/Protocol#Handshake">Protocol Docs</a>
*/
@AllArgsConstructor @ToString
public final class JavaPacketHandshakingInSetProtocol extends MinecraftJavaPacket {
public final class JavaHandshakingInSetProtocolPacket extends TCPPacket {
private static final byte ID = 0x00; // The ID of the packet
private static final int STATUS_HANDSHAKE = 1; // The status handshake ID

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.packet.impl.java;
package cc.restfulmc.api.common.packet.impl.java.tcp;
import cc.restfulmc.api.common.packet.TCPPacket;
import lombok.Getter;
import lombok.NonNull;
import me.braydon.mc.common.packet.MinecraftJavaPacket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@ -40,7 +40,7 @@ import java.io.IOException;
* @see <a href="https://wiki.vg/Protocol#Status_Request">Protocol Docs</a>
*/
@Getter
public final class JavaPacketStatusInStart extends MinecraftJavaPacket {
public final class JavaStatusInStartPacket extends TCPPacket {
private static final byte ID = 0x00; // The ID of the packet
/**

View File

@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common.packet.impl.java.udp;
import cc.restfulmc.api.common.packet.JavaQueryPacket;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* This packet is sent by the client to the server to request the
* full stats of the server. The server will respond with a payload
* containing the server's stats.
*
* @author Braydon
* @see <a href="https://wiki.vg/Query#Request_3">Query Protocol Docs</a>
*/
@AllArgsConstructor
public final class JavaQueryFullStatRequestPacket extends JavaQueryPacket {
private static final int ID = 0; // The ID of the packet
/**
* The response from the {@link JavaQueryHandshakeRequestPacket}.
*/
private final byte[] handshakeResponse;
/**
* Process this packet.
*
* @param socket the socket to process the packet for
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DatagramSocket socket) throws IOException {
try (
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(1460);
DataOutputStream dataOutputStream = new DataOutputStream(arrayOutputStream)
) {
dataOutputStream.write(MAGIC);
dataOutputStream.write(ID); // Packet ID
dataOutputStream.writeInt(1); // Session ID
dataOutputStream.write(padArrayEnd(handshakeResponse, 4)); // The handshake response payload
// Send the packet
byte[] bytes = arrayOutputStream.toByteArray();
socket.send(new DatagramPacket(bytes, bytes.length));
}
}
}

View File

@ -0,0 +1,79 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common.packet.impl.java.udp;
import cc.restfulmc.api.common.packet.JavaQueryPacket;
import lombok.Getter;
import lombok.NonNull;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.HashMap;
import java.util.Map;
/**
* This packet is sent by the server to the client in
* response to the {@link JavaQueryFullStatRequestPacket}.
*
* @author Braydon
* @see <a href="https://wiki.vg/Query#Response_3">Query Protocol Docs</a>
*/
@Getter
public final class JavaQueryFullStatResponsePacket extends JavaQueryPacket {
/**
* The response from the server, null if none.
*/
private Map<String, String> response;
/**
* Process this packet.
*
* @param socket the socket to process the packet for
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DatagramSocket socket) throws IOException {
// Handle receiving of the packet
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
// Construct a response from the received packet.
Map<String, String> response = new HashMap<>();
String previousEntry = null;
for (byte[] bytes : split(trim(receivePacket.getData()))) {
String entry = new String(bytes); // The entry
if (previousEntry != null) {
response.put(previousEntry, entry);
previousEntry = null;
continue;
}
previousEntry = entry;
}
this.response = response;
}
}

View File

@ -0,0 +1,70 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common.packet.impl.java.udp;
import cc.restfulmc.api.common.packet.JavaQueryPacket;
import lombok.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* This packet is sent by the client to the
* server to request a handshake. This will
* then allow us to send further packets such
* as {@link JavaQueryFullStatRequestPacket}.
*
* @author Braydon
* @see <a href="https://wiki.vg/Query#Request">Query Protocol Docs</a>
*/
public final class JavaQueryHandshakeRequestPacket extends JavaQueryPacket {
private static final int ID = 9; // The ID of the packet
/**
* Process this packet.
*
* @param socket the socket to process the packet for
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DatagramSocket socket) throws IOException {
try (
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(1460);
DataOutputStream dataOutputStream = new DataOutputStream(arrayOutputStream)
) {
dataOutputStream.write(MAGIC);
dataOutputStream.write(ID); // Packet ID
dataOutputStream.writeInt(1); // Session ID
dataOutputStream.write(new byte[] {}); // No payload data
// Send the packet
byte[] bytes = arrayOutputStream.toByteArray();
bytes = padArrayEnd(bytes, 11 - bytes.length);
socket.send(new DatagramPacket(bytes, bytes.length));
}
}
}

View File

@ -0,0 +1,64 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.common.packet.impl.java.udp;
import cc.restfulmc.api.common.packet.JavaQueryPacket;
import lombok.Getter;
import lombok.NonNull;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* This packet is sent by the server to the client in
* response to the {@link JavaQueryHandshakeRequestPacket}.
*
* @author Braydon
* @see <a href="https://wiki.vg/Query#Response">Query Protocol Docs</a>
*/
@Getter
public final class JavaQueryHandshakeResponsePacket extends JavaQueryPacket {
/**
* The response from the server.
*/
private byte[] response;
/**
* Process this packet.
*
* @param socket the socket to process the packet for
* @throws IOException if an I/O error occurs
*/
@Override
public void process(@NonNull DatagramSocket socket) throws IOException {
// Handle receiving of the packet
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
// Set the response to the integer value of the received data
response = intToBytes(Integer.parseInt(new String(receivePacket.getData()).trim()));
}
}

View File

@ -21,10 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.renderer;
package cc.restfulmc.api.common.renderer;
import cc.restfulmc.api.model.skin.ISkinPart;
import lombok.NonNull;
import me.braydon.mc.model.skin.ISkinPart;
import java.awt.*;
import java.awt.geom.AffineTransform;

View File

@ -21,18 +21,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.renderer;
package cc.restfulmc.api.common.renderer;
import cc.restfulmc.api.common.ImageUtils;
import cc.restfulmc.api.model.skin.ISkinPart;
import cc.restfulmc.api.model.skin.Skin;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.braydon.mc.common.ImageUtils;
import me.braydon.mc.model.skin.ISkinPart;
import me.braydon.mc.model.skin.Skin;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.io.ByteArrayInputStream;
/**
* A renderer for a {@link ISkinPart}.
@ -63,17 +63,17 @@ public abstract class SkinRenderer<T extends ISkinPart> {
*/
@SneakyThrows
protected final BufferedImage getVanillaSkinPart(@NonNull Skin skin, @NonNull ISkinPart.Vanilla part, double size) {
BufferedImage skinImage = ImageIO.read(new URL(skin.getUrl())); // The skin texture
ISkinPart.Vanilla.Coordinates coordinates = part.getCoordinates(); // The coordinates of the part
// The skin texture is legacy, use legacy coordinates
if (skinImage.getHeight() == 32 && part.hasLegacyCoordinates()) {
if (skin.isLegacy() && part.hasLegacyCoordinates()) {
coordinates = part.getLegacyCoordinates();
}
int width = part.getWidth(); // The width of the part
if (skin.getModel() == Skin.Model.SLIM && part.isFrontArm()) {
width--;
}
BufferedImage skinImage = ImageIO.read(new ByteArrayInputStream(skin.getSkinImage())); // The skin texture
BufferedImage partTexture = getSkinPartTexture(skinImage, coordinates.getX(), coordinates.getY(), width, part.getHeight(), size);
if (coordinates instanceof ISkinPart.Vanilla.LegacyCoordinates legacyCoordinates && legacyCoordinates.isFlipped()) {
partTexture = ImageUtils.flip(partTexture);

View File

@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.renderer.impl;
package cc.restfulmc.api.common.renderer.impl;
import cc.restfulmc.api.common.ImageUtils;
import cc.restfulmc.api.common.renderer.SkinRenderer;
import cc.restfulmc.api.model.skin.ISkinPart;
import cc.restfulmc.api.model.skin.Skin;
import lombok.NonNull;
import me.braydon.mc.common.ImageUtils;
import me.braydon.mc.common.renderer.SkinRenderer;
import me.braydon.mc.model.skin.ISkinPart;
import me.braydon.mc.model.skin.Skin;
import java.awt.*;
import java.awt.image.BufferedImage;

View File

@ -21,12 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.renderer.impl;
package cc.restfulmc.api.common.renderer.impl;
import cc.restfulmc.api.common.renderer.IsometricSkinRenderer;
import cc.restfulmc.api.model.skin.ISkinPart;
import cc.restfulmc.api.model.skin.Skin;
import lombok.NonNull;
import me.braydon.mc.common.renderer.IsometricSkinRenderer;
import me.braydon.mc.model.skin.ISkinPart;
import me.braydon.mc.model.skin.Skin;
import java.awt.*;
import java.awt.geom.AffineTransform;

View File

@ -21,12 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.renderer.impl;
package cc.restfulmc.api.common.renderer.impl;
import cc.restfulmc.api.common.renderer.SkinRenderer;
import cc.restfulmc.api.model.skin.ISkinPart;
import cc.restfulmc.api.model.skin.Skin;
import lombok.NonNull;
import me.braydon.mc.common.renderer.SkinRenderer;
import me.braydon.mc.model.skin.ISkinPart;
import me.braydon.mc.model.skin.Skin;
import java.awt.*;
import java.awt.image.BufferedImage;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.web;
package cc.restfulmc.api.common.web;
import lombok.Getter;
import lombok.NonNull;

View File

@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.common.web;
package cc.restfulmc.api.common.web;
import cc.restfulmc.api.config.AppConfig;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import me.braydon.mc.config.AppConfig;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.config;
package cc.restfulmc.api.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.config;
package cc.restfulmc.api.config;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;

View File

@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.controller;
import cc.restfulmc.api.common.MojangServer;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.service.MojangService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The controller for handling
* Mojang related requests.
*
* @author Braydon
*/
@RestController
@RequestMapping(value = "/mojang", produces = MediaType.APPLICATION_JSON_VALUE)
@Log4j2(topic = "Mojang Controller")
@Tag(name = "Mojang Controller", description = "The controller for handling Mojang related requests.")
public final class MojangController {
/**
* The Mojang service to use.
*/
@NonNull private final MojangService mojangService;
@Autowired
public MojangController(@NonNull MojangService mojangService) {
this.mojangService = mojangService;
}
/**
* A GET route to get the status of Mojang servers.
*
* @return the status response
*/
@GetMapping("/status")
@ResponseBody
public ResponseEntity<Map<String, List<Map<String, Object>>>> getStatus() throws BadRequestException {
List<Map<String, Object>> servers = new ArrayList<>();
for (Map.Entry<MojangServer, MojangServer.Status> entry : mojangService.getMojangServerStatuses().entrySet()) {
MojangServer server = entry.getKey();
Map<String, Object> serverStatus = new HashMap<>();
serverStatus.put("name", server.getName());
serverStatus.put("endpoint", server.getEndpoint());
serverStatus.put("status", entry.getValue().name());
servers.add(serverStatus);
}
return ResponseEntity.ok(Map.of("servers", servers));
}
}

View File

@ -21,18 +21,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.controller;
package cc.restfulmc.api.controller;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.exception.impl.MojangRateLimitException;
import cc.restfulmc.api.exception.impl.ResourceNotFoundException;
import cc.restfulmc.api.model.Player;
import cc.restfulmc.api.model.cache.CachedPlayer;
import cc.restfulmc.api.service.MojangService;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.MojangRateLimitException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.Player;
import me.braydon.mc.model.cache.CachedPlayer;
import me.braydon.mc.service.MojangService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -50,7 +50,7 @@ import org.springframework.web.bind.annotation.*;
@Tag(name = "Player Controller", description = "The controller for handling player related requests.")
public final class PlayerController {
/**
* The Mojang service to use for player information.
* The Mojang service to use.
*/
@NonNull private final MojangService mojangService;
@ -63,6 +63,7 @@ public final class PlayerController {
* A GET route to get a player by their username or UUID.
*
* @param query the query to search for the player by
* @param signed whether the profile is signed
* @return the player response
* @throws BadRequestException if the UUID or username is invalid
* @throws ResourceNotFoundException if the player is not found
@ -71,9 +72,10 @@ public final class PlayerController {
@GetMapping("/{query}")
@ResponseBody
public ResponseEntity<CachedPlayer> getPlayer(
@Parameter(description = "The player username or UUID to get", example = "Rainnny") @PathVariable @NonNull String query
@Parameter(description = "The player username or UUID to get", example = "Rainnny") @PathVariable @NonNull String query,
@Parameter(description = "Whether the profile is signed by Mojang") @RequestParam(required = false) boolean signed
) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
return ResponseEntity.ofNullable(mojangService.getPlayer(query));
return ResponseEntity.ofNullable(mojangService.getPlayer(query, signed));
}
/**

View File

@ -21,17 +21,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.controller;
package cc.restfulmc.api.controller;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.exception.impl.ResourceNotFoundException;
import cc.restfulmc.api.model.MinecraftServer;
import cc.restfulmc.api.model.cache.CachedMinecraftServer;
import cc.restfulmc.api.service.MojangService;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.cache.CachedMinecraftServer;
import me.braydon.mc.service.MojangService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -52,7 +52,7 @@ import java.util.Map;
@Tag(name = "Server Controller", description = "The controller for handling server related requests.")
public final class ServerController {
/**
* The Mojang service to use for server information.
* The Mojang service to use.
*/
@NonNull private final MojangService mojangService;

View File

@ -21,10 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.exception;
package cc.restfulmc.api.exception;
import cc.restfulmc.api.model.response.ErrorResponse;
import io.sentry.Sentry;
import lombok.NonNull;
import me.braydon.mc.model.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ -66,6 +67,7 @@ public final class ExceptionControllerAdvice {
}
if (status == null) { // Fallback to 500
status = HttpStatus.INTERNAL_SERVER_ERROR;
Sentry.captureException(ex); // Capture with Sentry
}
return new ResponseEntity<>(new ErrorResponse(status, message), status);
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.exception.impl;
package cc.restfulmc.api.exception.impl;
import lombok.experimental.StandardException;
import org.springframework.http.HttpStatus;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.exception.impl;
package cc.restfulmc.api.exception.impl;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.exception.impl;
package cc.restfulmc.api.exception.impl;
import lombok.experimental.StandardException;
import org.springframework.http.HttpStatus;

View File

@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.log;
package cc.restfulmc.api.log;
import cc.restfulmc.api.common.IPUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import me.braydon.mc.common.IPUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model;
package cc.restfulmc.api.model;
import com.google.gson.JsonObject;
import lombok.*;

View File

@ -21,15 +21,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model;
package cc.restfulmc.api.model;
import cc.restfulmc.api.common.ColorUtils;
import cc.restfulmc.api.model.dns.DNSRecord;
import cc.restfulmc.api.model.token.JavaServerStatusToken;
import cc.restfulmc.api.service.pinger.MinecraftServerPinger;
import cc.restfulmc.api.service.pinger.impl.BedrockMinecraftServerPinger;
import cc.restfulmc.api.service.pinger.impl.JavaMinecraftServerPinger;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Continent;
import com.maxmind.geoip2.record.Country;
import com.maxmind.geoip2.record.Location;
import lombok.*;
import me.braydon.mc.common.ColorUtils;
import me.braydon.mc.model.dns.DNSRecord;
import me.braydon.mc.model.token.JavaServerStatusToken;
import me.braydon.mc.service.pinger.MinecraftServerPinger;
import me.braydon.mc.service.pinger.impl.BedrockMinecraftServerPinger;
import me.braydon.mc.service.pinger.impl.JavaMinecraftServerPinger;
import java.util.ArrayList;
import java.util.Arrays;
@ -41,7 +46,7 @@ import java.util.UUID;
*
* @author Braydon
*/
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
@AllArgsConstructor @Setter @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
public class MinecraftServer {
/**
* The hostname of this server.
@ -59,9 +64,14 @@ public class MinecraftServer {
@EqualsAndHashCode.Include private final int port;
/**
* The DNS records resolved for this server.
* The DNS records resolved for this server, null if none.
*/
@NonNull private final DNSRecord[] records;
private final DNSRecord[] records;
/**
* The Geo location of this server, null if unknown.
*/
private GeoLocation geo;
/**
* The player counts of this server.
@ -73,6 +83,74 @@ public class MinecraftServer {
*/
@NonNull private final MOTD motd;
/**
* The Geo location of a server.
*/
@AllArgsConstructor @Getter @ToString
public static class GeoLocation {
/**
* The continent of this server.
*/
@NonNull private final LocationData continent;
/**
* The country of this server.
*/
@NonNull private final LocationData country;
/**
* The city of this server, null if unknown.
*/
private final String city;
/**
* The latitude of this server.
*/
private final double latitude;
/**
* The longitude of this server.
*/
private final double longitude;
/**
* Create new geo location data
* from the given city response.
*
* @param geo the geo city response
* @return the geo location
*/
@NonNull
public static GeoLocation create(@NonNull CityResponse geo) {
Continent continent = geo.getContinent();
Country country = geo.getCountry();
City city = geo.getCity();
Location location = geo.getLocation();
return new GeoLocation(
new LocationData(continent.getCode(), continent.getName()),
new LocationData(country.getIsoCode(), country.getName()),
city == null ? null : city.getName(),
location.getLatitude(), location.getLongitude()
);
}
/**
* Data for a location.
*/
@AllArgsConstructor @Getter @ToString
public static class LocationData {
/**
* The location code.
*/
@NonNull private final String code;
/**
* The location name.
*/
@NonNull private final String name;
}
}
/**
* Player count data for a server.
*/

View File

@ -21,12 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model;
package cc.restfulmc.api.model;
import cc.restfulmc.api.model.skin.Skin;
import cc.restfulmc.api.model.token.MojangProfileToken;
import lombok.*;
import me.braydon.mc.model.skin.Skin;
import me.braydon.mc.model.token.MojangProfileToken;
import org.springframework.data.annotation.Id;
import java.util.UUID;
@ -40,7 +39,7 @@ public class Player {
/**
* The unique id of this player.
*/
@Id @EqualsAndHashCode.Include @NonNull private final UUID uniqueId;
@EqualsAndHashCode.Include @NonNull private final UUID uniqueId;
/**
* The username of this player.
@ -66,4 +65,13 @@ public class Player {
* The profile actions this player has, null if none.
*/
private final ProfileAction[] profileActions;
/**
* Is this player legacy?
* <p>
* A "Legacy" player is a player that
* has not yet migrated to a Mojang account.
* </p>
*/
private final boolean legacy;
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model;
package cc.restfulmc.api.model;
/**
* Profile actions that can

View File

@ -21,11 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.cache;
package cc.restfulmc.api.model.cache;
import cc.restfulmc.api.model.MinecraftServer;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import lombok.*;
import me.braydon.mc.model.MinecraftServer;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
@ -38,14 +39,14 @@ import java.io.Serializable;
@RedisHash(value = "server", timeToLive = 60L) // 1 minute (in seconds)
public final class CachedMinecraftServer implements Serializable {
/**
* The id of this cached server.
* The id of this cache element.
*/
@Id @JsonIgnore @NonNull private final String id;
/**
* The cached server.
*/
@NonNull private final MinecraftServer value;
@JsonUnwrapped @NonNull private final MinecraftServer value;
/**
* The unix timestamp of when this

View File

@ -21,17 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.cache;
package cc.restfulmc.api.model.cache;
import cc.restfulmc.api.model.Cape;
import cc.restfulmc.api.model.Player;
import cc.restfulmc.api.model.ProfileAction;
import cc.restfulmc.api.model.skin.Skin;
import cc.restfulmc.api.model.token.MojangProfileToken;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
import me.braydon.mc.model.Cape;
import me.braydon.mc.model.Player;
import me.braydon.mc.model.ProfileAction;
import me.braydon.mc.model.skin.Skin;
import me.braydon.mc.model.token.MojangProfileToken;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import java.io.Serializable;
@ -46,16 +48,26 @@ import java.util.UUID;
@ToString(callSuper = true)
@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds)
public final class CachedPlayer extends Player implements Serializable {
/**
* The id of this cache element.
* <p>
* This ID is in the given format:
* player:<uniqueId>-<signed>
* </p>
*/
@Id @NonNull @JsonIgnore private final String cacheId;
/**
* The unix timestamp of when this
* player was cached, -1 if not cached.
*/
private long cached;
public CachedPlayer(@NonNull UUID uniqueId, @NonNull String username, @NonNull Skin skin, Cape cape,
@NonNull MojangProfileToken.ProfileProperty[] properties, ProfileAction[] profileActions,
long cached) {
super(uniqueId, username, skin, cape, properties, profileActions);
public CachedPlayer(@NonNull String cacheId, @NonNull UUID uniqueId, @NonNull String username, @NonNull Skin skin,
Cape cape, @NonNull MojangProfileToken.ProfileProperty[] properties, ProfileAction[] profileActions,
boolean legacy, long cached) {
super(uniqueId, username, skin, cape, properties, profileActions, legacy);
this.cacheId = cacheId;
this.cached = cached;
}
}

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.cache;
package cc.restfulmc.api.model.cache;
import lombok.*;
import org.springframework.data.annotation.Id;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.cache;
package cc.restfulmc.api.model.cache;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.dns;
package cc.restfulmc.api.model.dns;
import lombok.*;

View File

@ -21,10 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.dns.impl;
package cc.restfulmc.api.model.dns.impl;
import cc.restfulmc.api.model.dns.DNSRecord;
import lombok.*;
import me.braydon.mc.model.dns.DNSRecord;
import java.net.InetAddress;

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.dns.impl;
package cc.restfulmc.api.model.dns.impl;
import cc.restfulmc.api.model.dns.DNSRecord;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import me.braydon.mc.model.dns.DNSRecord;
import java.net.InetSocketAddress;

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.response;
package cc.restfulmc.api.model.response;
import lombok.Getter;
import lombok.NonNull;

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.server;
package cc.restfulmc.api.model.server;
import cc.restfulmc.api.model.MinecraftServer;
import cc.restfulmc.api.model.dns.DNSRecord;
import lombok.*;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.dns.DNSRecord;
/**
* A Bedrock edition {@link MinecraftServer}.
@ -54,10 +54,10 @@ public final class BedrockMinecraftServer extends MinecraftServer {
*/
@NonNull private final GameMode gamemode;
private BedrockMinecraftServer(@NonNull String id, @NonNull String hostname, String ip, int port, @NonNull DNSRecord[] records,
@NonNull Edition edition, @NonNull Version version, @NonNull Players players, @NonNull MOTD motd,
@NonNull GameMode gamemode) {
super(hostname, ip, port, records, players, motd);
private BedrockMinecraftServer(@NonNull String id, @NonNull String hostname, String ip, int port, GeoLocation geo,
DNSRecord[] records, @NonNull Edition edition, @NonNull Version version,
@NonNull Players players, @NonNull MOTD motd, @NonNull GameMode gamemode) {
super(hostname, ip, port, records, geo, players, motd);
this.id = id;
this.edition = edition;
this.version = version;
@ -70,20 +70,19 @@ public final class BedrockMinecraftServer extends MinecraftServer {
* @param hostname the hostname of the server
* @param ip the IP address of the server
* @param port the port of the server
* @param records the DNS records of the server
* @param records the DNS records of the server, if any
* @param token the status token
* @return the Bedrock Minecraft server
*/
@NonNull
public static BedrockMinecraftServer create(@NonNull String hostname, String ip, int port,
@NonNull DNSRecord[] records, @NonNull String token) {
public static BedrockMinecraftServer create(@NonNull String hostname, String ip, int port, DNSRecord[] records, @NonNull String token) {
String[] split = token.split(";"); // Split the token
Edition edition = Edition.valueOf(split[0]);
Version version = new Version(Integer.parseInt(split[2]), split[3]);
Players players = new Players(Integer.parseInt(split[4]), Integer.parseInt(split[5]), null);
MOTD motd = MOTD.create(split[1] + "\n" + split[7]);
GameMode gameMode = new GameMode(split[8], Integer.parseInt(split[9]));
return new BedrockMinecraftServer(split[6], hostname, ip, port, records, edition, version, players, motd, gameMode);
GameMode gameMode = new GameMode(split[8], split.length > 9 ? Integer.parseInt(split[9]) : -1);
return new BedrockMinecraftServer(split[6], hostname, ip, port, null, records, edition, version, players, motd, gameMode);
}
/**
@ -129,7 +128,7 @@ public final class BedrockMinecraftServer extends MinecraftServer {
@NonNull private final String name;
/**
* The numeric of this gamemode.
* The numeric of this gamemode, -1 if unknown.
*/
private final int numericId;
}

View File

@ -21,19 +21,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.server;
package cc.restfulmc.api.model.server;
import cc.restfulmc.api.common.JavaMinecraftVersion;
import cc.restfulmc.api.config.AppConfig;
import cc.restfulmc.api.model.MinecraftServer;
import cc.restfulmc.api.model.dns.DNSRecord;
import cc.restfulmc.api.model.token.JavaServerChallengeStatusToken;
import cc.restfulmc.api.model.token.JavaServerStatusToken;
import cc.restfulmc.api.service.MojangService;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import me.braydon.mc.common.JavaMinecraftVersion;
import me.braydon.mc.config.AppConfig;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.dns.DNSRecord;
import me.braydon.mc.model.token.JavaServerStatusToken;
import me.braydon.mc.service.MojangService;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A Java edition {@link MinecraftServer}.
*
@ -51,6 +56,17 @@ public final class JavaMinecraftServer extends MinecraftServer {
*/
private final Favicon favicon;
/**
* The software of this server, present if query is on.
*/
private final String software;
/**
* The plugins on this server, present if
* query is on and plugins are present.
*/
private final Plugin[] plugins;
/**
* The Forge mod information for this server, null if none.
* <p>
@ -67,6 +83,16 @@ public final class JavaMinecraftServer extends MinecraftServer {
*/
private final ForgeData forgeData;
/**
* The main world of this server, present if query is on.
*/
private final String world;
/**
* Does this server support querying?
*/
private final boolean queryEnabled;
/**
* Does this server preview chat?
*
@ -97,14 +123,20 @@ public final class JavaMinecraftServer extends MinecraftServer {
*/
private boolean mojangBanned;
private JavaMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull DNSRecord[] records, @NonNull Version version,
@NonNull Players players, @NonNull MOTD motd, Favicon favicon, ModInfo modInfo, ForgeData forgeData,
boolean previewsChat, boolean enforcesSecureChat, boolean preventsChatReports, boolean mojangBanned) {
super(hostname, ip, port, records, players, motd);
private JavaMinecraftServer(@NonNull String hostname, String ip, int port, DNSRecord[] records, GeoLocation geo,
@NonNull Version version, @NonNull Players players, @NonNull MOTD motd, Favicon favicon,
String software, Plugin[] plugins, ModInfo modInfo, ForgeData forgeData, String world,
boolean queryEnabled, boolean previewsChat, boolean enforcesSecureChat, boolean preventsChatReports,
boolean mojangBanned) {
super(hostname, ip, port, records, geo, players, motd);
this.version = version;
this.favicon = favicon;
this.software = software;
this.plugins = plugins;
this.modInfo = modInfo;
this.forgeData = forgeData;
this.world = world;
this.queryEnabled = queryEnabled;
this.previewsChat = previewsChat;
this.enforcesSecureChat = enforcesSecureChat;
this.preventsChatReports = preventsChatReports;
@ -117,20 +149,35 @@ public final class JavaMinecraftServer extends MinecraftServer {
* @param hostname the hostname of the server
* @param ip the IP address of the server
* @param port the port of the server
* @param records the DNS records of the server
* @param token the status token
* @param records the DNS records of the server, if any
* @param statusToken the status token
* @param challengeStatusToken the challenge status token, null if none
* @return the Java Minecraft server
*/
@NonNull
public static JavaMinecraftServer create(@NonNull String hostname, String ip, int port,
@NonNull DNSRecord[] records, @NonNull JavaServerStatusToken token) {
String motdString = token.getDescription() instanceof String ? (String) token.getDescription() : null;
public static JavaMinecraftServer create(@NonNull String hostname, String ip, int port, DNSRecord[] records,
@NonNull JavaServerStatusToken statusToken, JavaServerChallengeStatusToken challengeStatusToken) {
String motdString = statusToken.getDescription() instanceof String ? (String) statusToken.getDescription() : null;
if (motdString == null) { // Not a string motd, convert from Json
motdString = new TextComponent(ComponentSerializer.parse(AppConfig.GSON.toJson(token.getDescription()))).toLegacyText();
motdString = new TextComponent(ComponentSerializer.parse(AppConfig.GSON.toJson(statusToken.getDescription()))).toLegacyText();
}
return new JavaMinecraftServer(hostname, ip, port, records, token.getVersion().detailedCopy(), Players.create(token.getPlayers()),
MOTD.create(motdString), Favicon.create(token.getFavicon(), hostname), token.getModInfo(), token.getForgeData(),
token.isPreviewsChat(), token.isEnforcesSecureChat(), token.isPreventsChatReports(), false
String software = challengeStatusToken == null ? null : challengeStatusToken.getSoftware(); // The server software
// Get the plugins from the challenge token
Plugin[] plugins = null;
if (challengeStatusToken != null) {
List<Plugin> list = new ArrayList<>();
for (Map.Entry<String, String> entry : challengeStatusToken.getPlugins().entrySet()) {
list.add(new Plugin(entry.getKey(), entry.getValue()));
}
plugins = list.toArray(new Plugin[0]);
}
String world = challengeStatusToken == null ? null : challengeStatusToken.getMap(); // The main server world
return new JavaMinecraftServer(hostname, ip, port, records, null, statusToken.getVersion().detailedCopy(), Players.create(statusToken.getPlayers()),
MOTD.create(motdString), Favicon.create(statusToken.getFavicon(), hostname), software, plugins, statusToken.getModInfo(),
statusToken.getForgeData(), world, challengeStatusToken != null, statusToken.isPreviewsChat(),
statusToken.isEnforcesSecureChat(), statusToken.isPreventsChatReports(), false
);
}
@ -180,6 +227,9 @@ public final class JavaMinecraftServer extends MinecraftServer {
}
}
JavaMinecraftVersion minecraftVersion = JavaMinecraftVersion.byProtocol(protocol);
if (minecraftVersion == JavaMinecraftVersion.UNKNOWN) {
minecraftVersion = null;
}
return new Version(name, platform, protocol, supportedVersions, minecraftVersion == null ? null : minecraftVersion.getName());
}
}
@ -217,6 +267,22 @@ public final class JavaMinecraftServer extends MinecraftServer {
}
}
/**
* A plugin for a server.
*/
@AllArgsConstructor @Getter @ToString
public static class Plugin {
/**
* The name of this plugin.
*/
@NonNull private final String name;
/**
* The version of this plugin.
*/
@NonNull private final String version;
}
/**
* Forge mod information for a server.
* <p>

View File

@ -21,13 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.skin;
package cc.restfulmc.api.model.skin;
import cc.restfulmc.api.common.renderer.SkinRenderer;
import cc.restfulmc.api.common.renderer.impl.BodySkinPartRenderer;
import cc.restfulmc.api.common.renderer.impl.IsometricHeadSkinPartRenderer;
import cc.restfulmc.api.common.renderer.impl.VanillaSkinPartRenderer;
import lombok.*;
import me.braydon.mc.common.renderer.SkinRenderer;
import me.braydon.mc.common.renderer.impl.BodySkinPartRenderer;
import me.braydon.mc.common.renderer.impl.IsometricHeadSkinPartRenderer;
import me.braydon.mc.common.renderer.impl.VanillaSkinPartRenderer;
import java.awt.image.BufferedImage;

View File

@ -21,14 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.skin;
package cc.restfulmc.api.model.skin;
import cc.restfulmc.api.common.ImageUtils;
import cc.restfulmc.api.config.AppConfig;
import cc.restfulmc.api.model.Player;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.JsonObject;
import lombok.*;
import me.braydon.mc.config.AppConfig;
import me.braydon.mc.model.Player;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@ -37,9 +42,9 @@ import java.util.Map;
*
* @author Braydon
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @Setter @Getter @ToString
@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @ToString
public final class Skin {
public static final Skin DEFAULT_STEVE = new Skin("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1", Model.DEFAULT);
public static final Skin DEFAULT_STEVE = create("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1", Model.DEFAULT);
/**
* The texture URL of this skin.
@ -51,10 +56,20 @@ public final class Skin {
*/
@NonNull private final Model model;
/**
* The image data of this skin.
*/
@JsonIgnore private final byte[] skinImage;
/**
* Is this skin legacy?
*/
private final boolean legacy;
/**
* URLs to the parts of this skin.
*/
@NonNull @JsonProperty("parts") private Map<String, String> partUrls = new HashMap<>();
@NonNull @JsonProperty("parts") private final Map<String, String> partUrls;
/**
* Populate the part URLs for this skin.
@ -88,7 +103,22 @@ public final class Skin {
if (metadataJsonObject != null) { // Parse the skin model
model = Model.valueOf(metadataJsonObject.get("model").getAsString().toUpperCase());
}
return new Skin(jsonObject.get("url").getAsString(), model);
return create(jsonObject.get("url").getAsString(), model);
}
/**
* Create a skin from the given URL and model.
*
* @param url the skin url
* @param model the skin model
* @return the constructed skin
*/
@NonNull @SneakyThrows
private static Skin create(@NonNull String url, @NonNull Model model) {
BufferedImage image = ImageIO.read(new URL(url)); // Get the skin image
byte[] bytes = ImageUtils.toByteArray(image); // Convert the image into bytes
boolean legacy = image.getWidth() == 64 && image.getHeight() == 32; // Is the skin legacy?
return new Skin(url, model, bytes, legacy, new HashMap<>());
}
/**

View File

@ -0,0 +1,76 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.model.token;
import cc.restfulmc.api.model.server.JavaMinecraftServer;
import lombok.*;
import java.util.HashMap;
import java.util.Map;
/**
* A token representing the response from
* sending a challenge request via UDP to
* a {@link JavaMinecraftServer} using the
* query.
*
* @author Braydon
*/
@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @ToString
public final class JavaServerChallengeStatusToken {
/**
* The map (world) of this server.
*/
@NonNull private final String map;
/**
* The software of this server.
*/
@NonNull private final String software;
/**
* The plugins of this server.
*/
private final Map<String, String> plugins;
/**
* Create a new challenge token
* from the given raw data.
*
* @param rawData the raw data
* @return the challenge token
*/
@NonNull
public static JavaServerChallengeStatusToken create(@NonNull Map<String, String> rawData) {
String[] splitPlugins = rawData.get("plugins").split(": ");
String software = splitPlugins[0]; // The server software
Map<String, String> plugins = new HashMap<>();
for (String plugin : splitPlugins[1].split("; ")) {
String[] split = plugin.split(" ");
plugins.put(split[0], split[1]);
}
return new JavaServerChallengeStatusToken(rawData.get("map"), software, plugins);
}
}

View File

@ -21,14 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.token;
package cc.restfulmc.api.model.token;
import cc.restfulmc.api.model.server.JavaMinecraftServer;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import me.braydon.mc.model.server.JavaMinecraftServer;
import java.util.UUID;

View File

@ -21,15 +21,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.token;
package cc.restfulmc.api.model.token;
import cc.restfulmc.api.config.AppConfig;
import cc.restfulmc.api.model.Cape;
import cc.restfulmc.api.model.ProfileAction;
import cc.restfulmc.api.model.skin.Skin;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.gson.JsonObject;
import lombok.*;
import me.braydon.mc.config.AppConfig;
import me.braydon.mc.model.Cape;
import me.braydon.mc.model.ProfileAction;
import me.braydon.mc.model.skin.Skin;
import java.util.Base64;
@ -61,6 +61,15 @@ public final class MojangProfileToken {
*/
@NonNull private final ProfileAction[] profileActions;
/**
* Is this profile legacy?
* <p>
* A "Legacy" profile is a profile that
* has not yet migrated to a Mojang account.
* </p>
*/
private final boolean legacy;
/**
* Get the properties of this skin.
*

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.model.token;
package cc.restfulmc.api.model.token;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -21,9 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.repository;
package cc.restfulmc.api.repository;
import me.braydon.mc.model.cache.CachedMinecraftServer;
import cc.restfulmc.api.model.cache.CachedMinecraftServer;
import org.springframework.data.repository.CrudRepository;
/**

View File

@ -21,16 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.repository;
package cc.restfulmc.api.repository;
import me.braydon.mc.model.cache.CachedPlayer;
import cc.restfulmc.api.model.cache.CachedPlayer;
import org.springframework.data.repository.CrudRepository;
import java.util.UUID;
/**
* A cache repository for {@link CachedPlayer}'s.
*
* @author Braydon
*/
public interface PlayerCacheRepository extends CrudRepository<CachedPlayer, UUID> { }
public interface PlayerCacheRepository extends CrudRepository<CachedPlayer, String> { }

View File

@ -21,9 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.repository;
package cc.restfulmc.api.repository;
import me.braydon.mc.model.cache.CachedPlayerName;
import cc.restfulmc.api.model.cache.CachedPlayerName;
import org.springframework.data.repository.CrudRepository;
/**

View File

@ -21,10 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.repository;
package cc.restfulmc.api.repository;
import me.braydon.mc.model.cache.CachedSkinPartTexture;
import me.braydon.mc.model.skin.ISkinPart;
import cc.restfulmc.api.model.cache.CachedSkinPartTexture;
import cc.restfulmc.api.model.skin.ISkinPart;
import org.springframework.data.repository.CrudRepository;
/**

View File

@ -0,0 +1,220 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.service;
import com.maxmind.db.CHMCache;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.exception.AddressNotFoundException;
import com.maxmind.geoip2.model.CityResponse;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.codehaus.plexus.archiver.tar.TarGZipUnArchiver;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
/**
* @author Braydon
*/
@Service @Log4j2(topic = "MaxMind")
public final class MaxMindService {
/**
* The directory to store databases.
*/
private static final File DATABASES_DIRECTORY = new File("databases");
/**
* The endpoint to download database files from.
*/
private static final String DATABASE_DOWNLOAD_ENDPOINT = "https://download.maxmind.com/app/geoip_download?edition_id=%s&license_key=%s&suffix=tar.gz";
@Value("${maxmind.license}")
private String license;
/**
* The currently loaded databases.
*/
private final Map<Database, DatabaseReader> databases = new HashMap<>();
@PostConstruct
public void onInitialize() {
// Load the databases
if (!license.equals("CHANGE_ME")) {
loadDatabases();
}
}
/**
* Load the databases.
*/
@SneakyThrows
private void loadDatabases() {
log.info("Loading databases...");
// Create the directory if it doesn't exist
if (!DATABASES_DIRECTORY.exists()) {
DATABASES_DIRECTORY.mkdirs();
}
// Download missing databases
for (Database database : Database.values()) {
File databaseFile = new File(DATABASES_DIRECTORY, database.getEdition() + ".mmdb");
if (!databaseFile.exists()) { // Doesn't exist, download it
downloadDatabase(database, databaseFile);
}
// Load the database and store it
databases.put(database, new DatabaseReader.Builder(databaseFile)
.withCache(new CHMCache()) // Enable caching
.build()
);
log.info("Loaded database {}", database.getEdition());
}
log.info("Loaded {} database(s)", databases.size());
}
/**
* Lookup a city by the given address.
*
* @param address the address
* @return the city response, null if none
*/
@SneakyThrows
public CityResponse lookupCity(@NonNull InetAddress address) {
DatabaseReader database = getDatabase(Database.CITY);
try {
return database == null ? null : database.city(address);
} catch (AddressNotFoundException ignored) {
// Safely ignore this and return null instead
return null;
}
}
/**
* Download the required files
* for the given database.
*
* @param database the database to download
* @param databaseFile the file for the database
*/
@SneakyThrows
private void downloadDatabase(@NonNull Database database, @NonNull File databaseFile) {
File downloadedFile = new File(DATABASES_DIRECTORY, database.getEdition() + ".tar.gz"); // The downloaded file
// Download the database if required
if (!downloadedFile.exists()) {
log.info("Downloading database {}...", database.getEdition());
long before = System.currentTimeMillis();
try (
BufferedInputStream inputStream = new BufferedInputStream(new URL(DATABASE_DOWNLOAD_ENDPOINT.formatted(database.getEdition(), license)).openStream());
FileOutputStream fileOutputStream = new FileOutputStream(downloadedFile)
) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
}
log.info("Downloaded database {} in {}ms", database.getEdition(), System.currentTimeMillis() - before);
}
// Extract the database once downloaded
log.info("Extracting database {}...", database.getEdition());
TarGZipUnArchiver archiver = new TarGZipUnArchiver();
archiver.setSourceFile(downloadedFile);
archiver.setDestDirectory(DATABASES_DIRECTORY);
archiver.extract();
log.info("Extracted database {}", database.getEdition());
// Locate the database file in the extracted directory
File[] files = DATABASES_DIRECTORY.listFiles();
assert files != null; // Ensure files is present
dirLoop: for (File directory : files) {
if (!directory.isDirectory() || !directory.getName().startsWith(database.getEdition())) {
continue;
}
File[] downloadedFiles = directory.listFiles();
assert downloadedFiles != null; // Ensures downloaded files is present
// Find the file for the database, move it to the
// correct directory, and delete the downloaded contents
for (File file : downloadedFiles) {
if (file.isFile() && file.getName().equals(databaseFile.getName())) {
Files.move(file.toPath(), databaseFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// Delete the downloaded contents
FileUtils.deleteDirectory(directory);
FileUtils.deleteQuietly(downloadedFile);
break dirLoop; // We're done here
}
}
}
}
/**
* Get the reader for the given database.
*
* @param database the database to get
* @return the database reader, null if none
*/
public DatabaseReader getDatabase(@NonNull Database database) {
return databases.get(database);
}
/**
* Cleanup when the app is destroyed.
*/
@PreDestroy @SneakyThrows
public void cleanup() {
for (DatabaseReader database : databases.values()) {
database.close();
}
databases.clear();
}
/**
* A database for MaxMind.
*/
@AllArgsConstructor @Getter @ToString
public enum Database {
CITY("GeoLite2-City");
/**
* The edition of this database.
*/
@NonNull private final String edition;
}
}

View File

@ -21,50 +21,52 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.service;
package cc.restfulmc.api.service;
import cc.restfulmc.api.common.*;
import cc.restfulmc.api.common.web.JsonWebException;
import cc.restfulmc.api.common.web.JsonWebRequest;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.exception.impl.MojangRateLimitException;
import cc.restfulmc.api.exception.impl.ResourceNotFoundException;
import cc.restfulmc.api.model.MinecraftServer;
import cc.restfulmc.api.model.Player;
import cc.restfulmc.api.model.ProfileAction;
import cc.restfulmc.api.model.cache.CachedMinecraftServer;
import cc.restfulmc.api.model.cache.CachedPlayer;
import cc.restfulmc.api.model.cache.CachedPlayerName;
import cc.restfulmc.api.model.cache.CachedSkinPartTexture;
import cc.restfulmc.api.model.dns.DNSRecord;
import cc.restfulmc.api.model.dns.impl.ARecord;
import cc.restfulmc.api.model.dns.impl.SRVRecord;
import cc.restfulmc.api.model.server.JavaMinecraftServer;
import cc.restfulmc.api.model.skin.ISkinPart;
import cc.restfulmc.api.model.skin.Skin;
import cc.restfulmc.api.model.token.MojangProfileToken;
import cc.restfulmc.api.model.token.MojangUsernameToUUIDToken;
import cc.restfulmc.api.repository.MinecraftServerCacheRepository;
import cc.restfulmc.api.repository.PlayerCacheRepository;
import cc.restfulmc.api.repository.PlayerNameCacheRepository;
import cc.restfulmc.api.repository.SkinPartTextureCacheRepository;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.hash.Hashing;
import com.maxmind.geoip2.model.CityResponse;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Getter;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.common.*;
import me.braydon.mc.common.web.JsonWebException;
import me.braydon.mc.common.web.JsonWebRequest;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.MojangRateLimitException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.Player;
import me.braydon.mc.model.ProfileAction;
import me.braydon.mc.model.cache.CachedMinecraftServer;
import me.braydon.mc.model.cache.CachedPlayer;
import me.braydon.mc.model.cache.CachedPlayerName;
import me.braydon.mc.model.cache.CachedSkinPartTexture;
import me.braydon.mc.model.dns.DNSRecord;
import me.braydon.mc.model.dns.impl.ARecord;
import me.braydon.mc.model.dns.impl.SRVRecord;
import me.braydon.mc.model.server.JavaMinecraftServer;
import me.braydon.mc.model.skin.ISkinPart;
import me.braydon.mc.model.skin.Skin;
import me.braydon.mc.model.token.MojangProfileToken;
import me.braydon.mc.model.token.MojangUsernameToUUIDToken;
import me.braydon.mc.repository.MinecraftServerCacheRepository;
import me.braydon.mc.repository.PlayerCacheRepository;
import me.braydon.mc.repository.PlayerNameCacheRepository;
import me.braydon.mc.repository.SkinPartTextureCacheRepository;
import net.jodah.expiringmap.ExpirationPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@ -79,11 +81,9 @@ import java.util.concurrent.TimeUnit;
@Service
@Log4j2(topic = "Mojang Service")
public final class MojangService {
private static final String SESSION_SERVER_ENDPOINT = "https://sessionserver.mojang.com";
private static final String API_ENDPOINT = "https://api.mojang.com";
private static final String UUID_TO_PROFILE = SESSION_SERVER_ENDPOINT + "/session/minecraft/profile/%s";
private static final String USERNAME_TO_UUID = API_ENDPOINT + "/users/profiles/minecraft/%s";
private static final String FETCH_BLOCKED_SERVERS = SESSION_SERVER_ENDPOINT + "/blockedservers";
private static final String UUID_TO_PROFILE = MojangServer.SESSION.getEndpoint() + "/session/minecraft/profile/%s";
private static final String USERNAME_TO_UUID = MojangServer.API.getEndpoint() + "/users/profiles/minecraft/%s";
private static final String FETCH_BLOCKED_SERVERS = MojangServer.SESSION.getEndpoint() + "/blockedservers";
private static final int DEFAULT_PART_TEXTURE_SIZE = 128;
private static final int MAX_PART_TEXTURE_SIZE = 512;
@ -93,6 +93,11 @@ public final class MojangService {
private static final Splitter DOT_SPLITTER = Splitter.on('.');
private static final Joiner DOT_JOINER = Joiner.on('.');
/**
* The MaxMind service to use for Geo lookups.
*/
@NonNull private final MaxMindService maxMindService;
/**
* The cache repository for {@link Player}'s by their username.
*/
@ -104,7 +109,7 @@ public final class MojangService {
@NonNull private final PlayerCacheRepository playerCache;
/**
* The cache repository for {@link Skin.Part}'s.
* The cache repository for {@link ISkinPart}'s.
*/
@NonNull private final SkinPartTextureCacheRepository skinPartTextureCache;
@ -113,6 +118,11 @@ public final class MojangService {
*/
@NonNull private final MinecraftServerCacheRepository minecraftServerCache;
/**
* Mapped statuses for {@link MojangServer}'s.
*/
@Getter private final Map<MojangServer, MojangServer.Status> mojangServerStatuses = Collections.synchronizedMap(new HashMap<>());
/**
* A list of banned server hashes provided by Mojang.
* <p>
@ -132,8 +142,9 @@ public final class MojangService {
private final ExpiringSet<String> blockedServersCache = new ExpiringSet<>(ExpirationPolicy.CREATED, 10L, TimeUnit.MINUTES);
@Autowired
public MojangService(@NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache,
public MojangService(@NonNull MaxMindService maxMindService, @NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache,
@NonNull SkinPartTextureCacheRepository skinPartTextureCache, @NonNull MinecraftServerCacheRepository minecraftServerCache) {
this.maxMindService = maxMindService;
this.playerNameCache = playerNameCache;
this.playerCache = playerCache;
this.skinPartTextureCache = skinPartTextureCache;
@ -142,14 +153,23 @@ public final class MojangService {
@PostConstruct
public void onInitialize() {
// Schedule a task to fetch statuses
// of Mojang servers every few minutes
new Timer().scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
fetchMojangServerStatuses();
}
}, 0L, 60L * 3L * 1000L);
// Schedule a task to fetch blocked
// servers from Mojang every 15 minutes.
// servers from Mojang every hour.
new Timer().scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
fetchBlockedServers();
}
}, 0L, 60L * 15L * 1000L);
}, 0L, 60L * 60L * 1000L);
}
/**
@ -216,7 +236,7 @@ public final class MojangService {
Skin skin = null; // The target skin to get the skin part of
long before = System.currentTimeMillis();
try {
CachedPlayer player = getPlayer(query); // Retrieve the player
CachedPlayer player = getPlayer(query, false); // Retrieve the player
skin = player.getSkin(); // Use the player's skin
} catch (Exception ignored) {
// Simply ignore, and fallback to the default skin
@ -231,16 +251,10 @@ public final class MojangService {
BufferedImage texture = part.render(skin, overlays, size); // Render the skin part
log.info("Render of skin part took {}ms: {}", System.currentTimeMillis() - before, id);
// Convert BufferedImage to byte array
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ImageIO.write(texture, "png", outputStream);
outputStream.flush();
byte[] bytes = outputStream.toByteArray();
skinPartTextureCache.save(new CachedSkinPartTexture(id, bytes)); // Cache the texture
log.info("Cached skin part texture: {}", id);
return bytes;
}
byte[] bytes = ImageUtils.toByteArray(texture); // Convert the image into a byte array
skinPartTextureCache.save(new CachedSkinPartTexture(id, bytes)); // Cache the texture
log.info("Cached skin part texture: {}", id);
return bytes;
}
/**
@ -252,14 +266,15 @@ public final class MojangService {
* and then return the response.
* </p>
*
* @param query the query to search for the player by
* @param query the query to search for the player by
* @param signed whether the profile is signed
* @return the player
* @throws BadRequestException if the UUID or username is invalid
* @throws ResourceNotFoundException if the player is not found
* @throws MojangRateLimitException if the Mojang API rate limit is reached
*/
@NonNull
public CachedPlayer getPlayer(@NonNull String query) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
public CachedPlayer getPlayer(@NonNull String query, boolean signed) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
log.info("Requesting player with query: {}", query);
UUID uuid; // The player UUID to lookup
@ -278,10 +293,11 @@ public final class MojangService {
uuid = usernameToUUID(query);
log.info("Found UUID for username {}: {}", query, uuid);
}
String cacheId = "%s-%s".formatted(uuid, signed); // The cache id of the player
// Check the cache for the player
// and return it if it's present
Optional<CachedPlayer> cached = playerCache.findById(uuid);
Optional<CachedPlayer> cached = playerCache.findById(cacheId);
if (cached.isPresent()) { // Respond with the cache if present
log.info("Found player in cache: {}", uuid);
return cached.get();
@ -291,17 +307,16 @@ public final class MojangService {
// the player profile by their UUID
try {
log.info("Retrieving player profile for UUID: {}", uuid);
MojangProfileToken token = JsonWebRequest.makeRequest(
UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET
).execute(MojangProfileToken.class);
String endpoint = UUID_TO_PROFILE.formatted(uuid) + (signed ? "?unsigned=false" : "");
MojangProfileToken token = JsonWebRequest.makeRequest(endpoint, HttpMethod.GET).execute(MojangProfileToken.class);
MojangProfileToken.SkinProperties skinProperties = token.getSkinProperties(); // Get the skin and cape
ProfileAction[] profileActions = token.getProfileActions();
// Build our player model, cache it, and then return it
CachedPlayer player = new CachedPlayer(uuid, token.getName(),
CachedPlayer player = new CachedPlayer(cacheId, uuid, token.getName(),
skinProperties.getSkin() == null ? Skin.DEFAULT_STEVE : skinProperties.getSkin(),
skinProperties.getCape(), token.getProperties(), profileActions.length == 0 ? null : profileActions,
System.currentTimeMillis()
token.isLegacy(), System.currentTimeMillis()
);
// Store in the cache
playerCache.save(player);
@ -341,10 +356,13 @@ public final class MojangService {
} catch (BadRequestException | ResourceNotFoundException ignored) {
// Safely ignore these, we will use the default server icon
}
if (icon == null) { // Use the default server icon
icon = DEFAULT_SERVER_ICON;
try {
assert icon != null;
return Base64.getDecoder().decode(icon); // Return the decoded favicon
} catch (Exception ex) { // Use the default server icon
log.error("Failed getting server favicon for %s:".formatted(hostname), ex);
return Base64.getDecoder().decode(DEFAULT_SERVER_ICON);
}
return Base64.getDecoder().decode(icon); // Return the decoded favicon
}
/**
@ -423,9 +441,10 @@ public final class MojangService {
throw new BadRequestException("Invalid port defined");
}
}
String cacheKey = "%s-%s".formatted(platform.name(), lookupHostname.replace(":", "-"));
// Check the cache for the server
Optional<CachedMinecraftServer> cached = minecraftServerCache.findById("%s-%s".formatted(platform.name(), lookupHostname));
Optional<CachedMinecraftServer> cached = minecraftServerCache.findById(cacheKey);
if (cached.isPresent()) { // Respond with the cache if present
log.info("Found server in cache: {}", hostname);
return cached.get();
@ -447,13 +466,28 @@ public final class MojangService {
log.info("Resolved hostname: {} -> {}", hostname, ip);
}
// Attempt to perform a Geo lookup on the server
CityResponse geo = null; // The server's Geo location
try {
log.info("Looking up Geo location data for {}...", ip);
InetAddress address = InetAddress.getByName(ip == null ? hostname : ip);
geo = maxMindService.lookupCity(address);
} catch (Exception ex) {
log.error("Failed looking up Geo location data for %s:".formatted(ip), ex);
}
// Build our server model, cache it, and then return it
MinecraftServer response = platform.getPinger().ping(hostname, ip, port, records.toArray(new DNSRecord[0])); // Ping the server and await a response
if (response == null) { // No response from ping
throw new ResourceNotFoundException("Server didn't respond to ping");
}
if (geo != null) { // Update Geo location data in the server if present
response.setGeo(MinecraftServer.GeoLocation.create(geo));
}
CachedMinecraftServer minecraftServer = new CachedMinecraftServer(
platform.name() + "-" + lookupHostname, response, System.currentTimeMillis()
cacheKey, response, System.currentTimeMillis()
);
// Get the blocked status of the Java server
@ -508,6 +542,20 @@ public final class MojangService {
}
}
/**
* Fetch the statuses of {@link MojangServer}'s.
*/
@SneakyThrows
private void fetchMojangServerStatuses() {
log.info("Checking Mojang server statuses...");
Arrays.stream(MojangServer.values()).parallel().forEach(server -> {
log.info("Pinging {}...", server.getEndpoint());
MojangServer.Status status = server.getStatus(); // Retrieve the server status
log.info("Retrieved status of {}: {}", server.getEndpoint(), status.name());
mojangServerStatuses.put(server, status); // Cache the server status
});
}
/**
* Fetch a list of blocked servers from Mojang.
*/
@ -539,10 +587,20 @@ public final class MojangService {
return true;
}
String hashed = Hashing.sha1().hashBytes(hostname.toLowerCase().getBytes(StandardCharsets.ISO_8859_1)).toString();
boolean blocked = bannedServerHashes.contains(hashed); // Is the hostname blocked?
boolean blocked = bannedServerHashes != null && (bannedServerHashes.contains(hashed)); // Is the hostname blocked?
if (blocked) { // Cache the blocked hostname
blockedServersCache.add(hostname);
}
return blocked;
}
/**
* Cleanup when the app is destroyed.
*/
@PreDestroy
public void cleanup() {
mojangServerStatuses.clear();
bannedServerHashes.clear();
blockedServersCache.clear();
}
}

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.service.pinger;
package cc.restfulmc.api.service.pinger;
import cc.restfulmc.api.model.MinecraftServer;
import cc.restfulmc.api.model.dns.DNSRecord;
import lombok.NonNull;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.dns.DNSRecord;
/**
* A {@link MinecraftServerPinger} is

View File

@ -21,17 +21,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.service.pinger.impl;
package cc.restfulmc.api.service.pinger.impl;
import cc.restfulmc.api.common.packet.impl.bedrock.BedrockUnconnectedPingPacket;
import cc.restfulmc.api.common.packet.impl.bedrock.BedrockUnconnectedPongPacket;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.exception.impl.ResourceNotFoundException;
import cc.restfulmc.api.model.dns.DNSRecord;
import cc.restfulmc.api.model.server.BedrockMinecraftServer;
import cc.restfulmc.api.service.pinger.MinecraftServerPinger;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPing;
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPong;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.dns.DNSRecord;
import me.braydon.mc.model.server.BedrockMinecraftServer;
import me.braydon.mc.service.pinger.MinecraftServerPinger;
import java.io.IOException;
import java.net.DatagramSocket;
@ -60,7 +60,7 @@ public final class BedrockMinecraftServerPinger implements MinecraftServerPinger
*/
@Override
public BedrockMinecraftServer ping(@NonNull String hostname, String ip, int port, @NonNull DNSRecord[] records) {
log.info("Pinging {}:{}...", hostname, port);
log.info("Opening UDP connection to {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
// Open a socket connection to the server
@ -69,13 +69,13 @@ public final class BedrockMinecraftServerPinger implements MinecraftServerPinger
socket.connect(new InetSocketAddress(hostname, port));
long ping = System.currentTimeMillis() - before; // Calculate the ping
log.info("Pinged {}:{} in {}ms", hostname, port, ping);
log.info("UDP Connection to {}:{} opened. Ping: {}ms", hostname, port, ping);
// Send the unconnected ping packet
new BedrockPacketUnconnectedPing().process(socket);
new BedrockUnconnectedPingPacket().process(socket);
// Handle the received unconnected pong packet
BedrockPacketUnconnectedPong unconnectedPong = new BedrockPacketUnconnectedPong();
BedrockUnconnectedPongPacket unconnectedPong = new BedrockUnconnectedPongPacket();
unconnectedPong.process(socket);
String response = unconnectedPong.getResponse();
if (response == null) { // No pong response

View File

@ -0,0 +1,177 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.service.pinger.impl;
import cc.restfulmc.api.common.JavaMinecraftVersion;
import cc.restfulmc.api.common.packet.impl.java.tcp.JavaHandshakingInSetProtocolPacket;
import cc.restfulmc.api.common.packet.impl.java.tcp.JavaStatusInStartPacket;
import cc.restfulmc.api.common.packet.impl.java.udp.JavaQueryFullStatRequestPacket;
import cc.restfulmc.api.common.packet.impl.java.udp.JavaQueryFullStatResponsePacket;
import cc.restfulmc.api.common.packet.impl.java.udp.JavaQueryHandshakeRequestPacket;
import cc.restfulmc.api.common.packet.impl.java.udp.JavaQueryHandshakeResponsePacket;
import cc.restfulmc.api.config.AppConfig;
import cc.restfulmc.api.exception.impl.BadRequestException;
import cc.restfulmc.api.exception.impl.ResourceNotFoundException;
import cc.restfulmc.api.model.dns.DNSRecord;
import cc.restfulmc.api.model.server.JavaMinecraftServer;
import cc.restfulmc.api.model.token.JavaServerChallengeStatusToken;
import cc.restfulmc.api.model.token.JavaServerStatusToken;
import cc.restfulmc.api.service.pinger.MinecraftServerPinger;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
/**
* The {@link MinecraftServerPinger} for pinging
* {@link JavaMinecraftServer}'s over TCP/UDP.
*
* @author Braydon
*/
@Log4j2(topic = "Java MC Server Pinger")
public final class JavaMinecraftServerPinger implements MinecraftServerPinger<JavaMinecraftServer> {
private static final int TIMEOUT = 3000; // The timeout for the socket
/**
* Ping the server with the given hostname and port.
*
* @param hostname the hostname of the server
* @param ip the ip of the server, null if unresolved
* @param port the port of the server
* @param records the DNS records of the server
* @return the server that was pinged
*/
@Override
public JavaMinecraftServer ping(@NonNull String hostname, String ip, int port, @NonNull DNSRecord[] records) {
log.info("Pinging {}:{}...", hostname, port);
try {
// Ping the server and retrieve both the status token, and the challenge status token
JavaServerStatusToken statusToken = retrieveStatusToken(hostname, port);
JavaServerChallengeStatusToken challengeStatusToken = null;
try {
challengeStatusToken = retrieveChallengeStatusToken(hostname, port);
} catch (Exception ex) {
// An exception will be raised if querying
// is disabled on the server. If the exception
// is not caused by querying being disabled, we
// want to log the error.
if (!(ex instanceof IOException)) {
log.error("Failed retrieving challenge status token for %s:%s:".formatted(hostname, port), ex);
}
}
// Return the server
return JavaMinecraftServer.create(hostname, ip, port, records, statusToken, challengeStatusToken);
} catch (IOException ex) {
if (ex instanceof UnknownHostException) {
throw new BadRequestException("Unknown hostname: %s".formatted(hostname));
} else if (ex instanceof ConnectException || ex instanceof SocketTimeoutException) {
throw new ResourceNotFoundException(ex);
}
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);
}
return null;
}
/**
* Ping a server and retrieve its response.
*
* @param hostname the hostname to ping
* @param port the port to ping
* @return the status token
* @throws IOException if an I/O error occurs
* @throws ResourceNotFoundException if the server didn't respond
*/
@NonNull
private JavaServerStatusToken retrieveStatusToken(@NonNull String hostname, int port) throws IOException, ResourceNotFoundException {
log.info("Opening TCP connection to {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
// Open a socket connection to the server
try (Socket socket = new Socket()) {
socket.setTcpNoDelay(true);
socket.connect(new InetSocketAddress(hostname, port), TIMEOUT);
long ping = System.currentTimeMillis() - before; // Calculate the ping
log.info("TCP Connection to {}:{} opened. Ping: {}ms", hostname, port, ping);
// Begin packet transaction
try (DataInputStream inputStream = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) {
// Begin handshaking with the server
new JavaHandshakingInSetProtocolPacket(hostname, port, JavaMinecraftVersion.getMinimumVersion().getProtocol())
.process(inputStream, outputStream);
// Send the status request to the server, and await back the response
JavaStatusInStartPacket packetStatusInStart = new JavaStatusInStartPacket();
packetStatusInStart.process(inputStream, outputStream);
String response = packetStatusInStart.getResponse();
if (response == null) { // No response
throw new ResourceNotFoundException("Server didn't respond to status request");
}
return AppConfig.GSON.fromJson(response, JavaServerStatusToken.class);
}
}
}
/**
* Ping a server and retrieve its challenge status token.
*
* @param hostname the hostname to ping
* @param port the port to ping
* @return the challenge token
* @throws IOException if an I/O error occurs
*/
@NonNull
private JavaServerChallengeStatusToken retrieveChallengeStatusToken(@NonNull String hostname, int port) throws IOException {
log.info("Opening UDP connection to {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
// Open a socket connection to the server
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(500);
socket.connect(new InetSocketAddress(hostname, port));
long ping = System.currentTimeMillis() - before; // Calculate the ping
log.info("UDP Connection to {}:{} opened. Ping: {}ms", hostname, port, ping);
// Begin handshaking with the server
new JavaQueryHandshakeRequestPacket().process(socket);
JavaQueryHandshakeResponsePacket handshakeResponse = new JavaQueryHandshakeResponsePacket();
handshakeResponse.process(socket);
// Send the full stats request to the server, and await back the response
new JavaQueryFullStatRequestPacket(handshakeResponse.getResponse()).process(socket);
JavaQueryFullStatResponsePacket fullStatResponse = new JavaQueryFullStatResponsePacket();
fullStatResponse.process(socket);
// Return the challenge token
return JavaServerChallengeStatusToken.create(fullStatResponse.getResponse());
}
}
}

View File

@ -11,6 +11,16 @@ logging:
file:
path: "./logs"
# Sentry Configuration
sentry:
dsn: https://87487c1562d043f79c09e77e4bc359b8@sentry.rainnny.club/2
tracesSampleRate: 1.0
# MaxMind Configuration
# Used for IP Geo location
maxmind:
license: "CHANGE_ME"
# Spring Configuration
spring:
data:

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -4,11 +4,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="A simple, yet useful RESTful API for Minecraft utilizing Springboot.">
<meta name="theme-color" content="#3A34EB">
<meta name="theme-color" content="#3C8627">
<title>RESTfulMC</title>
</head>
<body>
<h1>RESTfulMC</h1>
<h1>RESTfulMC API</h1>
<p>A simple, yet useful RESTful API for Minecraft utilizing Springboot.</p>
<p>View the source <a href="https://git.rainnny.club/Rainnny/RESTfulMC">here</a>!</p>
</body>

View File

@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.test.config;
package cc.restfulmc.api.test.config;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

View File

@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (c) 2024 Braydon (Rainnny).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.restfulmc.api.test.controller;
import cc.restfulmc.api.controller.MojangController;
import cc.restfulmc.api.test.config.TestRedisConfig;
import lombok.NonNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for the {@link MojangController}.
*
* @author Braydon
*/
@SpringBootTest(classes = TestRedisConfig.class)
@AutoConfigureMockMvc
public final class MojangControllerTests {
/**
* The {@link MockMvc} instance to use for testing.
*/
@NonNull private final MockMvc mockMvc;
@Autowired
public MojangControllerTests(@NonNull MockMvc mockMvc) {
this.mockMvc = mockMvc;
}
/**
* Run a test to ensure retrieving
* the status of Mojang servers is
* successful.
*
* @throws Exception if the test fails
*/
@Test
void ensureServerStatusCheckSuccess() throws Exception {
mockMvc.perform(get("/mojang/status")
.accept(MediaType.APPLICATION_JSON) // Accept JSON
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
).andExpect(status().isOk()) // Expect 200 (OK)
.andReturn();
}
}

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.test.controller;
package cc.restfulmc.api.test.controller;
import cc.restfulmc.api.controller.PlayerController;
import cc.restfulmc.api.test.config.TestRedisConfig;
import lombok.NonNull;
import me.braydon.mc.controller.PlayerController;
import me.braydon.mc.test.config.TestRedisConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;

View File

@ -21,11 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.braydon.mc.test.controller;
package cc.restfulmc.api.test.controller;
import cc.restfulmc.api.controller.ServerController;
import cc.restfulmc.api.test.config.TestRedisConfig;
import lombok.NonNull;
import me.braydon.mc.controller.ServerController;
import me.braydon.mc.test.config.TestRedisConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@ -67,7 +67,7 @@ public final class ServerControllerTests {
.accept(MediaType.APPLICATION_JSON) // Accept JSON
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
).andExpect(status().isOk()) // Expect 200 (OK)
.andExpect(jsonPath("$.value.hostname") // Expect the server's resolved hostname
.andExpect(jsonPath("$.hostname") // Expect the server's resolved hostname
.value("mc.hypixel.net")
).andReturn();
}
@ -80,12 +80,12 @@ public final class ServerControllerTests {
*/
@Test
void ensureBedrockServerLookupSuccess() throws Exception {
mockMvc.perform(get("/server/bedrock/gateway.wildnetwork.net")
mockMvc.perform(get("/server/bedrock/wildprison.bedrock.minehut.gg")
.accept(MediaType.APPLICATION_JSON) // Accept JSON
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
).andExpect(status().isOk()) // Expect 200 (OK)
.andExpect(jsonPath("$.value.hostname") // Expect the server's resolved hostname
.value("gateway.wildnetwork.net")
.andExpect(jsonPath("$.hostname") // Expect the server's resolved hostname
.value("wildprison.bedrock.minehut.gg")
).andReturn();
}

4
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,4 @@
# Contributing
Made changes or improvements to RESTfulMC? Consider opening a [pull request](https://git.rainnny.club/Rainnny/RESTfulMC/pulls) to merge your changes into the project. Remember to stick with project conventions!
This project follows [Semantic Versioning](https://semver.org).

29
DemoPlugin/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
*.class
*.log
*.ctxt
.mtj.tmp/
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
hs_err_pid*
replay_pid*
.idea
cmake-build-*/
.idea/**/mongoSettings.xml
*.iws
out/
build/
jars/
target/
.idea_modules/
atlassian-ide-plugin.xml
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
git.properties
pom.xml.versionsBackup

5
DemoPlugin/README.md Normal file
View File

@ -0,0 +1,5 @@
# RESTfulMC Demo Plugin
This plugin provides an example of what can be returned from the API.
## Demo Server
Ping `demo.restfulmc.cc` on Java or Bedrock, or better yet, view it [here](https://restfulmc.cc/server/java/demo.restfulmc.cc)

85
DemoPlugin/pom.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--Project Details-->
<groupId>me.braydon</groupId>
<artifactId>DemoPlugin</artifactId>
<version>1.0.0</version>
<!-- Properties -->
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Used for compiling the source code with the proper Java version -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- Enable incremental builds, this is reversed due to -->
<!-- a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209 -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<!-- Handles shading of dependencies in the final output jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<!-- Filter the resources dir for placeholders -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<!-- Repositories -->
<repositories>
<!-- Used by Velocity -->
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<!-- Dependencies -->
<dependencies>
<!-- Server Jars -->
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.3.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
package cc.restfulmc.demo;
import cc.restfulmc.demo.listener.ServerPingListener;
import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
/**
* @author Braydon
*/
@Plugin(id = "demoplugin", name = "DemoPlugin", version = "1.0.0")
public final class DemoPlugin {
private final ProxyServer server;
@Inject
public DemoPlugin(ProxyServer server) {
this.server = server;
}
@Subscribe
public void onProxyInitialize(ProxyInitializeEvent event) {
server.getEventManager().register(this, new ServerPingListener());
}
@Subscribe(order = PostOrder.FIRST)
public void onLogin(PreLoginEvent event) {
event.setResult(PreLoginEvent.PreLoginComponentResult.denied(Component.text("§cYou can't join a demo server :(")));
}
}

View File

@ -0,0 +1,61 @@
package cc.restfulmc.demo.listener;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.ModInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author Braydon
*/
public final class ServerPingListener {
private static final String[] MESSAGES = new String[] {
"wow omg so cool!",
"Hello World!",
"Rainnny was here",
"Star on GitHub!",
"restfulmc.cc",
"discord.restfulmc.cc"
};
private static final String[] PLAYERS = new String[] {
"Rainnny", "Notch", "jeb_", "hypixel", "Dinnerbone", "C418", "g", "hey"
};
@Subscribe
public void onProxyPing(ProxyPingEvent event) {
ServerPing ping = event.getPing(); // Get the ping response
ThreadLocalRandom random = ThreadLocalRandom.current();
// Update the version
ServerPing.Version version = new ServerPing.Version(ping.getVersion().getProtocol(), "RESTfulMC Demo");
// Update the player count
List<ServerPing.SamplePlayer> playerSamples = new ArrayList<>();
for (int i = 0; i < 3; i++) {
playerSamples.add(new ServerPing.SamplePlayer(PLAYERS[random.nextInt(PLAYERS.length)], UUID.randomUUID()));
}
ServerPing.Players players = new ServerPing.Players(random.nextInt(300, 25000), 30000, playerSamples);
TextComponent motd = Component.text(String.join("\n",
"§f §2§lRESTfulMC §7Demo Server",
"§7 " + MESSAGES[random.nextInt(MESSAGES.length)]
));
// Update the mod info
ModInfo modInfo = new ModInfo(ModInfo.DEFAULT.getType(), Arrays.asList(
new ModInfo.Mod("bob", "1.0"),
new ModInfo.Mod("ross", "1.0")
));
// Set the ping response
event.setPing(new ServerPing(version, players, motd, ping.getFavicon().orElse(null), modInfo));
}
}

28
DiscordBot/.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
*.class
*.log
*.ctxt
.mtj.tmp/
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
hs_err_pid*
replay_pid*
.idea
cmake-build-*/
.idea/**/mongoSettings.xml
*.iws
out/
build/
target/
.idea_modules/
atlassian-ide-plugin.xml
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
git.properties
pom.xml.versionsBackup

13
DiscordBot/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM maven:3.8.5-openjdk-17-slim
# Set the working directory
WORKDIR /home/container
# Copy project files
COPY . .
# Build the app
RUN mvn clean package -q -T4C
# Start the app
CMD ["java", "-jar", "target/DiscordBot.jar"]

2
DiscordBot/README.md Normal file
View File

@ -0,0 +1,2 @@
# RESTfulMC Discord Bot
This Discord bot allows you to interact with the API directly from Discord. Want to try it out? Join the [RESTfulMC Discord](https://discord.restfulmc.cc)

122
DiscordBot/pom.xml Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--Project Details-->
<groupId>cc.restfulmc</groupId>
<artifactId>DiscordBot</artifactId>
<version>1.0.0</version>
<!-- Properties -->
<properties>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.1.0-alpha1</slf4j.version>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Used for compiling the source code with the proper Java version -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- Enable incremental builds, this is reversed due to -->
<!-- a bug as seen in https://issues.apache.org/jira/browse/MCOMPILER-209 -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<!-- Handles shading of dependencies in the final output jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Specify the apps main class -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>cc.restfulmc.bot.DiscordBot</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<!-- Repositories -->
<repositories>
<repository>
<id>rainnny-repo-public</id>
<url>https://maven.rainnny.club/public</url>
</repository>
</repositories>
<!-- Dependencies -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
<!-- Libraries -->
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.0.0-beta.23</version>
<exclusions>
<exclusion>
<groupId>club.minnced</groupId>
<artifactId>opus-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cc.restfulmc</groupId>
<artifactId>Java-SDK</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,62 @@
package cc.restfulmc.bot;
import cc.restfulmc.bot.command.CommandManager;
import cc.restfulmc.sdk.client.ClientConfig;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import lombok.Getter;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.SelfUser;
/**
* @author Braydon
*/
@Slf4j(topic = "RESTfulMC Bot") @Getter
public final class DiscordBot {
/**
* The JDA bot instance.
*/
private JDA jda;
@SneakyThrows
public DiscordBot() {
String token = System.getenv("BOT_TOKEN");
if (token == null) { // Missing BOT_TOKEN
throw new NullPointerException("Missing BOT_TOKEN environment variable");
}
jda = JDABuilder.createLight(token)
.setActivity(Activity.watching("Minecraft servers"))
.build();
jda.awaitReady(); // Wait for JDA to become ready
// Setup the API SDK
RESTfulMCClient apiClient = new RESTfulMCClient(ClientConfig.defaultConfig());
// Commands
new CommandManager(this, apiClient);
SelfUser self = jda.getSelfUser();
log.info("Logged in as bot {} ({})", self.getAsTag(), self.getId());
// Add a cleanup hook to cleanup the bot when the JVM shuts down
Runtime.getRuntime().addShutdownHook(new Thread(this::cleanup));
}
/**
* Cleanup the bot.
*/
public void cleanup() {
log.info("Cleaning up...");
jda.shutdown();
jda = null;
log.info("Goodbye!");
}
public static void main(@NonNull String[] args) {
new DiscordBot();
}
}

View File

@ -0,0 +1,61 @@
package cc.restfulmc.bot.command;
import cc.restfulmc.bot.DiscordBot;
import cc.restfulmc.bot.command.impl.PlayerCommand;
import cc.restfulmc.bot.command.impl.ServerCommand;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import lombok.NonNull;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Braydon
*/
public final class CommandManager extends ListenerAdapter {
/**
* The registered slash commands.
*/
private final List<SlashCommand> commands = Collections.synchronizedList(new ArrayList<>());
public CommandManager(@NonNull DiscordBot bot, @NonNull RESTfulMCClient apiClient) {
// Register commands
registerCommand(new PlayerCommand(apiClient));
registerCommand(new ServerCommand(apiClient));
// Update the commands on Discord
CommandListUpdateAction updateCommands = bot.getJda().updateCommands();
for (SlashCommand command : commands) {
updateCommands.addCommands(Commands.slash(command.getName(), command.getDescription()).addOptions(command.getOptions()));
}
updateCommands.queue();
// Handle registered events
bot.getJda().addEventListener(this);
}
@Override
public void onSlashCommandInteraction(@NonNull SlashCommandInteractionEvent event) {
for (SlashCommand command : commands) {
if (command.getName().equals(event.getName())) {
event.deferReply().queue(); // Inform Discord we received the command
command.onExecute(event.getUser(), event.getMember(), event); // Invoke the command
break;
}
}
}
/**
* Register a slash command.
*
* @param command the command to register
*/
public void registerCommand(@NonNull SlashCommand command) {
commands.add(command);
}
}

View File

@ -0,0 +1,73 @@
package cc.restfulmc.bot.command;
import cc.restfulmc.sdk.exception.RESTfulMCAPIException;
import lombok.Getter;
import lombok.NonNull;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
/**
* A wrapper for slash commands.
*
* @author Braydon
*/
@Getter
public abstract class SlashCommand {
/**
* The name of this command.
*/
@NonNull private final String name;
/**
* The description of this command.
*/
@NonNull private final String description;
/**
* Optional options for this command.
*/
private final OptionData[] options;
public SlashCommand(@NonNull String name, @NonNull String description, OptionData... options) {
this.name = name;
this.description = description;
this.options = options;
}
/**
* Invoked when this command is executed.
*
* @param user the executing user
* @param member the executing member, null if in dms
* @param event the event that triggered this command
*/
public abstract void onExecute(@NonNull User user, Member member, @NonNull SlashCommandInteractionEvent event);
/**
* Reply to an interaction with an API error.
*
* @param event the event to reply to
* @param apiError the api error to reply with
*/
protected final void replyWithApiError(@NonNull SlashCommandInteractionEvent event, @NonNull RESTfulMCAPIException apiError) {
replyWithGenericError(event, apiError.getCode() + " | API Error", apiError.getLocalizedMessage());
}
/**
* Reply to an interaction with a generic error.
*
* @param event the event to reply to
* @param title the title of the error
* @param description the description of the error
*/
protected final void replyWithGenericError(@NonNull SlashCommandInteractionEvent event, @NonNull String title, @NonNull String description) {
event.getHook().sendMessageEmbeds(new EmbedBuilder()
.setColor(0xAA0000)
.setTitle(title)
.setDescription(description)
.build()).queue();
}
}

View File

@ -0,0 +1,72 @@
package cc.restfulmc.bot.command.impl;
import cc.restfulmc.bot.command.SlashCommand;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import cc.restfulmc.sdk.exception.RESTfulMCAPIException;
import cc.restfulmc.sdk.response.Player;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
/**
* @author Braydon
*/
@Slf4j(topic = "Server Lookup Command")
public final class PlayerCommand extends SlashCommand {
/**
* The API client to use for lookups.
*/
@NonNull private final RESTfulMCClient apiClient;
public PlayerCommand(@NonNull RESTfulMCClient apiClient) {
super("player", "Lookup a player by their username or UUID",
new OptionData(OptionType.STRING, "query", "The player username or UUID").setRequired(true)
);
this.apiClient = apiClient;
}
/**
* Invoked when this command is executed.
*
* @param user the executing user
* @param member the executing member, null if in dms
* @param event the event that triggered this command
*/
@Override
public void onExecute(@NonNull User user, Member member, @NonNull SlashCommandInteractionEvent event) {
OptionMapping query = event.getOption("query"); // Get the query
assert query != null;
String queryValue = query.getAsString(); // Get the query value
// Lookup the requested player by the given query
apiClient.async().getPlayer(queryValue).whenComplete((player, ex) -> {
// Failed to lookup the player, handle the error
if (ex != null) {
if (ex.getCause() instanceof RESTfulMCAPIException apiError) {
replyWithApiError(event, apiError);
} else { // Only print real errors
log.error("Failed fetching player:", ex);
}
return;
}
// Respond with the player
long cached = player.getCached(); // The timestamp the player was cached
event.getHook().sendMessageEmbeds(new EmbedBuilder()
.setColor(0x55FF55)
.setTitle("<:steve:1232815662599114753> Player Response", "https://api.restfulmc.cc/player/" + queryValue)
.addField("Unique ID", player.getUniqueId().toString(), true)
.addField("Username", player.getUsername(), true)
.addField("Legacy", player.isLegacy() ? "Yes" : "No", true)
.addField("Cached", cached == -1L ? "No" : "Yes, <t:" + (cached / 1000L) + ":R>", true)
.setThumbnail(player.getSkin().getParts().get(Player.SkinPart.HEAD))
.setFooter("Requested by " + user.getName() + " | " + user.getId(), user.getEffectiveAvatarUrl())
.build()).queue();
});
}
}

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