This commit is contained in:
DAM
2024-12-23 20:05:01 +01:00
parent d3e3f9f2e0
commit 478386252a
308 changed files with 0 additions and 4260 deletions

View File

@@ -1,12 +0,0 @@
[*]
charset = utf-8
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
indent_size = 4
[*.{json,yml}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

View File

@@ -1,118 +0,0 @@
import json
import requests
import os
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
VIRUSTOTAL_API_KEY = os.environ.get('VIRUSTOTAL_API_KEY')
DISCORD_WEBHOOK = os.environ.get('DISCORD_WEBHOOK')
LAST_COMMIT = os.environ.get('LAST_COMMIT')
NEW_COMMIT = os.environ.get('NEW_COMMIT')
def main():
directory = os.listdir('./build/libs')
jar = [file for file in directory if file.endswith('.jar')][0]
version = jar.split('-')[2].split('.jar')[0]
virus_total_link = None
if VIRUSTOTAL_API_KEY is not None:
virus_total_req = requests.post(
f'https://www.virustotal.com/api/v3/files',
files={'file': (jar, open(f'./build/libs/{jar}', 'rb'), 'application/jar')},
headers={
'x-apikey': VIRUSTOTAL_API_KEY,
'accept': 'application/json'
}
)
if virus_total_req.status_code == 200:
virus_total_id = virus_total_req.json()['data']['id']
virus_total_link = f'https://www.virustotal.com/gui/file-analysis/{virus_total_id}/detection'
changes = {}
changes_message = '**Changes:**\n'
html_url = None
if GITHUB_TOKEN is not None:
github_req = requests.get(
f'https://api.github.com/repos/DAMcraft/MeteorServerSeeker/compare/{LAST_COMMIT}...{NEW_COMMIT}',
headers={
'Authorization': f'Bearer {GITHUB_TOKEN}'
}
)
if github_req.status_code == 200:
for commit in github_req.json().get('commits', []):
sha = commit['sha']
message = commit['commit']['message']
changes[sha] = message
changes_message += (f'- [`{sha[:7]}`](https://github.com/DAMcraft/MeteorServerSeeker/commit/{sha}/) '
f'{message}\n')
# Delete old release
get_tags = requests.get(
f"https://api.github.com/repos/DAMcraft/MeteorServerSeeker/releases/tags/latest",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}"
},
)
if get_tags.status_code == 200:
old_release = get_tags.json()
release_id = old_release['id']
del_req = requests.delete(
f"https://api.github.com/repos/DAMcraft/MeteorServerSeeker/releases/{release_id}",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}"
}
)
# New release
req = requests.post(
f"https://api.github.com/repos/DAMcraft/MeteorServerSeeker/releases",
headers={
'Authorization': f'Bearer {GITHUB_TOKEN}'
},
json={
"tag_name": f"latest",
"target_commitish": f"{NEW_COMMIT}",
"name": f"Dev Build (Based on {version})",
"body": changes_message + f"\nVirusTotal: {virus_total_link}",
"draft": False,
"prerelease": True,
"make_latest": 'true'
}
)
release_id = req.json()['id']
html_url = req.json()['html_url']
# Upload jar
requests.post(
f"https://uploads.github.com/repos/DAMcraft/MeteorServerSeeker/releases/{release_id}/assets?name={jar}",
headers={
'Authorization': f'Bearer {GITHUB_TOKEN}',
'Content-Type': 'application/jar'
},
data=open(f'./build/libs/{jar}', 'rb')
)
if DISCORD_WEBHOOK is not None:
requests.post(
DISCORD_WEBHOOK,
files={
jar: open(f'./build/libs/{jar}', 'rb'),
'payload_json': (None, json.dumps({
'embeds': [
{
'title': "New Dev Build!",
'description': f'A new dev build based on {version} has been released!\n'
f'[VirusTotal]({virus_total_link})\n'
f'{changes_message}\n'
f'[View on GitHub]({html_url})',
'color': 0x14c384
}
]
}))
}
)
if __name__ == '__main__':
main()

View File

@@ -1,30 +0,0 @@
name: Publish Development Build
on: push
jobs:
build:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
java-version: 21
distribution: adopt
- uses: actions/setup-python@v2
with:
python-version: 3.12.1
- name: Build
run: ./gradlew build -Pgithub_sha=${{ github.sha }}
- name: Run python script
run: |
pip3 install -r ./.github/workflows/requirements.txt
python3 ./.github/workflows/prerelease.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VIRUSTOTAL_API_KEY: ${{ secrets.VT_API_KEY }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
LAST_COMMIT: ${{ github.event.before }}
NEW_COMMIT: ${{ github.event.after }}

View File

@@ -1,19 +0,0 @@
name: Build Pull Request
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
java-version: 21
distribution: adopt
- name: Build
run: ./gradlew build
- name: Upload artifacts
uses: actions/upload-artifact@v2.2.4
with:
name: build-artifacts
path: build/libs

View File

@@ -1 +0,0 @@
requests

33
.gitignore vendored
View File

@@ -1,33 +0,0 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run/

373
LICENSE
View File

@@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,40 +0,0 @@
plugins {
id "fabric-loom" version "1.7-SNAPSHOT"
}
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
maven {
name = "Meteor Dev Releases"
url = "https://maven.meteordev.org/releases"
}
maven {
name = "Meteor Dev Snapshots"
url = "https://maven.meteordev.org/snapshots"
}
}
dependencies {
// Fabric
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Meteor
modImplementation "meteordevelopment:meteor-client:${project.meteor_version}"
}
processResources {
filesMatching("fabric.mod.json") {
expand "version": project.version, "mc_version": project.minecraft_version, "gh_hash": (System.getenv("GITHUB_SHA") ?: "")
}
}
tasks.withType(JavaCompile).configureEach {
it.options.encoding("UTF-8")
}

View File

@@ -1,17 +0,0 @@
org.gradle.jvmargs=-Xmx2G
# Fabric Properties (https://fabricmc.net/develop)
minecraft_version=1.21
yarn_mappings=1.21+build.9
loader_version=0.15.11
fabric_version=0.102.0+1.21
# Mod Properties
mod_version=4.4.0
maven_group=de.damcraft
archives_base_name=server-seeker
# Dependencies
# Meteor (https://maven.meteordev.org)
meteor_version=0.5.8-SNAPSHOT

Binary file not shown.

View File

@@ -1,6 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

244
gradlew vendored
View File

@@ -1,244 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname country
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored
View File

@@ -1,92 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,10 +0,0 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}

View File

@@ -1,166 +0,0 @@
package de.damcraft.serverseeker;
import com.google.gson.JsonObject;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import de.damcraft.serverseeker.ssapi.responses.UserInfoResponse;
import meteordevelopment.meteorclient.systems.Systems;
import net.minecraft.util.Util;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.BiConsumer;
import static de.damcraft.serverseeker.ServerSeeker.LOG;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class DiscordAuth {
private static final int port = 7637;
// Store as a string because it's too big and I don't want to import unnecessary libraries if I join it to a String anyway
private static final String clientId = "1087083964432404590";
public static final String url =
"https://discord.com/api/oauth2/authorize" +
"?client_id=" + clientId +
"&redirect_uri=http%3A%2F%2F127.0.0.1%3A" + port + "%2F" +
"&response_type=code" +
"&scope=identify";
private static HttpServer server;
private static BiConsumer<String, String> callback;
public static void auth(BiConsumer<String, String> callback) {
DiscordAuth.callback = callback;
Util.getOperatingSystem().open(url);
startServer();
}
private static void startServer() {
try {
server = HttpServer.create();
server.bind(new InetSocketAddress("127.0.0.1", port), 0);
server.createContext("/", new AuthHandler());
server.start();
} catch (IOException e) {
LOG.error(e.toString());
}
}
public static void stopServer() {
if (server == null) return;
server.stop(0);
server = null;
callback = null;
}
private static class AuthHandler implements HttpHandler {
@Override
public void handle(HttpExchange req) throws IOException {
if (req.getRequestMethod().equals("GET")) {
// Login
List<NameValuePair> query = URLEncodedUtils.parse(req.getRequestURI(), StandardCharsets.UTF_8);
boolean ok = false;
for (NameValuePair pair : query) {
if (pair.getName().equals("code")) {
handleCode(pair.getValue());
ok = true;
break;
}
}
if (!ok) {
writeText(req, "Cannot authenticate.");
} else writeText(req, "You may now close this page.");
stopServer();
} else if (req.getRequestMethod().equals("OPTIONS")) {
req.getResponseHeaders().add("Allow", "GET, OPTIONS");
req.sendResponseHeaders(204, -1);
} else {
req.sendResponseHeaders(405, -1);
LOG.warn("Invalid request method: {}", req.getRequestMethod());
}
}
private void writeText(HttpExchange req, String text) throws IOException {
OutputStream out = req.getResponseBody();
req.sendResponseHeaders(200, text.length());
out.write(text.getBytes(StandardCharsets.UTF_8));
out.flush();
out.close();
}
public void handleCode(String code) {
// Get the ServerSeeker auth token
JsonObject params = new JsonObject();
params.addProperty("code", code);
params.addProperty("usage", "meteor serverseeker");
String jsonResp = SmallHttp.post("https://api.serverseeker.net/get_token", params.toString());
// {"api_key": "..."} or {"error": "..."}
JsonObject obj = gson.fromJson(jsonResp, JsonObject.class);
if (obj.has("error")) {
System.out.println("Error: " + obj.get("error").getAsString());
callback.accept(null, obj.get("error").getAsString());
return;
}
if (!obj.has("api_key")) {
System.out.println("Error: No api_key in response.");
callback.accept(null, "No api_key in response.");
return;
}
String apiKey = obj.get("api_key").getAsString();
Systems.get(ServerSeekerSystem.class).apiKey = apiKey;
// Get the discord user info
params = new JsonObject();
params.addProperty("api_key", apiKey);
jsonResp = SmallHttp.post("https://api.serverseeker.net/user_info", params.toString());
// {
// "discord_id": user_id,
// "discord_username": discord_username,
// "discord_avatar_url": avatar_url
// } or {"error": "..."}
UserInfoResponse userInfo = gson.fromJson(jsonResp, UserInfoResponse.class);
if (userInfo.isError()) {
System.out.println("Error: " + userInfo.error);
callback.accept(null, userInfo.error);
return;
}
String discordId = userInfo.discord_id;
String discordUsername = userInfo.discord_username;
String discordAvatarUrl = userInfo.discord_avatar_url;
Systems.get(ServerSeekerSystem.class).discordId = discordId;
Systems.get(ServerSeekerSystem.class).discordUsername = discordUsername;
Systems.get(ServerSeekerSystem.class).discordAvatarUrl = discordAvatarUrl == null ? "" : discordAvatarUrl;
callback.accept(apiKey, null);
}
}
}

View File

@@ -1,80 +0,0 @@
package de.damcraft.serverseeker;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.systems.RenderSystem;
import de.damcraft.serverseeker.ssapi.responses.UserInfoResponse;
import meteordevelopment.meteorclient.renderer.Texture;
import meteordevelopment.meteorclient.systems.Systems;
import meteordevelopment.meteorclient.utils.network.Http;
import org.lwjgl.BufferUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import static de.damcraft.serverseeker.ServerSeeker.LOG;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class DiscordAvatar extends Texture {
public DiscordAvatar(String url) {
BufferedImage avatar;
try {
InputStream stream = Http.get(url).sendInputStream();
if (stream == null) {
JsonObject params = new JsonObject();
params.addProperty("api_key", Systems.get(ServerSeekerSystem.class).apiKey);
String jsonResp = SmallHttp.post("https://api.serverseeker.net/user_info", params.toString());
UserInfoResponse userInfo = gson.fromJson(jsonResp, UserInfoResponse.class);
if (userInfo.isError()) {
System.out.println("Error: " + userInfo.error);
return;
}
String discordId = userInfo.discord_id;
String discordUsername = userInfo.discord_username;
String discordAvatarUrl = userInfo.discord_avatar_url == null ? "" : userInfo.discord_avatar_url;
Systems.get(ServerSeekerSystem.class).discordId = discordId;
Systems.get(ServerSeekerSystem.class).discordUsername = discordUsername;
Systems.get(ServerSeekerSystem.class).discordAvatarUrl = discordAvatarUrl;
stream = Http.get(discordAvatarUrl).sendInputStream();
}
if (stream == null) {
System.err.println("Failed to get avatar, are you Vero?");
return;
}
avatar = ImageIO.read(stream);
} catch (IOException e) {
LOG.error(e.toString());
return;
}
byte[] data = new byte[avatar.getWidth() * avatar.getHeight() * 3];
int[] pixel = new int[4];
int i = 0;
for (int y = 0; y < avatar.getHeight(); y++) {
for (int x = 0; x < avatar.getWidth(); x++) {
avatar.getData().getPixel(x, y, pixel);
for (int j = 0; j < 3; j++) {
data[i] = (byte) pixel[j];
i++;
}
}
}
upload(BufferUtils.createByteBuffer(data.length).put(data));
}
private void upload(ByteBuffer data) {
Runnable action = () -> upload(32, 32, data, Texture.Format.RGB, Texture.Filter.Nearest, Texture.Filter.Nearest, false);
if (RenderSystem.isOnRenderThread()) action.run();
else RenderSystem.recordRenderCall(action::run);
}
}

View File

@@ -1,87 +0,0 @@
package de.damcraft.serverseeker;
import com.google.gson.Gson;
import com.mojang.logging.LogUtils;
import de.damcraft.serverseeker.country.Countries;
import de.damcraft.serverseeker.country.Country;
import de.damcraft.serverseeker.country.CountrySetting;
import de.damcraft.serverseeker.hud.HistoricPlayersHud;
import de.damcraft.serverseeker.modules.BungeeSpoofModule;
import de.damcraft.serverseeker.utils.HistoricPlayersUpdater;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import meteordevelopment.meteorclient.MeteorClient;
import meteordevelopment.meteorclient.addons.GithubRepo;
import meteordevelopment.meteorclient.addons.MeteorAddon;
import meteordevelopment.meteorclient.gui.utils.SettingsWidgetFactory;
import meteordevelopment.meteorclient.systems.hud.Hud;
import meteordevelopment.meteorclient.systems.modules.Category;
import meteordevelopment.meteorclient.systems.modules.Modules;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.item.Items;
import org.slf4j.Logger;
import java.util.Map;
public class ServerSeeker extends MeteorAddon {
/*
Feature list for anticope.pages.dev:
(creates features matching the RegEx '(?:add\(new )([^(]+)(?:\([^)]*)\)\)', as anticope checks for that.
add(new Find servers with many parameters, for example: Cracked, Description, Player count, much more...())
add(new Server database with around 1.000.000 servers!())
add(new Over 80.000.000 players tracked!())
add(new Search for ANY server you want!())
add(new Join misconfigured BungeeCord backends with any name you want!())
*/
public static final Logger LOG = LogUtils.getLogger();
public static final Category CATEGORY = new Category("ServerSeeker", Items.SPYGLASS.getDefaultStack());
public static final Map<String, Country> COUNTRY_MAP = new Object2ReferenceOpenHashMap<>();
public static final Gson gson = new Gson();
@Override
public void onInitialize() {
LOG.info("Loaded the ServerSeeker addon!");
// Load countries
Countries.init();
Modules.get().add( new BungeeSpoofModule() );
Hud.get().register(HistoricPlayersHud.INFO);
SettingsWidgetFactory.registerCustomFactory(CountrySetting.class, (theme) -> (table, setting) -> {
CountrySetting.countrySettingW(table, (CountrySetting) setting, theme);
});
MeteorClient.EVENT_BUS.subscribe(HistoricPlayersUpdater.class);
}
@Override
public void onRegisterCategories() {
Modules.registerCategory(CATEGORY);
}
@Override
public String getPackage() {
return "de.damcraft.serverseeker";
}
@Override
public GithubRepo getRepo() {
return new GithubRepo("DAMcraft", "MeteorServerSeeker");
}
@Override
public String getWebsite() {
return "https://serverseeker.net/";
}
@Override
public String getCommit() {
String commit = FabricLoader
.getInstance()
.getModContainer("serverseeker")
.get().getMetadata()
.getCustomValue("github:sha")
.getAsString();
return commit.isEmpty() ? null : commit.trim();
}
}

View File

@@ -1,45 +0,0 @@
package de.damcraft.serverseeker;
import meteordevelopment.meteorclient.systems.System;
import meteordevelopment.meteorclient.systems.Systems;
import net.minecraft.nbt.NbtCompound;
public class ServerSeekerSystem extends System<ServerSeekerSystem> {
public ServerSeekerSystem() {
super("serverseeker");
}
public String apiKey = "";
public String discordId = "";
public String discordUsername = "";
public String discordAvatarUrl = "";
public static ServerSeekerSystem get() {
return Systems.get(ServerSeekerSystem.class);
}
@Override
public NbtCompound toTag() {
NbtCompound tag = new NbtCompound();
tag.putString("apiKey", apiKey);
tag.putString("userId", discordId);
tag.putString("username", discordUsername);
tag.putString("avatarUrl", discordAvatarUrl);
return tag;
}
@Override
public ServerSeekerSystem fromTag(NbtCompound tag) {
apiKey = tag.getString("apiKey");
discordId = tag.getString("userId");
discordUsername = tag.getString("username");
discordAvatarUrl = tag.getString("avatarUrl");
return this;
}
}

View File

@@ -1,60 +0,0 @@
package de.damcraft.serverseeker;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import static de.damcraft.serverseeker.ServerSeeker.LOG;
public class SmallHttp {
public static String post(String url, String json) {
try (HttpClient client = HttpClient.newHttpClient()) {
return client.send(HttpRequest.newBuilder()
.uri(URI.create(url))
.POST(HttpRequest.BodyPublishers.ofString(json))
.header("Content-Type", "application/json")
.build(),
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
).body();
} catch (IOException | InterruptedException e) {
LOG.error(e.toString());
return null;
}
}
public static String get(String url) {
try (HttpClient client = HttpClient.newHttpClient()) {
return client.send(HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
).body();
} catch (IOException | InterruptedException e) {
LOG.error(e.toString());
return null;
}
}
public static HttpResponse<InputStream> download(String url) {
try (HttpClient client = HttpClient.newHttpClient()) {
HttpResponse<InputStream> req = client.send(HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build(),
HttpResponse.BodyHandlers.ofInputStream()
);
if (req.headers().firstValue("location").isPresent()) {
return download(req.headers().firstValue("location").get());
}
return req;
} catch (IOException | InterruptedException e) {
LOG.error(e.toString());
return null;
}
}
}

View File

@@ -1,258 +0,0 @@
package de.damcraft.serverseeker.country;
import de.damcraft.serverseeker.ServerSeeker;
public class Countries {
public static final Country UN = new DefaultCountry("Any", "UN");
public static void init() {
ServerSeeker.COUNTRY_MAP.put("UN", UN);
ServerSeeker.COUNTRY_MAP.put("AF", new Country("Afghanistan", "AF"));
ServerSeeker.COUNTRY_MAP.put("AX", new Country("Aland Islands", "AX"));
ServerSeeker.COUNTRY_MAP.put("AL", new Country("Albania", "AL"));
ServerSeeker.COUNTRY_MAP.put("DZ", new Country("Algeria", "DZ"));
ServerSeeker.COUNTRY_MAP.put("AS", new Country("American Samoa", "AS"));
ServerSeeker.COUNTRY_MAP.put("AD", new Country("Andorra", "AD"));
ServerSeeker.COUNTRY_MAP.put("AO", new Country("Angola", "AO"));
ServerSeeker.COUNTRY_MAP.put("AI", new Country("Anguilla", "AI"));
ServerSeeker.COUNTRY_MAP.put("AQ", new Country("Antarctica", "AQ"));
ServerSeeker.COUNTRY_MAP.put("AG", new Country("Antigua And Barbuda", "AG"));
ServerSeeker.COUNTRY_MAP.put("AR", new Country("Argentina", "AR"));
ServerSeeker.COUNTRY_MAP.put("AM", new Country("Armenia", "AM"));
ServerSeeker.COUNTRY_MAP.put("AW", new Country("Aruba", "AW"));
ServerSeeker.COUNTRY_MAP.put("AU", new Country("Australia", "AU"));
ServerSeeker.COUNTRY_MAP.put("AT", new Country("Austria", "AT"));
ServerSeeker.COUNTRY_MAP.put("AZ", new Country("Azerbaijan", "AZ"));
ServerSeeker.COUNTRY_MAP.put("BS", new Country("Bahamas", "BS"));
ServerSeeker.COUNTRY_MAP.put("BH", new Country("Bahrain", "BH"));
ServerSeeker.COUNTRY_MAP.put("BD", new Country("Bangladesh", "BD"));
ServerSeeker.COUNTRY_MAP.put("BB", new Country("Barbados", "BB"));
ServerSeeker.COUNTRY_MAP.put("BY", new Country("Belarus", "BY"));
ServerSeeker.COUNTRY_MAP.put("BE", new Country("Belgium", "BE"));
ServerSeeker.COUNTRY_MAP.put("BZ", new Country("Belize", "BZ"));
ServerSeeker.COUNTRY_MAP.put("BJ", new Country("Benin", "BJ"));
ServerSeeker.COUNTRY_MAP.put("BM", new Country("Bermuda", "BM"));
ServerSeeker.COUNTRY_MAP.put("BT", new Country("Bhutan", "BT"));
ServerSeeker.COUNTRY_MAP.put("BO", new Country("Bolivia", "BO"));
ServerSeeker.COUNTRY_MAP.put("BA", new Country("Bosnia And Herzegovina", "BA"));
ServerSeeker.COUNTRY_MAP.put("BW", new Country("Botswana", "BW"));
ServerSeeker.COUNTRY_MAP.put("BV", new Country("Bouvet Island", "BV"));
ServerSeeker.COUNTRY_MAP.put("BR", new Country("Brazil", "BR"));
ServerSeeker.COUNTRY_MAP.put("IO", new Country("British Indian Ocean Territory", "IO"));
ServerSeeker.COUNTRY_MAP.put("BN", new Country("Brunei Darussalam", "BN"));
ServerSeeker.COUNTRY_MAP.put("BG", new Country("Bulgaria", "BG"));
ServerSeeker.COUNTRY_MAP.put("BF", new Country("Burkina Faso", "BF"));
ServerSeeker.COUNTRY_MAP.put("BI", new Country("Burundi", "BI"));
ServerSeeker.COUNTRY_MAP.put("KH", new Country("Cambodia", "KH"));
ServerSeeker.COUNTRY_MAP.put("CM", new Country("Cameroon", "CM"));
ServerSeeker.COUNTRY_MAP.put("CA", new Country("Canada", "CA"));
ServerSeeker.COUNTRY_MAP.put("CV", new Country("Cape Verde", "CV"));
ServerSeeker.COUNTRY_MAP.put("KY", new Country("Cayman Islands", "KY"));
ServerSeeker.COUNTRY_MAP.put("CF", new Country("Central African Republic", "CF"));
ServerSeeker.COUNTRY_MAP.put("TD", new Country("Chad", "TD"));
ServerSeeker.COUNTRY_MAP.put("CL", new Country("Chile", "CL"));
ServerSeeker.COUNTRY_MAP.put("CN", new Country("China", "CN"));
ServerSeeker.COUNTRY_MAP.put("CX", new Country("Christmas Island", "CX"));
ServerSeeker.COUNTRY_MAP.put("CC", new Country("Cocos (Keeling) Islands", "CC"));
ServerSeeker.COUNTRY_MAP.put("CO", new Country("Colombia", "CO"));
ServerSeeker.COUNTRY_MAP.put("KM", new Country("Comoros", "KM"));
ServerSeeker.COUNTRY_MAP.put("CG", new Country("Congo", "CG"));
ServerSeeker.COUNTRY_MAP.put("CD", new Country("Congo, Democratic Republic", "CD"));
ServerSeeker.COUNTRY_MAP.put("CK", new Country("Cook Islands", "CK"));
ServerSeeker.COUNTRY_MAP.put("CR", new Country("Costa Rica", "CR"));
ServerSeeker.COUNTRY_MAP.put("CI", new Country("Cote D'Ivoire", "CI"));
ServerSeeker.COUNTRY_MAP.put("HR", new Country("Croatia", "HR"));
ServerSeeker.COUNTRY_MAP.put("CU", new Country("Cuba", "CU"));
ServerSeeker.COUNTRY_MAP.put("CY", new Country("Cyprus", "CY"));
ServerSeeker.COUNTRY_MAP.put("CZ", new Country("Czech Republic", "CZ"));
ServerSeeker.COUNTRY_MAP.put("DK", new Country("Denmark", "DK"));
ServerSeeker.COUNTRY_MAP.put("DJ", new Country("Djibouti", "DJ"));
ServerSeeker.COUNTRY_MAP.put("DM", new Country("Dominica", "DM"));
ServerSeeker.COUNTRY_MAP.put("DO", new Country("Dominican Republic", "DO"));
ServerSeeker.COUNTRY_MAP.put("EC", new Country("Ecuador", "EC"));
ServerSeeker.COUNTRY_MAP.put("EG", new Country("Egypt", "EG"));
ServerSeeker.COUNTRY_MAP.put("SV", new Country("El Salvador", "SV"));
ServerSeeker.COUNTRY_MAP.put("GQ", new Country("Equatorial Guinea", "GQ"));
ServerSeeker.COUNTRY_MAP.put("ER", new Country("Eritrea", "ER"));
ServerSeeker.COUNTRY_MAP.put("EE", new Country("Estonia", "EE"));
ServerSeeker.COUNTRY_MAP.put("ET", new Country("Ethiopia", "ET"));
ServerSeeker.COUNTRY_MAP.put("FK", new Country("Falkland Islands (Malvinas)", "FK"));
ServerSeeker.COUNTRY_MAP.put("FO", new Country("Faroe Islands", "FO"));
ServerSeeker.COUNTRY_MAP.put("FJ", new Country("Fiji", "FJ"));
ServerSeeker.COUNTRY_MAP.put("FI", new Country("Finland", "FI"));
ServerSeeker.COUNTRY_MAP.put("FR", new Country("France", "FR"));
ServerSeeker.COUNTRY_MAP.put("GF", new Country("French Guiana", "GF"));
ServerSeeker.COUNTRY_MAP.put("PF", new Country("French Polynesia", "PF"));
ServerSeeker.COUNTRY_MAP.put("TF", new Country("French Southern Territories", "TF"));
ServerSeeker.COUNTRY_MAP.put("GA", new Country("Gabon", "GA"));
ServerSeeker.COUNTRY_MAP.put("GM", new Country("Gambia", "GM"));
ServerSeeker.COUNTRY_MAP.put("GE", new Country("Georgia", "GE"));
ServerSeeker.COUNTRY_MAP.put("DE", new Country("Germany", "DE"));
ServerSeeker.COUNTRY_MAP.put("GH", new Country("Ghana", "GH"));
ServerSeeker.COUNTRY_MAP.put("GI", new Country("Gibraltar", "GI"));
ServerSeeker.COUNTRY_MAP.put("GR", new Country("Greece", "GR"));
ServerSeeker.COUNTRY_MAP.put("GL", new Country("Greenland", "GL"));
ServerSeeker.COUNTRY_MAP.put("GD", new Country("Grenada", "GD"));
ServerSeeker.COUNTRY_MAP.put("GP", new Country("Guadeloupe", "GP"));
ServerSeeker.COUNTRY_MAP.put("GU", new Country("Guam", "GU"));
ServerSeeker.COUNTRY_MAP.put("GT", new Country("Guatemala", "GT"));
ServerSeeker.COUNTRY_MAP.put("GG", new Country("Guernsey", "GG"));
ServerSeeker.COUNTRY_MAP.put("GN", new Country("Guinea", "GN"));
ServerSeeker.COUNTRY_MAP.put("GW", new Country("Guinea-Bissau", "GW"));
ServerSeeker.COUNTRY_MAP.put("GY", new Country("Guyana", "GY"));
ServerSeeker.COUNTRY_MAP.put("HT", new Country("Haiti", "HT"));
ServerSeeker.COUNTRY_MAP.put("HM", new Country("Heard Island & Mcdonald Islands", "HM"));
ServerSeeker.COUNTRY_MAP.put("VA", new Country("Holy See (Vatican City State)", "VA"));
ServerSeeker.COUNTRY_MAP.put("HN", new Country("Honduras", "HN"));
ServerSeeker.COUNTRY_MAP.put("HK", new Country("Hong Kong", "HK"));
ServerSeeker.COUNTRY_MAP.put("HU", new Country("Hungary", "HU"));
ServerSeeker.COUNTRY_MAP.put("IS", new Country("Iceland", "IS"));
ServerSeeker.COUNTRY_MAP.put("IN", new Country("India", "IN"));
ServerSeeker.COUNTRY_MAP.put("ID", new Country("Indonesia", "ID"));
ServerSeeker.COUNTRY_MAP.put("IR", new Country("Iran, Islamic Republic Of", "IR"));
ServerSeeker.COUNTRY_MAP.put("IQ", new Country("Iraq", "IQ"));
ServerSeeker.COUNTRY_MAP.put("IE", new Country("Ireland", "IE"));
ServerSeeker.COUNTRY_MAP.put("IM", new Country("Isle Of Man", "IM"));
ServerSeeker.COUNTRY_MAP.put("IL", new Country("Israel", "IL"));
ServerSeeker.COUNTRY_MAP.put("IT", new Country("Italy", "IT"));
ServerSeeker.COUNTRY_MAP.put("JM", new Country("Jamaica", "JM"));
ServerSeeker.COUNTRY_MAP.put("JP", new Country("Japan", "JP"));
ServerSeeker.COUNTRY_MAP.put("JE", new Country("Jersey", "JE"));
ServerSeeker.COUNTRY_MAP.put("JO", new Country("Jordan", "JO"));
ServerSeeker.COUNTRY_MAP.put("KZ", new Country("Kazakhstan", "KZ"));
ServerSeeker.COUNTRY_MAP.put("KE", new Country("Kenya", "KE"));
ServerSeeker.COUNTRY_MAP.put("KI", new Country("Kiribati", "KI"));
ServerSeeker.COUNTRY_MAP.put("KR", new Country("Korea", "KR"));
ServerSeeker.COUNTRY_MAP.put("KP", new Country("North Korea", "KP"));
ServerSeeker.COUNTRY_MAP.put("KW", new Country("Kuwait", "KW"));
ServerSeeker.COUNTRY_MAP.put("KG", new Country("Kyrgyzstan", "KG"));
ServerSeeker.COUNTRY_MAP.put("LA", new Country("Lao People's Democratic Republic", "LA"));
ServerSeeker.COUNTRY_MAP.put("LV", new Country("Latvia", "LV"));
ServerSeeker.COUNTRY_MAP.put("LB", new Country("Lebanon", "LB"));
ServerSeeker.COUNTRY_MAP.put("LS", new Country("Lesotho", "LS"));
ServerSeeker.COUNTRY_MAP.put("LR", new Country("Liberia", "LR"));
ServerSeeker.COUNTRY_MAP.put("LY", new Country("Libyan Arab Jamahiriya", "LY"));
ServerSeeker.COUNTRY_MAP.put("LI", new Country("Liechtenstein", "LI"));
ServerSeeker.COUNTRY_MAP.put("LT", new Country("Lithuania", "LT"));
ServerSeeker.COUNTRY_MAP.put("LU", new Country("Luxembourg", "LU"));
ServerSeeker.COUNTRY_MAP.put("MO", new Country("Macao", "MO"));
ServerSeeker.COUNTRY_MAP.put("MK", new Country("Macedonia", "MK"));
ServerSeeker.COUNTRY_MAP.put("MG", new Country("Madagascar", "MG"));
ServerSeeker.COUNTRY_MAP.put("MW", new Country("Malawi", "MW"));
ServerSeeker.COUNTRY_MAP.put("MY", new Country("Malaysia", "MY"));
ServerSeeker.COUNTRY_MAP.put("MV", new Country("Maldives", "MV"));
ServerSeeker.COUNTRY_MAP.put("ML", new Country("Mali", "ML"));
ServerSeeker.COUNTRY_MAP.put("MT", new Country("Malta", "MT"));
ServerSeeker.COUNTRY_MAP.put("MH", new Country("Marshall Islands", "MH"));
ServerSeeker.COUNTRY_MAP.put("MQ", new Country("Martinique", "MQ"));
ServerSeeker.COUNTRY_MAP.put("MR", new Country("Mauritania", "MR"));
ServerSeeker.COUNTRY_MAP.put("MU", new Country("Mauritius", "MU"));
ServerSeeker.COUNTRY_MAP.put("YT", new Country("Mayotte", "YT"));
ServerSeeker.COUNTRY_MAP.put("MX", new Country("Mexico", "MX"));
ServerSeeker.COUNTRY_MAP.put("FM", new Country("Micronesia, Federated States Of", "FM"));
ServerSeeker.COUNTRY_MAP.put("MD", new Country("Moldova", "MD"));
ServerSeeker.COUNTRY_MAP.put("MC", new Country("Monaco", "MC"));
ServerSeeker.COUNTRY_MAP.put("MN", new Country("Mongolia", "MN"));
ServerSeeker.COUNTRY_MAP.put("ME", new Country("Montenegro", "ME"));
ServerSeeker.COUNTRY_MAP.put("MS", new Country("Montserrat", "MS"));
ServerSeeker.COUNTRY_MAP.put("MA", new Country("Morocco", "MA"));
ServerSeeker.COUNTRY_MAP.put("MZ", new Country("Mozambique", "MZ"));
ServerSeeker.COUNTRY_MAP.put("MM", new Country("Myanmar", "MM"));
ServerSeeker.COUNTRY_MAP.put("NA", new Country("Namibia", "NA"));
ServerSeeker.COUNTRY_MAP.put("NR", new Country("Nauru", "NR"));
ServerSeeker.COUNTRY_MAP.put("NP", new Country("Nepal", "NP"));
ServerSeeker.COUNTRY_MAP.put("NL", new Country("Netherlands", "NL"));
ServerSeeker.COUNTRY_MAP.put("AN", new Country("Netherlands Antilles", "AN"));
ServerSeeker.COUNTRY_MAP.put("NC", new Country("New Caledonia", "NC"));
ServerSeeker.COUNTRY_MAP.put("NZ", new Country("New Zealand", "NZ"));
ServerSeeker.COUNTRY_MAP.put("NI", new Country("Nicaragua", "NI"));
ServerSeeker.COUNTRY_MAP.put("NE", new Country("Niger", "NE"));
ServerSeeker.COUNTRY_MAP.put("NG", new Country("Nigeria", "NG"));
ServerSeeker.COUNTRY_MAP.put("NU", new Country("Niue", "NU"));
ServerSeeker.COUNTRY_MAP.put("NF", new Country("Norfolk Island", "NF"));
ServerSeeker.COUNTRY_MAP.put("MP", new Country("Northern Mariana Islands", "MP"));
ServerSeeker.COUNTRY_MAP.put("NO", new Country("Norway", "NO"));
ServerSeeker.COUNTRY_MAP.put("OM", new Country("Oman", "OM"));
ServerSeeker.COUNTRY_MAP.put("PK", new Country("Pakistan", "PK"));
ServerSeeker.COUNTRY_MAP.put("PW", new Country("Palau", "PW"));
ServerSeeker.COUNTRY_MAP.put("PS", new Country("Palestinian Territory, Occupied", "PS"));
ServerSeeker.COUNTRY_MAP.put("PA", new Country("Panama", "PA"));
ServerSeeker.COUNTRY_MAP.put("PG", new Country("Papua New Guinea", "PG"));
ServerSeeker.COUNTRY_MAP.put("PY", new Country("Paraguay", "PY"));
ServerSeeker.COUNTRY_MAP.put("PE", new Country("Peru", "PE"));
ServerSeeker.COUNTRY_MAP.put("PH", new Country("Philippines", "PH"));
ServerSeeker.COUNTRY_MAP.put("PN", new Country("Pitcairn", "PN"));
ServerSeeker.COUNTRY_MAP.put("PL", new Country("Poland", "PL"));
ServerSeeker.COUNTRY_MAP.put("PT", new Country("Portugal", "PT"));
ServerSeeker.COUNTRY_MAP.put("PR", new Country("Puerto Rico", "PR"));
ServerSeeker.COUNTRY_MAP.put("QA", new Country("Qatar", "QA"));
ServerSeeker.COUNTRY_MAP.put("RE", new Country("Reunion", "RE"));
ServerSeeker.COUNTRY_MAP.put("RO", new Country("Romania", "RO"));
ServerSeeker.COUNTRY_MAP.put("RU", new Country("Russian Federation", "RU"));
ServerSeeker.COUNTRY_MAP.put("RW", new Country("Rwanda", "RW"));
ServerSeeker.COUNTRY_MAP.put("BL", new Country("Saint Barthelemy", "BL"));
ServerSeeker.COUNTRY_MAP.put("SH", new Country("Saint Helena", "SH"));
ServerSeeker.COUNTRY_MAP.put("KN", new Country("Saint Kitts And Nevis", "KN"));
ServerSeeker.COUNTRY_MAP.put("LC", new Country("Saint Lucia", "LC"));
ServerSeeker.COUNTRY_MAP.put("MF", new Country("Saint Martin", "MF"));
ServerSeeker.COUNTRY_MAP.put("PM", new Country("Saint Pierre And Miquelon", "PM"));
ServerSeeker.COUNTRY_MAP.put("VC", new Country("Saint Vincent And Grenadines", "VC"));
ServerSeeker.COUNTRY_MAP.put("WS", new Country("Samoa", "WS"));
ServerSeeker.COUNTRY_MAP.put("SM", new Country("San Marino", "SM"));
ServerSeeker.COUNTRY_MAP.put("ST", new Country("Sao Tome And Principe", "ST"));
ServerSeeker.COUNTRY_MAP.put("SA", new Country("Saudi Arabia", "SA"));
ServerSeeker.COUNTRY_MAP.put("SN", new Country("Senegal", "SN"));
ServerSeeker.COUNTRY_MAP.put("RS", new Country("Serbia", "RS"));
ServerSeeker.COUNTRY_MAP.put("SC", new Country("Seychelles", "SC"));
ServerSeeker.COUNTRY_MAP.put("SL", new Country("Sierra Leone", "SL"));
ServerSeeker.COUNTRY_MAP.put("SG", new Country("Singapore", "SG"));
ServerSeeker.COUNTRY_MAP.put("SK", new Country("Slovakia", "SK"));
ServerSeeker.COUNTRY_MAP.put("SI", new Country("Slovenia", "SI"));
ServerSeeker.COUNTRY_MAP.put("SB", new Country("Solomon Islands", "SB"));
ServerSeeker.COUNTRY_MAP.put("SO", new Country("Somalia", "SO"));
ServerSeeker.COUNTRY_MAP.put("ZA", new Country("South Africa", "ZA"));
ServerSeeker.COUNTRY_MAP.put("GS", new Country("South Georgia And Sandwich Isl.", "GS"));
ServerSeeker.COUNTRY_MAP.put("ES", new Country("Spain", "ES"));
ServerSeeker.COUNTRY_MAP.put("LK", new Country("Sri Lanka", "LK"));
ServerSeeker.COUNTRY_MAP.put("SD", new Country("Sudan", "SD"));
ServerSeeker.COUNTRY_MAP.put("SR", new Country("Suriname", "SR"));
ServerSeeker.COUNTRY_MAP.put("SJ", new Country("Svalbard And Jan Mayen", "SJ"));
ServerSeeker.COUNTRY_MAP.put("SZ", new Country("Swaziland", "SZ"));
ServerSeeker.COUNTRY_MAP.put("SE", new Country("Sweden", "SE"));
ServerSeeker.COUNTRY_MAP.put("CH", new Country("Switzerland", "CH"));
ServerSeeker.COUNTRY_MAP.put("SY", new Country("Syrian Arab Republic", "SY"));
ServerSeeker.COUNTRY_MAP.put("TW", new Country("Taiwan", "TW"));
ServerSeeker.COUNTRY_MAP.put("TJ", new Country("Tajikistan", "TJ"));
ServerSeeker.COUNTRY_MAP.put("TZ", new Country("Tanzania", "TZ"));
ServerSeeker.COUNTRY_MAP.put("TH", new Country("Thailand", "TH"));
ServerSeeker.COUNTRY_MAP.put("TL", new Country("Timor-Leste", "TL"));
ServerSeeker.COUNTRY_MAP.put("TG", new Country("Togo", "TG"));
ServerSeeker.COUNTRY_MAP.put("TK", new Country("Tokelau", "TK"));
ServerSeeker.COUNTRY_MAP.put("TO", new Country("Tonga", "TO"));
ServerSeeker.COUNTRY_MAP.put("TT", new Country("Trinidad And Tobago", "TT"));
ServerSeeker.COUNTRY_MAP.put("TN", new Country("Tunisia", "TN"));
ServerSeeker.COUNTRY_MAP.put("TR", new Country("Turkey", "TR"));
ServerSeeker.COUNTRY_MAP.put("TM", new Country("Turkmenistan", "TM"));
ServerSeeker.COUNTRY_MAP.put("TC", new Country("Turks And Caicos Islands", "TC"));
ServerSeeker.COUNTRY_MAP.put("TV", new Country("Tuvalu", "TV"));
ServerSeeker.COUNTRY_MAP.put("UG", new Country("Uganda", "UG"));
ServerSeeker.COUNTRY_MAP.put("UA", new Country("Ukraine", "UA"));
ServerSeeker.COUNTRY_MAP.put("AE", new Country("United Arab Emirates", "AE"));
ServerSeeker.COUNTRY_MAP.put("GB", new Country("United Kingdom", "GB"));
ServerSeeker.COUNTRY_MAP.put("US", new Country("United States", "US"));
ServerSeeker.COUNTRY_MAP.put("UM", new Country("United States Outlying Islands", "UM"));
ServerSeeker.COUNTRY_MAP.put("UY", new Country("Uruguay", "UY"));
ServerSeeker.COUNTRY_MAP.put("UZ", new Country("Uzbekistan", "UZ"));
ServerSeeker.COUNTRY_MAP.put("VU", new Country("Vanuatu", "VU"));
ServerSeeker.COUNTRY_MAP.put("VE", new Country("Venezuela", "VE"));
ServerSeeker.COUNTRY_MAP.put("VN", new Country("Vietnam", "VN"));
ServerSeeker.COUNTRY_MAP.put("VG", new Country("Virgin Islands, British", "VG"));
ServerSeeker.COUNTRY_MAP.put("VI", new Country("Virgin Islands, U.S.", "VI"));
ServerSeeker.COUNTRY_MAP.put("WF", new Country("Wallis And Futuna", "WF"));
ServerSeeker.COUNTRY_MAP.put("EH", new Country("Western Sahara", "EH"));
ServerSeeker.COUNTRY_MAP.put("YE", new Country("Yemen", "YE"));
ServerSeeker.COUNTRY_MAP.put("ZM", new Country("Zambia", "ZM"));
ServerSeeker.COUNTRY_MAP.put("ZW", new Country("Zimbabwe", "ZW"));
}
}

View File

@@ -1,114 +0,0 @@
package de.damcraft.serverseeker.country;
import meteordevelopment.meteorclient.renderer.Texture;
import meteordevelopment.meteorclient.utils.network.MeteorExecutor;
import net.minecraft.resource.Resource;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import static meteordevelopment.meteorclient.MeteorClient.LOG;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class Country implements Comparable<Country> {
public final Identifier identifier;
public final String name;
public final String code;
private final TextureData textureData;
public Country(String name, String code) {
this.name = name;
this.code = code.toLowerCase(Locale.ENGLISH);
this.identifier = Identifier.of("serverseeker", String.format("textures/flags/%s.png", this.code));
if (mc.getResourceManager().getResource(this.identifier).isEmpty()) {
LOG.error("Could not find flag for country: {}", this.code);
this.textureData = new EmptyTextureData();
} else this.textureData = new CountryTextureData();
}
@Nullable
public Texture getTexture() {
Texture texture = this.textureData.get();
if (texture == null) return this == Countries.UN ? null : Countries.UN.getTexture();
return texture;
}
public void dispose() {
this.textureData.dispose();
}
@Override
public int compareTo(@NotNull Country o) {
return this.name.compareTo(o.name);
}
public sealed interface TextureData {
@Nullable
default Texture get() {
return null;
}
default void dispose() {}
}
public final class CountryTextureData implements TextureData {
private Texture texture = null;
private State state = State.EMPTY;
@Nullable
@Override
public Texture get() {
if (this.state == State.DONE) return this.texture;
else {
if (this.state == State.EMPTY) MeteorExecutor.execute(this::load);
return null;
}
}
@Override
public void dispose() {
if (this.state == State.DONE) {
this.texture.dispose();
this.texture = null;
}
this.state = State.EMPTY;
}
private void load() {
this.state = State.LOADING;
Resource textureResource = mc.getResourceManager().getResource(Country.this.identifier).orElseThrow();
try (InputStream imageStream = textureResource.getInputStream()) {
BufferedImage bufferedImage = ImageIO.read(imageStream);
int[] pixels = bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null, 0, bufferedImage.getWidth());
byte[] data = new byte[bufferedImage.getWidth() * bufferedImage.getHeight() * 3];
for (int i = 0; i < pixels.length; i++) {
data[3 * i] = (byte) ((pixels[i] >> 16) & 0xFF); // r
data[3 * i + 1] = (byte) ((pixels[i] >> 8) & 0xFF); // g
data[3 * i + 2] = (byte) ((pixels[i]) & 0xFF); // b
}
this.texture = new Texture(bufferedImage.getWidth(), bufferedImage.getHeight(), data, Texture.Format.RGB, Texture.Filter.Nearest, Texture.Filter.Nearest);
this.state = State.DONE;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public enum State {
EMPTY,
LOADING,
DONE
}
}
public static final class EmptyTextureData implements TextureData {}
}

View File

@@ -1,93 +0,0 @@
package de.damcraft.serverseeker.country;
import de.damcraft.serverseeker.ServerSeeker;
import meteordevelopment.meteorclient.gui.GuiTheme;
import meteordevelopment.meteorclient.gui.renderer.GuiRenderer;
import meteordevelopment.meteorclient.gui.widgets.WLabel;
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.settings.IVisible;
import meteordevelopment.meteorclient.settings.Setting;
import net.minecraft.nbt.NbtCompound;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class CountrySetting extends Setting<Country> {
public final Predicate<Country> filter;
public CountrySetting(String name, String description, Country defaultValue, Consumer<Country> onChanged, Consumer<Setting<Country>> onModuleActivated, IVisible visible, Predicate<Country> filter) {
super(name, description, defaultValue, onChanged, onModuleActivated, visible);
this.filter = filter;
}
public static void countrySettingW(WTable table, CountrySetting setting, GuiTheme theme) {
WHorizontalList list = table.add(theme.horizontalList()).expandX().widget();
WCountry country = list.add(new WCountry(setting.get())).widget();
String name = setting.get().name;
WLabel nameLabel = list.add(theme.label(name)).widget();
WButton select = list.add(theme.button("Select")).widget();
select.action = () -> {
CountrySettingScreen screen = new CountrySettingScreen(theme, setting);
screen.onClosed(() -> {
country.set(setting.get());
nameLabel.set(setting.get().name);
});
mc.setScreen(screen);
};
WButton reset = list.add(theme.button(GuiRenderer.RESET)).expandCellX().right().widget();
reset.action = () -> {
setting.reset();
country.set(ServerSeeker.COUNTRY_MAP.get("UN"));
nameLabel.set("Any");
};
}
@Override
protected Country parseImpl(String str) {
return ServerSeeker.COUNTRY_MAP.get(str);
}
@Override
protected boolean isValueValid(Country value) {
return value.code != null && ServerSeeker.COUNTRY_MAP.containsValue(value);
}
@Override
protected NbtCompound save(NbtCompound tag) {
tag.putString("value", get().code);
return tag;
}
@Override
protected Country load(NbtCompound tag) {
return ServerSeeker.COUNTRY_MAP.get(tag.getString("value"));
}
public static class Builder extends SettingBuilder<Builder, Country, CountrySetting> {
private Predicate<Country> filter;
public Builder() {
super(null);
}
public CountrySetting.Builder filter(Predicate<Country> filter) {
this.filter = filter;
return this;
}
@Override
public CountrySetting build() {
return new CountrySetting(name, description, defaultValue, onChanged, onModuleActivated, visible, filter);
}
}
}

View File

@@ -1,72 +0,0 @@
package de.damcraft.serverseeker.country;
import de.damcraft.serverseeker.ServerSeeker;
import meteordevelopment.meteorclient.gui.GuiTheme;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.WLabel;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.input.WTextBox;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.utils.render.color.Color;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Collection;
public class CountrySettingScreen extends WindowScreen {
private final CountrySetting setting;
private WTable table;
private WTextBox filter;
private String filterText = "";
public CountrySettingScreen(GuiTheme theme, CountrySetting setting) {
super(theme, "Select Country");
this.setting = setting;
}
@Override
public void initWidgets() {
filter = add(theme.textBox("")).minWidth(400).expandX().widget();
filter.setFocused(true);
filter.action = () -> {
filterText = filter.get().trim();
table.clear();
initTable();
};
table = add(theme.table()).expandX().widget();
initTable();
}
private void initTable() {
Collection<Country> countries = ServerSeeker.COUNTRY_MAP.values();
// Sort alphabetically. Save to array to avoid concurrent modification.
Country[] countryArray = countries.toArray(new Country[0]);
Arrays.sort(countryArray);
for (Country country : countryArray) {
if (setting.filter != null && !setting.filter.test(country)) continue;
boolean isSelected = country == setting.get();
if (!filterText.isEmpty() && (
!StringUtils.containsIgnoreCase(country.name, filterText) && !StringUtils.containsIgnoreCase(country.code, filterText)
)) continue;
table.add(new WCountry(country)).widget();
WLabel label = table.add(theme.label(country.name)).widget();
if (isSelected) label.color = Color.GREEN;
WButton select = table.add(theme.button("Select")).expandCellX().right().widget();
select.action = () -> {
setting.set(country);
close();
};
table.row();
}
}
}

View File

@@ -1,10 +0,0 @@
package de.damcraft.serverseeker.country;
/**
* Default texture, always loaded, should only be used for UN
*/
public class DefaultCountry extends Country {
public DefaultCountry(String name, String code) {
super(name, code);
}
}

View File

@@ -1,42 +0,0 @@
package de.damcraft.serverseeker.country;
import meteordevelopment.meteorclient.gui.renderer.GuiRenderer;
import meteordevelopment.meteorclient.gui.widgets.WWidget;
import meteordevelopment.meteorclient.renderer.Texture;
public class WCountry extends WWidget {
private Country country;
public WCountry(Country country) {
this.country = country;
}
public void set(Country country) {
this.country = country;
}
@Override
protected void onCalculateSize() {
double s = theme.scale(32);
width = s;
height = s;
}
@Override
protected void onRender(GuiRenderer renderer, double mouseX, double mouseY, double delta) {
Texture texture = this.country.getTexture();
if (texture == null) return;
int wanted_height = (int) (super.width * texture.height / texture.width);
// Center y
int wanted_y = (int) (y + (super.height - wanted_height) / 2);
if (texture.isValid()) {
renderer.texture(x, wanted_y, super.width, wanted_height, 0, texture);
}
}
}

View File

@@ -1,511 +0,0 @@
package de.damcraft.serverseeker.gui;
import com.google.common.net.HostAndPort;
import de.damcraft.serverseeker.ServerSeeker;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.country.Country;
import de.damcraft.serverseeker.country.CountrySetting;
import de.damcraft.serverseeker.ssapi.requests.ServersRequest;
import de.damcraft.serverseeker.ssapi.responses.ServersResponse;
import de.damcraft.serverseeker.utils.MCVersionUtil;
import de.damcraft.serverseeker.utils.MultiplayerScreenUtil;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WContainer;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.settings.*;
import meteordevelopment.meteorclient.utils.network.MeteorExecutor;
import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.client.network.ServerInfo;
import net.minecraft.nbt.NbtCompound;
import java.util.List;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class FindNewServersScreen extends WindowScreen {
public static NbtCompound savedSettings;
private int timer;
public WButton findButton;
private boolean threadHasFinished;
private String threadError;
private List<ServersResponse.Server> threadServers;
public enum Cracked {
Any,
Yes,
No;
public Boolean toBoolOrNull() {
return switch (this) {
case Any -> null;
case Yes -> true;
case No -> false;
};
}
}
public enum Version {
Current,
Any,
Protocol,
VersionString;
@Override
public String toString() {
return switch (this) {
case Current -> "Current";
case Any -> "Any";
case Protocol -> "Protocol";
case VersionString -> "Version String";
};
}
}
public enum NumRangeType {
Any,
Equals,
AtLeast,
AtMost,
Between;
@Override
public String toString() {
return switch (this) {
case Any -> "Any";
case Equals -> "Equal To";
case AtLeast -> "At Least";
case AtMost -> "At Most";
case Between -> "Between";
};
}
}
// Didn't have a better name
public enum GeoSearchType {
None,
ASN,
Country
}
private final Settings settings = new Settings();
private final SettingGroup sg = settings.getDefaultGroup();
WContainer settingsContainer;
private final Setting<Cracked> crackedSetting = sg.add(new EnumSetting.Builder<Cracked>()
.name("cracked")
.description("Whether the server should be cracked or not")
.defaultValue(Cracked.Any)
.build()
);
private final Setting<NumRangeType> onlinePlayersNumTypeSetting = sg.add(new EnumSetting.Builder<NumRangeType>()
.name("online-players-range")
.description("The type of number range for the online players")
.defaultValue(NumRangeType.Any)
.build()
);
private final Setting<Integer> equalsOnlinePlayersSetting = sg.add(new IntSetting.Builder()
.name("online-players")
.description("The amount of online players the server should have")
.defaultValue(2)
.min(0)
.visible(() -> onlinePlayersNumTypeSetting.get().equals(NumRangeType.Equals))
.noSlider()
.build()
);
private final Setting<Integer> atLeastOnlinePlayersSetting = sg.add(new IntSetting.Builder()
.name("minimum-online-players")
.description("The minimum amount of online players the server should have")
.defaultValue(1)
.min(0)
.visible(() -> onlinePlayersNumTypeSetting.get().equals(NumRangeType.AtLeast) || onlinePlayersNumTypeSetting.get().equals(NumRangeType.Between))
.noSlider()
.build()
);
private final Setting<Integer> atMostOnlinePlayersSetting = sg.add(new IntSetting.Builder()
.name("maximum-online-players")
.description("The maximum amount of online players the server should have")
.defaultValue(20)
.min(0)
.visible(() -> onlinePlayersNumTypeSetting.get().equals(NumRangeType.AtMost) || onlinePlayersNumTypeSetting.get().equals(NumRangeType.Between))
.noSlider()
.build()
);
private final Setting<NumRangeType> maxPlayersNumTypeSetting = sg.add(new EnumSetting.Builder<NumRangeType>()
.name("max-players-range")
.description("The type of number range for the max players")
.defaultValue(NumRangeType.Any)
.build()
);
private final Setting<Integer> equalsMaxPlayersSetting = sg.add(new IntSetting.Builder()
.name("max-players")
.description("The amount of max players the server should have")
.defaultValue(2)
.min(0)
.visible(() -> maxPlayersNumTypeSetting.get().equals(NumRangeType.Equals))
.noSlider()
.build()
);
private final Setting<Integer> atLeastMaxPlayersSetting = sg.add(new IntSetting.Builder()
.name("minimum-max-players")
.description("The minimum amount of max players the server should have")
.defaultValue(1)
.min(0)
.visible(() -> maxPlayersNumTypeSetting.get().equals(NumRangeType.AtLeast) || maxPlayersNumTypeSetting.get().equals(NumRangeType.Between))
.noSlider()
.build()
);
private final Setting<Integer> atMostMaxPlayersSetting = sg.add(new IntSetting.Builder()
.name("maximum-max-players")
.description("The maximum amount of max players the server should have")
.defaultValue(20)
.min(0)
.visible(() -> maxPlayersNumTypeSetting.get().equals(NumRangeType.AtMost) || maxPlayersNumTypeSetting.get().equals(NumRangeType.Between))
.noSlider()
.build()
);
private final Setting<String> descriptionSetting = sg.add(new StringSetting.Builder()
.name("MOTD")
.description("What the MOTD of the server should contain (empty for any)")
.defaultValue("")
.build()
);
private final Setting<ServersRequest.Software> softwareSetting = sg.add(new EnumSetting.Builder<ServersRequest.Software>()
.name("software")
.description("The server software the servers should have")
.defaultValue(ServersRequest.Software.Any)
.build()
);
private final Setting<Version> versionSetting = sg.add(new EnumSetting.Builder<Version>()
.name("version")
.description("The protocol version the servers should have")
.defaultValue(Version.Current)
.build()
);
private final Setting<Integer> protocolVersionSetting = sg.add(new IntSetting.Builder()
.name("protocol")
.description("The protocol version the servers should have")
.defaultValue(SharedConstants.getProtocolVersion())
.visible(() -> versionSetting.get() == Version.Protocol)
.min(0)
.noSlider()
.build()
);
private final Setting<String> versionStringSetting = sg.add(new StringSetting.Builder()
.name("version-string")
.description("The version string (e.g. 1.19.3) of the protocol version the server should have, results may contain different versions that have the same protocol version. Must be at least 1.7.1")
.defaultValue("1.20.2")
.visible(() -> versionSetting.get() == Version.VersionString)
.build()
);
private final Setting<Boolean> onlineOnlySetting = sg.add(new BoolSetting.Builder()
.name("online-only")
.description("Whether to only show servers that are online")
.defaultValue(true)
.build()
);
private final Setting<Boolean> ignoreModded = sg.add(new BoolSetting.Builder()
.name("ignore-modded")
.description("Will not give you servers where mods have been detected")
.defaultValue(true)
.build()
);
private final Setting<Boolean> onlyBungeeSpoofable = sg.add(new BoolSetting.Builder()
.name("only-bungee-spoofable")
.description("Will only give you servers where you can use BungeeSpoof")
.defaultValue(false)
.build()
);
private final Setting<GeoSearchType> geoSearchTypeSetting = sg.add(new EnumSetting.Builder<GeoSearchType>()
.name("geo-search-type")
.description("Whether to search by ASN or country code")
.defaultValue(GeoSearchType.Country)
.build()
);
private final Setting<Integer> asnNumberSetting = sg.add(new IntSetting.Builder()
.name("asn")
.description("The ASN of the server")
.defaultValue(24940)
.noSlider()
.visible(() -> geoSearchTypeSetting.get() == GeoSearchType.ASN)
.build()
);
private final Setting<Country> countrySetting = sg.add(new CountrySetting.Builder()
.name("country")
.description("The country the server should be located in")
.defaultValue(ServerSeeker.COUNTRY_MAP.get("UN"))
.visible(() -> geoSearchTypeSetting.get() == GeoSearchType.Country)
.build()
);
MultiplayerScreen multiplayerScreen;
public FindNewServersScreen(MultiplayerScreen multiplayerScreen) {
super(GuiThemes.get(), "Find new servers");
this.multiplayerScreen = multiplayerScreen;
}
@Override
public void initWidgets() {
loadSettings();
onClosed(this::saveSettings);
settingsContainer = add(theme.verticalList()).widget();
settingsContainer.add(theme.settings(settings));
add(theme.button("Reset all")).expandX().widget().action = this::resetSettings;
findButton = add(theme.button("Find")).expandX().widget();
findButton.action = () -> {
ServersRequest request = new ServersRequest();
switch (onlinePlayersNumTypeSetting.get()) {
// [n, "inf"]
case AtLeast -> request.setOnlinePlayers(atLeastOnlinePlayersSetting.get(), -1);
// [0, n]
case AtMost -> request.setOnlinePlayers(0, atMostOnlinePlayersSetting.get());
// [min, max]
case Between -> request.setOnlinePlayers(atLeastOnlinePlayersSetting.get(), atMostOnlinePlayersSetting.get());
// [n, n]
case Equals -> request.setOnlinePlayers(equalsOnlinePlayersSetting.get());
}
switch (maxPlayersNumTypeSetting.get()) {
// [n, "inf"]
case AtLeast -> request.setMaxPlayers(atLeastMaxPlayersSetting.get(), -1);
// [0, n]
case AtMost -> request.setMaxPlayers(0, atMostMaxPlayersSetting.get());
// [min, max]
case Between -> request.setMaxPlayers(atLeastMaxPlayersSetting.get(), atMostMaxPlayersSetting.get());
// [n, n]
case Equals -> request.setMaxPlayers(equalsMaxPlayersSetting.get());
}
switch (geoSearchTypeSetting.get()) {
case ASN -> request.setAsn(asnNumberSetting.get());
case Country -> {
if (countrySetting.get().name.equalsIgnoreCase("any")) break;
request.setCountryCode(countrySetting.get().code);
}
}
request.setCracked(crackedSetting.get().toBoolOrNull());
request.setDescription(descriptionSetting.get());
request.setSoftware(softwareSetting.get());
switch (versionSetting.get()) {
case Protocol -> request.setProtocolVersion(protocolVersionSetting.get());
case VersionString -> {
Integer protocol = MCVersionUtil.versionToProtocol(versionStringSetting.get());
if (protocol == null) {
clear();
add(theme.label("Unknown version string"));
return;
}
request.setProtocolVersion(protocol);
}
case Current -> request.setProtocolVersion(SharedConstants.getProtocolVersion());
}
if (!onlineOnlySetting.get()) request.setOnlineAfter(0);
if (ignoreModded.get()) request.setIgnoreModded(true);
if (onlyBungeeSpoofable.get()) request.setOnlyBungeeSpoofable(true);
this.locked = true;
this.threadHasFinished = false;
this.threadError = null;
this.threadServers = null;
MeteorExecutor.execute(() -> {
String jsonResp = SmallHttp.post("https://api.serverseeker.net/servers", request.json());
ServersResponse resp = gson.fromJson(jsonResp, ServersResponse.class);
// Set error message if there is one
if (resp.isError()) {
this.threadError = resp.error;
this.threadHasFinished = true;
return;
}
this.threadServers = resp.data;
this.threadHasFinished = true;
});
};
}
@Override
public void tick() {
super.tick();
settings.tick(settingsContainer, theme);
if (threadHasFinished) handleThreadFinish();
if (locked) {
if (timer > 2) {
findButton.set(getNext(findButton));
timer = 0;
}
else {
timer++;
}
}
else if (!findButton.getText().equals("Find")) {
findButton.set("Find");
}
}
@Override
protected void onClosed() {
ServerSeeker.COUNTRY_MAP.values().forEach(Country::dispose);
}
private String getNext(WButton add) {
return switch (add.getText()) {
case "Find", "oo0" -> "ooo";
case "ooo" -> "0oo";
case "0oo" -> "o0o";
case "o0o" -> "oo0";
default -> "Find";
};
}
private void handleThreadFinish() {
this.threadHasFinished = false;
this.locked = false;
if (this.threadError != null) {
clear();
add(theme.label(this.threadError)).expandX();
WButton backButton = add(theme.button("Back")).expandX().widget();
backButton.action = this::reload;
this.locked = false;
return;
}
clear();
List<ServersResponse.Server> servers = this.threadServers;
if (servers.isEmpty()) {
add(theme.label("No servers found")).expandX();
WButton backButton = add(theme.button("Back")).expandX().widget();
backButton.action = this::reload;
this.locked = false;
return;
}
add(theme.label("Found " + servers.size() + " servers")).expandX();
WButton addAllButton = add(theme.button("Add all")).expandX().widget();
addAllButton.action = () -> {
for (ServersResponse.Server server : servers) {
String ip = server.server;
// Add server to list
MultiplayerScreenUtil.addNameIpToServerList(multiplayerScreen, "ServerSeeker " + ip, ip, false);
}
MultiplayerScreenUtil.saveList(multiplayerScreen);
// Reload widget
MultiplayerScreenUtil.reloadServerList(multiplayerScreen);
// Close screen
if (this.client == null) return;
client.setScreen(this.multiplayerScreen);
};
WTable table = add(theme.table()).widget();
table.add(theme.label("Server IP"));
table.add(theme.label("Version"));
table.row();
table.add(theme.horizontalSeparator()).expandX();
table.row();
for (ServersResponse.Server server : servers) {
final String serverIP = server.server;
String serverVersion = server.version;
table.add(theme.label(serverIP));
table.add(theme.label(serverVersion));
WButton addServerButton = theme.button("Add Server");
addServerButton.action = () -> {
System.out.println(multiplayerScreen.getServerList() == null);
ServerInfo info = new ServerInfo("ServerSeeker " + serverIP, serverIP, ServerInfo.ServerType.OTHER);
MultiplayerScreenUtil.addInfoToServerList(multiplayerScreen, info);
addServerButton.visible = false;
};
WButton joinServerButton = theme.button("Join Server");
HostAndPort hap = HostAndPort.fromString(serverIP);
joinServerButton.action = ()
-> ConnectScreen.connect(new TitleScreen(), MinecraftClient.getInstance(), new ServerAddress(hap.getHost(), hap.getPort()), new ServerInfo("a", hap.toString(), ServerInfo.ServerType.OTHER), false, null);
WButton serverInfoButton = theme.button("Server Info");
serverInfoButton.action = () -> this.client.setScreen(new ServerInfoScreen(serverIP));
table.add(addServerButton);
table.add(joinServerButton);
table.add(serverInfoButton);
table.row();
}
this.locked = false;
}
public void saveSettings() {
savedSettings = sg.toTag();
}
public void loadSettings() {
if (savedSettings == null) return;
sg.fromTag(savedSettings);
}
public void resetSettings() {
for (Setting<?> setting : sg) {
setting.reset();
}
saveSettings();
reload();
}
}

View File

@@ -1,174 +0,0 @@
package de.damcraft.serverseeker.gui;
import com.google.common.net.HostAndPort;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.ssapi.requests.WhereisRequest;
import de.damcraft.serverseeker.ssapi.responses.WhereisResponse;
import de.damcraft.serverseeker.utils.MultiplayerScreenUtil;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WContainer;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.settings.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.client.network.ServerInfo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import static de.damcraft.serverseeker.ServerSeeker.gson;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class FindPlayerScreen extends WindowScreen {
private final MultiplayerScreen multiplayerScreen;
public enum NameOrUUID {
Name,
UUID
}
private final Settings settings = new Settings();
private final SettingGroup sg = settings.getDefaultGroup();
private final Setting<NameOrUUID> nameOrUUID = sg.add(new EnumSetting.Builder<NameOrUUID>()
.name("name-or-uuid")
.description("Whether to search by name or UUID.")
.defaultValue(NameOrUUID.Name)
.build()
);
private final Setting<String> name = sg.add(new StringSetting.Builder()
.name("name")
.description("The name to search for.")
.defaultValue("")
.visible(() -> nameOrUUID.get() == NameOrUUID.Name)
.build()
);
private final Setting<String> uuid = sg.add(new StringSetting.Builder()
.name("UUID")
.description("The UUID to search for.")
.defaultValue("")
.visible(() -> nameOrUUID.get() == NameOrUUID.UUID)
.build()
);
WContainer settingsContainer;
public FindPlayerScreen(MultiplayerScreen multiplayerScreen) {
super(GuiThemes.get(), "Find Players");
this.multiplayerScreen = multiplayerScreen;
}
@Override
public void initWidgets() {
WContainer settingsContainer = add(theme.verticalList()).widget();
settingsContainer.add(theme.settings(settings)).expandX();
this.settingsContainer = settingsContainer;
add(theme.button("Find Player")).expandX().widget().action = () -> {
WhereisRequest request = new WhereisRequest();
switch (nameOrUUID.get()) {
case Name -> request.setName(name.get());
case UUID -> request.setUuid(uuid.get());
}
String jsonResponse = SmallHttp.post("https://api.serverseeker.net/whereis", request.json());
WhereisResponse resp = gson.fromJson(jsonResponse, WhereisResponse.class);
// Set error message if there is one
if (resp.isError()) {
clear();
add(theme.label(resp.error)).expandX();
return;
}
clear();
List<WhereisResponse.Record> data = resp.data;
if (data.isEmpty()) {
clear();
add(theme.label("Not found")).expandX();
return;
}
add(theme.label("Found " + data.size() + " servers:"));
WTable table = add(theme.table()).widget();
WButton addAllButton = table.add(theme.button("Add all")).expandX().widget();
addAllButton.action = () -> addAllServers(data);
table.row();
table.add(theme.label("Server IP"));
table.add(theme.label("Player name"));
table.add(theme.label("Last seen"));
table.row();
table.add(theme.horizontalSeparator()).expandX();
table.row();
for (WhereisResponse.Record server : data) {
String serverIP = server.server;
String playerName = server.name;
long playerLastSeen = server.last_seen; // Unix timestamp
// Format last seen to human-readable
String playerLastSeenFormatted = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.format(Instant.ofEpochSecond(playerLastSeen).atZone(ZoneId.systemDefault()).toLocalDateTime());
int minWidth = (int)(mc.getWindow().getWidth() * 0.2);
table.add(theme.label(serverIP)).minWidth(minWidth);
table.add(theme.label(playerName)).minWidth(minWidth);
table.add(theme.label(playerLastSeenFormatted)).minWidth(minWidth);
WButton addServerButton = theme.button("Add Server");
addServerButton.action = () -> {
ServerInfo info = new ServerInfo("ServerSeeker " + serverIP + " (Player: " + playerName + ")", serverIP, ServerInfo.ServerType.OTHER);
MultiplayerScreenUtil.addInfoToServerList(multiplayerScreen, info);
addServerButton.visible = false;
};
HostAndPort hap = HostAndPort.fromString(serverIP);
WButton joinServerButton = theme.button("Join Server");
joinServerButton.action = () -> {
ConnectScreen.connect(new TitleScreen(), MinecraftClient.getInstance(), new ServerAddress(hap.getHost(), hap.getPort()), new ServerInfo("a", hap.toString(), ServerInfo.ServerType.OTHER), false, null);
};
WButton serverInfoButton = theme.button("Server Info");
serverInfoButton.action = () -> this.client.setScreen(new ServerInfoScreen(serverIP));
table.add(addServerButton);
table.add(joinServerButton);
table.add(serverInfoButton);
table.row();
}
};
}
private void addAllServers(List<WhereisResponse.Record> records) {
for (WhereisResponse.Record record : records) {
String serverIP = record.server;
String playerName = record.name;
ServerInfo info = new ServerInfo("ServerSeeker " + serverIP + " (Player: " + playerName + ")", serverIP, ServerInfo.ServerType.OTHER);
MultiplayerScreenUtil.addInfoToServerList(multiplayerScreen, info, false);
}
MultiplayerScreenUtil.saveList(multiplayerScreen);
if (client == null) return;
client.setScreen(this.multiplayerScreen);
}
@Override
public void tick() {
super.tick();
settings.tick(settingsContainer, theme);
}
}

View File

@@ -1,186 +0,0 @@
package de.damcraft.serverseeker.gui;
import de.damcraft.serverseeker.ServerSeekerSystem;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.ssapi.requests.ServerInfoRequest;
import de.damcraft.serverseeker.ssapi.responses.ServerInfoResponse;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.systems.accounts.Account;
import meteordevelopment.meteorclient.systems.accounts.Accounts;
import meteordevelopment.meteorclient.systems.accounts.types.CrackedAccount;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.network.ServerInfo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import static de.damcraft.serverseeker.ServerSeeker.gson;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class GetInfoScreen extends WindowScreen {
MultiplayerServerListWidget.Entry entry;
private boolean waitingForAuth = false;
public GetInfoScreen(MultiplayerScreen multiplayerScreen, MultiplayerServerListWidget.Entry entry) {
super(GuiThemes.get(), "Get players");
this.parent = multiplayerScreen;
this.entry = entry;
}
@Override
public void initWidgets() {
if (entry == null) {
add(theme.label("No server selected"));
return;
}
String apiKey = ServerSeekerSystem.get().apiKey;
if (apiKey.isEmpty()) {
WHorizontalList widgetList = add(theme.horizontalList()).expandX().widget();
widgetList.add(theme.label("Please authenticate with Discord. "));
waitingForAuth = true;
WButton loginButton = widgetList.add(theme.button("Login")).widget();
loginButton.action = () -> {
if (this.client == null) return;
this.client.setScreen(new LoginWithDiscordScreen(this));
};
return;
}
// Get info about the server
if (!(entry instanceof MultiplayerServerListWidget.ServerEntry)) {
add(theme.label("No server selected"));
return;
}
ServerInfo serverInfo = ((MultiplayerServerListWidget.ServerEntry) entry).getServer();
String address = serverInfo.address;
// Check if the server matches the regex for ip(:port)
if (!address.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}(?::[0-9]{1,5})?$")) {
add(theme.label("You can only get player info for servers with an IP address"));
return;
}
String ip = address.split(":")[0];
int port = address.split(":").length > 1 ? Integer.parseInt(address.split(":")[1]) : 25565;
// Get the players using the API
/* {
"api_key": "...", // Your api_key
"ip": "109.123.240.84", // The ip of the server
"port": 25565 // The port of the server (defaults to 25565)
} */
ServerInfoRequest request = new ServerInfoRequest();
request.setIpPort(ip, port);
String jsonResp = SmallHttp.post("https://api.serverseeker.net/server_info", request.json());
ServerInfoResponse resp = gson.fromJson(jsonResp, ServerInfoResponse.class);
// Set error message if there is one
if (resp.isError()) {
clear();
add(theme.label(resp.error)).expandX();
return;
}
clear();
List<ServerInfoResponse.Player> players = resp.players;
if (players.isEmpty()) {
clear();
add(theme.label("No records of players found.")).expandX();
return;
}
/* "players": [ // An array of when which players were seen on the server. Limited to 1000
{
"last_seen": 1683790506, // The last time the player was seen on the server (unix timestamp)
"name": "DAMcraft", // The name of the player
"uuid": "68af4d98-24a2-41b6-96bc-a9c2ef9b397b" // The uuid of the player
}, ...
] */
boolean cracked = false;
if (resp.cracked != null) {
cracked = resp.cracked;
}
if (!cracked) {
add(theme.label("Attention: The server is NOT cracked!")).expandX();
add(theme.label("")).expandX();
}
String playersLabel = players.size() == 1 ? " player:" : " players:";
add(theme.label("Found " + players.size() + playersLabel));
WTable table = add(theme.table()).widget();
table.add(theme.label("Name "));
table.add(theme.label("Last seen "));
table.add(theme.label("Login (cracked)"));
table.row();
table.add(theme.horizontalSeparator()).expandX();
table.row();
for (ServerInfoResponse.Player player : players) {
String name = player.name;
long lastSeen = player.last_seen;
String lastSeenFormatted = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.format(Instant.ofEpochSecond(lastSeen).atZone(ZoneId.systemDefault()).toLocalDateTime());
table.add(theme.label(name + " "));
table.add(theme.label(lastSeenFormatted + " "));
if (mc.getSession().getUsername().equals(name)) {
table.add(theme.label("Logged in")).expandCellX();
} else {
WButton loginButton = table.add(theme.button("Login")).widget();
// Check if the user is currently logged in
if (mc.getSession().getUsername().equals(name)) {
loginButton.visible = false;
}
// Log in the user
loginButton.action = () -> {
loginButton.visible = false;
if (this.client == null) return;
// Check if the account already exists
boolean exists = false;
for (Account<?> account : Accounts.get()) {
if (account instanceof CrackedAccount && account.getUsername().equals(name)) {
account.login();
exists = true;
break;
}
}
if (!exists) {
CrackedAccount account = new CrackedAccount(name);
account.login();
Accounts.get().add(account);
}
close();
};
}
table.row();
}
}
@Override
public void tick() {
if (waitingForAuth) {
String authToken = ServerSeekerSystem.get().apiKey;
if (!authToken.isEmpty()) {
this.reload();
this.waitingForAuth = false;
}
}
}
}

View File

@@ -1,132 +0,0 @@
package de.damcraft.serverseeker.gui;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mojang.logging.LogUtils;
import de.damcraft.serverseeker.SmallHttp;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.net.http.HttpResponse;
import java.nio.file.*;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class InstallMeteorScreen extends Screen {
public InstallMeteorScreen() {
super(Text.of("Meteor Client is not installed!"));
}
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, this.height / 4 - 60 + 20, -1);
}
protected void init() {
super.init();
ButtonWidget quitButton = this.addDrawableChild(ButtonWidget.builder(Text.translatable("menu.quit"), (button) -> {
this.client.scheduleStop();
}).dimensions(this.width / 2 + 2, this.height / 4 + 100 + 25, 148, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.of("Automatically install Meteor (§arecommended§r)"), (button) -> {
quitButton.active = false;
CompletableFuture.runAsync(() -> {
install();
quitButton.active = true;
});
}).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.of("Manual installation"), (button) -> {
Util.getOperatingSystem().open("https://meteorclient.com/faq/installation");
}).dimensions(this.width / 2 - 150, this.height / 4 + 100 + 25, 148, 20).build());
}
private void install() {
String result = SmallHttp.get("https://meteorclient.com/api/stats");
if (result == null) {
this.displayError("Failed to get install meteor automatically! Please install it manually.");
return;
}
Gson gson = new Gson();
JsonObject json = gson.fromJson(result, JsonObject.class);
String currentVersion = SharedConstants.getGameVersion().getName();
String stableVersion = json.get("mc_version").getAsString();
String devBuildVersion = json.get("dev_build_mc_version").getAsString();
String url;
if (currentVersion.equals(stableVersion)) {
url = "https://meteorclient.com/api/download";
} else if (currentVersion.equals(devBuildVersion)) {
url = "https://meteorclient.com/api/download?devBuild=latest";
} else {
this.displayError("Failed to find Meteor for your current version.");
return;
}
HttpResponse<InputStream> file = SmallHttp.download(url);
if (file == null) {
this.displayError("Failed to download Meteor! Please install it manually.");
return;
}
Optional<String> filenameT = file.headers().firstValue("Content-Disposition");
String filename = "meteor-client.jar";
if (filenameT.isPresent()) {
String[] parts = filenameT.get().split("; ");
for (String part : parts) {
if (part.startsWith("filename=")) {
filename = part.substring(9);
break;
}
}
if (filename.startsWith("\"") && filename.endsWith("\"")) {
filename = filename.substring(1, filename.length() - 1);
}
}
// Get the mods folder
Path modsFolder = FabricLoader.getInstance().getGameDir().resolve("mods");
if (!Files.exists(modsFolder)) {
this.displayError("Failed to find mods folder! Please install Meteor manually.");
return;
}
// Save the file
try {
Files.copy(file.body(), modsFolder.resolve(filename));
} catch (IOException | SecurityException | InvalidPathException e) {
this.displayError("Failed to save Meteor! Please install it manually.");
LogUtils.getLogger().error(e.toString()); // we can't import without causing errors (no meteor)
return;
}
// Success message
this.displayNotice("Successfully installed Meteor! Please restart your game.");
}
private void displayError(String errorMessage) {
this.displayNotice(errorMessage);
this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> {
Util.getOperatingSystem().open("https://meteorclient.com/faq/installation");
}).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build());
}
private void displayNotice(String noticeMessage) {
this.clearChildren();
this.addDrawableChild(new TextWidget(
this.width / 2 - 250,
this.height / 4,
500,
20,
Text.of(noticeMessage),
this.textRenderer
));
}
}

View File

@@ -1,61 +0,0 @@
package de.damcraft.serverseeker.gui;
import de.damcraft.serverseeker.DiscordAuth;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class LoginWithDiscordScreen extends WindowScreen {
private boolean canClose = false;
WindowScreen parent;
public LoginWithDiscordScreen(WindowScreen parent) {
super(GuiThemes.get(), "Login with Discord");
this.parent = parent;
}
@Override
public void initWidgets() {
DiscordAuth.auth((apiKey, error) -> {
if (error != null) {
this.canClose = true;
clear();
add(theme.label("Failed to authenticate with Discord. Reason: " + error));
return;
}
if (apiKey == null) {
this.canClose = true;
clear();
add(theme.label("Failed to authenticate with Discord."));
return;
}
close();
});
add(theme.label("Please authenticate with Discord in your browser."));
add(theme.label("The browser didn't open? Click below to copy the link and open it manually")).expandX();
WButton copy = add(theme.button("Copy")).expandX().widget();
copy.action = () -> {
String url = DiscordAuth.url;
mc.keyboard.setClipboard(url);
};
WButton cancel = add(theme.button("Cancel")).expandX().widget();
cancel.action = () -> {
DiscordAuth.stopServer();
close();
};
}
@Override
public void tick() {}
@Override
public boolean shouldCloseOnEsc() {
return this.canClose;
}
}

View File

@@ -1,115 +0,0 @@
package de.damcraft.serverseeker.gui;
import com.google.common.net.HostAndPort;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.ssapi.requests.ServerInfoRequest;
import de.damcraft.serverseeker.ssapi.responses.ServerInfoResponse;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.client.network.ServerInfo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class ServerInfoScreen extends WindowScreen {
private final String serverIp;
public ServerInfoScreen(String serverIp) {
super(GuiThemes.get(), "Server Info: " + serverIp);
this.serverIp = serverIp;
}
@Override
public void initWidgets() {
add(theme.label("Fetching server info..."));
ServerInfoRequest request = new ServerInfoRequest();
HostAndPort hap = HostAndPort.fromString(serverIp);
request.setIpPort(hap.getHost(), hap.getPort());
String jsonResp = SmallHttp.post("https://api.serverseeker.net/server_info", request.json());
ServerInfoResponse resp = gson.fromJson(jsonResp, ServerInfoResponse.class);
if (resp.isError()) {
clear();
add(theme.label(resp.error)).expandX();
return;
}
clear();
Boolean cracked = resp.cracked;
String description = resp.description;
int onlinePlayers = resp.online_players;
int maxPlayers = resp.max_players;
int protocol = resp.protocol;
int lastSeen = resp.last_seen;
String version = resp.version;
List<ServerInfoResponse.Player> players = resp.players;
WTable dataTable = add(theme.table()).widget();
WTable playersTable = add(theme.table()).expandX().widget();
dataTable.add(theme.label("Cracked: "));
dataTable.add(theme.label(cracked == null ? "Unknown" : cracked.toString()));
dataTable.row();
dataTable.add(theme.label("Description: "));
if (description.length() > 100) description = description.substring(0, 100) + "...";
description = description.replace("\n", "\\n");
description = description.replace("§r", "");
dataTable.add(theme.label(description));
dataTable.row();
dataTable.add(theme.label("Online Players (last scan): "));
dataTable.add(theme.label(String.valueOf(onlinePlayers)));
dataTable.row();
dataTable.add(theme.label("Max Players: "));
dataTable.add(theme.label(String.valueOf(maxPlayers)));
dataTable.row();
dataTable.add(theme.label("Last Seen: "));
String lastSeenDate = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.format(Instant.ofEpochSecond(lastSeen).atZone(ZoneId.systemDefault()).toLocalDateTime());
dataTable.add(theme.label(lastSeenDate));
dataTable.row();
dataTable.add(theme.label("Version: "));
dataTable.add(theme.label(version + " (" + protocol + ")"));
playersTable.add(theme.label(""));
playersTable.row();
playersTable.add(theme.label("Players:"));
playersTable.row();
playersTable.add(theme.label("Name ")).expandX();
playersTable.add(theme.label("Last seen ")).expandX();
playersTable.row();
playersTable.add(theme.horizontalSeparator()).expandX();
playersTable.row();
for (ServerInfoResponse.Player player : players) {
String name = player.name;
long playerLastSeen = player.last_seen;
String lastSeenFormatted = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.format(Instant.ofEpochSecond(playerLastSeen).atZone(ZoneId.systemDefault()).toLocalDateTime());
playersTable.add(theme.label(name + " ")).expandX();
playersTable.add(theme.label(lastSeenFormatted + " ")).expandX();
playersTable.row();
}
WButton joinServerButton = add(theme.button("Join this Server")).expandX().widget();
joinServerButton.action = ()
-> ConnectScreen.connect(new TitleScreen(), MinecraftClient.getInstance(), new ServerAddress(hap.getHost(), hap.getPort()), new ServerInfo("a", hap.toString(), ServerInfo.ServerType.OTHER), false, null);
}
}

View File

@@ -1,142 +0,0 @@
package de.damcraft.serverseeker.gui;
import de.damcraft.serverseeker.DiscordAvatar;
import de.damcraft.serverseeker.ServerSeekerSystem;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.ssapi.responses.UserInfoResponse;
import de.damcraft.serverseeker.utils.MultiplayerScreenUtil;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
import meteordevelopment.meteorclient.utils.render.color.Color;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class ServerSeekerScreen extends WindowScreen {
private final MultiplayerScreen multiplayerScreen;
public ServerSeekerScreen(MultiplayerScreen multiplayerScreen) {
super(GuiThemes.get(), "ServerSeeker");
this.multiplayerScreen = multiplayerScreen;
}
private boolean waitingForAuth = false;
@Override
public void initWidgets() {
String authToken = ServerSeekerSystem.get().apiKey;
if (authToken.isEmpty()) {
WHorizontalList widgetList = add(theme.horizontalList()).expandX().widget();
widgetList.add(theme.label("Please authenticate with Discord. "));
waitingForAuth = true;
WButton loginButton = widgetList.add(theme.button("Login")).widget();
loginButton.action = () -> {
if (this.client == null) return;
this.client.setScreen(new LoginWithDiscordScreen(this));
};
return;
}
WHorizontalList accountList = add(theme.horizontalList()).expandX().widget();
// Add an image of the user's avatar
String avatarUrl = ServerSeekerSystem.get().discordAvatarUrl;
if (avatarUrl != null && !avatarUrl.isEmpty()) {
accountList.add(theme.texture(32, 32, 0, new DiscordAvatar(ServerSeekerSystem.get().discordAvatarUrl + "?size=32")));
}
accountList.add(theme.label(ServerSeekerSystem.get().discordUsername)).expandX();
WButton logoutButton = accountList.add(theme.button("Logout")).widget();
logoutButton.action = () -> {
ServerSeekerSystem.get().apiKey = "";
ServerSeekerSystem.get().discordId = "";
ServerSeekerSystem.get().discordUsername = "";
ServerSeekerSystem.get().discordAvatarUrl = "";
ServerSeekerSystem.get().save();
reload();
};
WTable userInfoList = add(theme.table()).widget();
userInfoList.add(theme.label("Loading..."));
new Thread(() -> {
String reqBody = "{\"api_key\":\"" + authToken + "\"}";
String userInfoJson = SmallHttp.post("https://api.serverseeker.net/user_info", reqBody);
UserInfoResponse userInfo = gson.fromJson(userInfoJson, UserInfoResponse.class);
userInfoList.clear();
userInfoList.add(theme.label("Requests made:"));
userInfoList.row();
int whereisRequestsMade = userInfo.requests_made_whereis;
int whereisRequestsTotal = userInfo.requests_per_day_whereis;
userInfoList.add(theme.label("Whereis: "));
userInfoList.add(theme.label(whereisRequestsMade + "/" + whereisRequestsTotal)).widget().color(whereisRequestsTotal == whereisRequestsMade ? Color.RED : Color.WHITE);
userInfoList.row();
int serversRequestsMade = userInfo.requests_made_servers;
int serversRequestsTotal = userInfo.requests_per_day_servers;
userInfoList.add(theme.label("Servers: "));
userInfoList.add(theme.label(serversRequestsMade + "/" + serversRequestsTotal)).widget().color(serversRequestsTotal == serversRequestsMade ? Color.RED : Color.WHITE);
userInfoList.row();
int serverInfoRequestsMade = userInfo.requests_made_server_info;
int serverInfoRequestsTotal = userInfo.requests_per_day_server_info;
userInfoList.add(theme.label("Server Info: "));
userInfoList.add(theme.label(serverInfoRequestsMade + "/" + serverInfoRequestsTotal)).widget().color(serverInfoRequestsTotal == serverInfoRequestsMade ? Color.RED : Color.WHITE);
}).start();
WHorizontalList widgetList = add(theme.horizontalList()).expandX().widget();
WButton newServersButton = widgetList.add(this.theme.button("Find new servers")).expandX().widget();
WButton findPlayersButton = widgetList.add(this.theme.button("Search players")).expandX().widget();
WButton cleanUpServersButton = widgetList.add(this.theme.button("Clean up")).expandX().widget();
newServersButton.action = () -> {
if (this.client == null) return;
this.client.setScreen(new FindNewServersScreen(this.multiplayerScreen));
};
findPlayersButton.action = () -> {
if (this.client == null) return;
this.client.setScreen(new FindPlayerScreen(this.multiplayerScreen));
};
cleanUpServersButton.action = () -> {
if (this.client == null) return;
clear();
add(theme.label("Are you sure you want to clean up your server list?"));
add(theme.label("This will remove all servers that start with \"ServerSeeker\""));
WHorizontalList buttonList = add(theme.horizontalList()).expandX().widget();
WButton backButton = buttonList.add(theme.button("Back")).expandX().widget();
backButton.action = this::reload;
WButton confirmButton = buttonList.add(theme.button("Confirm")).expandX().widget();
confirmButton.action = this::cleanUpServers;
};
}
@Override
public void tick() {
if (waitingForAuth) {
String authToken = ServerSeekerSystem.get().apiKey;
if (!authToken.isEmpty()) {
this.reload();
this.waitingForAuth = false;
}
}
}
public void cleanUpServers() {
if (this.client == null) return;
for (int i = 0; i < this.multiplayerScreen.getServerList().size(); i++) {
if (this.multiplayerScreen.getServerList().get(i).name.startsWith("ServerSeeker")) {
this.multiplayerScreen.getServerList().remove(this.multiplayerScreen.getServerList().get(i));
i--;
}
}
MultiplayerScreenUtil.saveList(multiplayerScreen);
MultiplayerScreenUtil.reloadServerList(multiplayerScreen);
client.setScreen(this.multiplayerScreen);
}
}

View File

@@ -1,189 +0,0 @@
package de.damcraft.serverseeker.hud;
import de.damcraft.serverseeker.ssapi.responses.ServerInfoResponse;
import de.damcraft.serverseeker.utils.HistoricPlayersUpdater;
import meteordevelopment.meteorclient.gui.GuiThemes;
import meteordevelopment.meteorclient.settings.*;
import meteordevelopment.meteorclient.systems.hud.*;
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
import net.minecraft.client.network.PlayerListEntry;
import java.util.ArrayList;
import java.util.List;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class HistoricPlayersHud extends HudElement {
public List<ServerInfoResponse.Player> players = List.of();
public Boolean isCracked = false;
public final static HudElementInfo<HistoricPlayersHud> INFO = new HudElementInfo<>(Hud.GROUP, "historic-players", "Displays players that were on this server in the past.", HistoricPlayersHud::new);
private final SettingGroup sgGeneral = settings.getDefaultGroup();
public HistoricPlayersHud() {
super(INFO);
new Thread(HistoricPlayersUpdater::update).start();
}
private final Setting<Boolean> showCrackedText = sgGeneral.add(new BoolSetting.Builder()
.name("show-cracked-text")
.description("Shows the cracked text.")
.defaultValue(true)
.build()
);
private final Setting<Boolean> showCurrentPlayers = sgGeneral.add(new BoolSetting.Builder()
.name("show-current-players")
.description("Shows players that are currently on the server.")
.defaultValue(true)
.build()
);
private final Setting<Boolean> showWhenEmpty = sgGeneral.add(new BoolSetting.Builder()
.name("show-when-empty")
.description("Shows the hud even when there are no historic players.")
.defaultValue(true)
.visible(() -> !showCurrentPlayers.get())
.build()
);
private final Setting<SettingColor> currentPlayersColor = sgGeneral.add(new ColorSetting.Builder()
.name("current-players-color")
.description("The color of the current players text.")
.defaultValue(new SettingColor(255, 255, 255))
.visible(showCurrentPlayers::get)
.build()
);
private final Setting<SettingColor> historicPlayersColor = sgGeneral.add(new ColorSetting.Builder()
.name("historic-players-name-color")
.description("The color of the historic players text.")
.defaultValue(new SettingColor(255, 255, 255))
.build()
);
private final Setting<SettingColor> historicPlayersLastSeenColor = sgGeneral.add(new ColorSetting.Builder()
.name("last-seen-color")
.description("The color of the historic players last seen text.")
.defaultValue(new SettingColor(175, 175, 175))
.build()
);
private final Setting<Integer> limit = sgGeneral.add(new IntSetting.Builder()
.name("limit")
.description("The maximum amount of players to display.")
.defaultValue(10)
.min(1)
.max(1000)
.noSlider()
.build()
);
private final Setting<Alignment> alignment = sgGeneral.add(new EnumSetting.Builder<Alignment>()
.name("alignment")
.description("Horizontal alignment.")
.defaultValue(Alignment.Auto)
.build()
);
@Override
public void render(HudRenderer renderer) {
super.render(renderer);
int line = 0;
int more = 0;
if (players.isEmpty() && !showWhenEmpty.get()) return;
String playersText = "Players:";
double playerOffset = alignX(renderer.textWidth(playersText), alignment.get());
renderer.text(playersText, x + playerOffset, y + line * renderer.textHeight(), GuiThemes.get().textColor(), true);
double longestLine = renderer.textWidth(playersText);
line++;
List<String> alreadyDisplayed = new ArrayList<>();
if (showCurrentPlayers.get() && mc.player != null) {
for (PlayerListEntry player : mc.player.networkHandler.getListedPlayerListEntries()) {
if (line >= limit.get()) {
more++;
continue;
}
alreadyDisplayed.add(String.valueOf(player.getProfile().getId()));
String name = player.getProfile().getName();
double offset = alignX(renderer.textWidth(name), alignment.get());
renderer.text(name, x + offset, y + line * renderer.textHeight(), currentPlayersColor.get(), true);
line++;
if (renderer.textWidth(name) > longestLine) longestLine = renderer.textWidth(name);
}
}
// Sort players by join time (newest first)
List<ServerInfoResponse.Player> players = new ArrayList<>(this.players);
players.sort((b, a) -> a.last_seen.compareTo(b.last_seen));
for (ServerInfoResponse.Player player : players) {
if (alreadyDisplayed.contains(player.uuid)) continue;
if (line >= limit.get()) {
more++;
continue;
}
// Convert last_seen to a human-readable format
String unit = "s";
double last_seen = (int) (System.currentTimeMillis() / 1000 - player.last_seen);
if (last_seen >= 60) {
last_seen /= 60;
unit = "min";
}
if (last_seen >= 60 && unit.equals("min")) {
last_seen /= 60;
unit = "h";
}
if (last_seen >= 24 && unit.equals("h")) {
last_seen /= 24;
unit = last_seen == 1 ? " day" : " days";
}
if (last_seen >= 30 && unit.equals(" days")) {
last_seen /= 30;
unit = last_seen == 1 ? " month" : " months";
}
if (last_seen >= 12 && (unit.equals(" months"))) {
last_seen /= 12;
unit = last_seen == 1 ? " year" : " years";
}
// Round to 1 decimal place
last_seen = Math.round(last_seen * 10) / 10.0;
double width = renderer.textWidth(player.name) + renderer.textWidth(" (" + last_seen + unit + ")");
double offset = alignX(width, alignment.get());
renderer.text(player.name, x + offset, y + line * renderer.textHeight(), historicPlayersColor.get(), true);
renderer.text(" (" + last_seen + unit + ")", x + offset + renderer.textWidth(player.name), y + line * renderer.textHeight(), historicPlayersLastSeenColor.get(), true);
line++;
if (width > longestLine) longestLine = width;
}
if (line == 1) {
String text = "No players found.";
double offset = alignX(renderer.textWidth(text), alignment.get());
renderer.text(text, x + offset, y + line * renderer.textHeight(), GuiThemes.get().textColor(), true);
line++;
if (renderer.textWidth(text) > longestLine) longestLine = renderer.textWidth("No players found.");
}
if (more > 0) {
String text = "... and " + more + " more";
double offset = alignX(renderer.textWidth(text), alignment.get());
renderer.text(text, x + offset, y + line * renderer.textHeight(), GuiThemes.get().textColor(), true);
line++;
if (renderer.textWidth(text) > longestLine) longestLine = renderer.textWidth(text);
}
if (showCrackedText.get() && isCracked) {
String text = "Server is cracked";
double offset = alignX(renderer.textWidth(text), alignment.get());
renderer.text(text, x + offset, y + line * renderer.textHeight(), GuiThemes.get().textColor(), true);
line++;
if (renderer.textWidth(text) > longestLine) longestLine = renderer.textWidth(text);
}
box.setSize(longestLine, line * renderer.textHeight());
}
}

View File

@@ -1,20 +0,0 @@
package de.damcraft.serverseeker.mixin;
import net.minecraft.network.packet.c2s.handshake.ConnectionIntent;
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(HandshakeC2SPacket.class)
public interface HandshakeC2SAccessor {
@Mutable
@Accessor
void setAddress(String address);
@Accessor("intendedState")
ConnectionIntent getNetworkState();
@Accessor("address")
String getAddress();
}

View File

@@ -1,54 +0,0 @@
package de.damcraft.serverseeker.mixin;
import com.google.gson.JsonObject;
import de.damcraft.serverseeker.ServerSeeker;
import de.damcraft.serverseeker.modules.BungeeSpoofModule;
import meteordevelopment.meteorclient.systems.modules.Modules;
import meteordevelopment.meteorclient.utils.network.Http;
import net.minecraft.network.packet.c2s.handshake.ConnectionIntent;
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static de.damcraft.serverseeker.ServerSeeker.gson;
import static meteordevelopment.meteorclient.MeteorClient.mc;
@Mixin(HandshakeC2SPacket.class)
public abstract class HandshakeC2SMixin {
@Mutable
@Shadow
@Final
private String address;
@Shadow
public abstract ConnectionIntent intendedState();
@Inject(method = "<init>(ILjava/lang/String;ILnet/minecraft/network/packet/c2s/handshake/ConnectionIntent;)V", at = @At("RETURN"))
private void onHandshakeC2SPacket(int i, String string, int j, ConnectionIntent connectionIntent, CallbackInfo ci) {
BungeeSpoofModule bungeeSpoofModule = Modules.get().get(BungeeSpoofModule.class);
if (!bungeeSpoofModule.isActive()) return;
if (this.intendedState() != ConnectionIntent.LOGIN) return;
ServerSeeker.LOG.info("Spoofing bungeecord handshake packet");
String spoofedUUID = mc.getSession().getUuidOrNull().toString();
String URL = "https://api.mojang.com/users/profiles/minecraft/" + mc.getSession().getUsername();
Http.Request request = Http.get(URL);
String response = request.sendString();
if (response != null) {
JsonObject jsonObject = gson.fromJson(response, JsonObject.class);
if (jsonObject != null && jsonObject.has("id")) {
spoofedUUID = jsonObject.get("id").getAsString();
}
}
this.address += "\u0000" + bungeeSpoofModule.spoofedAddress.get() + "\u0000" + spoofedUUID;
}
}

View File

@@ -1,12 +0,0 @@
package de.damcraft.serverseeker.mixin;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MultiplayerScreen.class)
public interface MultiplayerScreenAccessor {
@Accessor("serverListWidget")
MultiplayerServerListWidget getServerListWidget();
}

View File

@@ -1,71 +0,0 @@
package de.damcraft.serverseeker.mixin;
import de.damcraft.serverseeker.gui.GetInfoScreen;
import de.damcraft.serverseeker.gui.ServerSeekerScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MultiplayerScreen.class)
public abstract class MultiplayerScreenMixin extends Screen {
@Shadow
protected MultiplayerServerListWidget serverListWidget;
@Unique
private ButtonWidget getInfoButton;
protected MultiplayerScreenMixin() {
super(null);
}
@Inject(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/multiplayer/MultiplayerScreen;updateButtonActivationStates()V"))
private void onInit(CallbackInfo info) {
// Add a button which sets the current screen to the ServerSeekerScreen
this.addDrawableChild(
new ButtonWidget.Builder(
Text.literal("ServerSeeker"),
onPress -> {
if (this.client == null) return;
this.client.setScreen(new ServerSeekerScreen((MultiplayerScreen) (Object) this));
}
)
.position(150, 3)
.width(80)
.build()
);
// Add a button to get the info of the selected server
this.getInfoButton = this.addDrawableChild(
new ButtonWidget.Builder(
Text.literal("Get players"),
onPress -> {
if (this.client == null) return;
MultiplayerServerListWidget.Entry entry = this.serverListWidget.getSelectedOrNull();
if (entry != null) {
if (this.client == null) return;
this.client.setScreen(new GetInfoScreen((MultiplayerScreen) (Object) this, entry));
}
}
)
.position(150 + 80 + 5, 3)
.width(80)
.build()
);
}
@Inject(method = "updateButtonActivationStates", at = @At("TAIL"))
private void onUpdateButtonActivationStates(CallbackInfo info) {
// Enable the button if a server is selected
MultiplayerServerListWidget.Entry entry = this.serverListWidget.getSelectedOrNull();
this.getInfoButton.active = entry != null && !(entry instanceof MultiplayerServerListWidget.ScanningEntry);
}
}

View File

@@ -1,23 +0,0 @@
package de.damcraft.serverseeker.mixin;
import de.damcraft.serverseeker.ServerSeekerSystem;
import meteordevelopment.meteorclient.systems.System;
import meteordevelopment.meteorclient.systems.Systems;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = Systems.class, remap = false)
public abstract class SystemsMixin {
@Shadow
private static System<?> add(System<?> system) {
return null;
}
@Inject(method = "init", at = @At("HEAD"))
private static void onInit(CallbackInfo ci) {
add(new ServerSeekerSystem());
}
}

View File

@@ -1,22 +0,0 @@
package de.damcraft.serverseeker.mixin;
import de.damcraft.serverseeker.gui.InstallMeteorScreen;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class TitleScreenMixin {
@Inject(at = @At("HEAD"), method = "init()V", cancellable = true)
private void init(CallbackInfo info) {
// Check if meteor-client is installed
if (!FabricLoader.getInstance().isModLoaded("meteor-client")) {
info.cancel();
MinecraftClient.getInstance().setScreen(new InstallMeteorScreen());
}
}
}

View File

@@ -1,23 +0,0 @@
package de.damcraft.serverseeker.modules;
import de.damcraft.serverseeker.ServerSeeker;
import meteordevelopment.meteorclient.settings.Setting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.settings.StringSetting;
import meteordevelopment.meteorclient.systems.modules.Module;
public class BungeeSpoofModule extends Module {
SettingGroup sgGeneral = settings.getDefaultGroup();
public Setting<String> spoofedAddress = sgGeneral.add(new StringSetting.Builder()
.name("spoofed-address")
.description("The spoofed IP address that will be sent to the server.")
.defaultValue("127.0.0.1")
.filter((text, c) -> (text + c).matches("^[0-9a-f\\\\.:]{0,45}$"))
.build()
);
public BungeeSpoofModule() {
super(ServerSeeker.CATEGORY, "BungeeSpoof", "Allows you to join servers with an exposed bungeecord backend. ONLY ENABLE THIS IF YOU ACTUALLY WANT TO JOIN A BUNGEESPOOFABLE SERVER!");
}
}

View File

@@ -1,21 +0,0 @@
package de.damcraft.serverseeker.ssapi.requests;
import de.damcraft.serverseeker.ServerSeekerSystem;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class ServerInfoRequest {
private final String api_key = ServerSeekerSystem.get().apiKey;
private String ip;
private Integer port;
public void setIpPort(String ip, Integer port) {
this.ip = ip;
this.port = port;
}
public String json() {
return gson.toJson(this);
}
}

View File

@@ -1,103 +0,0 @@
package de.damcraft.serverseeker.ssapi.requests;
import com.google.gson.JsonArray;
import de.damcraft.serverseeker.ServerSeekerSystem;
import static de.damcraft.serverseeker.ServerSeeker.gson;
public class ServersRequest {
private final String api_key = ServerSeekerSystem.get().apiKey;
private Integer asn;
private String country_code;
private Boolean cracked;
private String description;
private JsonArray max_players;
private Integer online_after;
private JsonArray online_players;
private Integer protocol;
private Boolean ignore_modded;
private Boolean only_bungeespoofable;
public enum Software {
Any,
Bukkit,
Spigot,
Paper,
Vanilla
}
private Software software;
public void setAsn(Integer asn) {
this.asn = asn;
}
public void setCountryCode(String cc) {
this.country_code = cc;
}
public void setCracked(Boolean cracked) {
this.cracked = cracked;
}
public void setDescription(String description) {
this.description = description;
}
public void setMaxPlayers(Integer exact) {
this.max_players = new JsonArray();
this.max_players.add(exact);
this.max_players.add(exact);
}
public void setMaxPlayers(Integer min, Integer max) {
this.max_players = new JsonArray();
this.max_players.add(min);
if (max == -1) {
max_players.add("inf");
}
else {
this.max_players.add(max);
}
}
public void setOnlineAfter(Integer unix_timestamp) {
this.online_after = unix_timestamp;
}
public void setOnlinePlayers(Integer exact) {
this.online_players = new JsonArray();
this.online_players.add(exact);
this.online_players.add(exact);
}
public void setOnlinePlayers(Integer min, Integer max) {
this.online_players = new JsonArray();
this.online_players.add(min);
if (max == -1) {
this.online_players.add("inf");
}
else {
this.online_players.add(max);
}
}
public void setProtocolVersion(Integer version) {
this.protocol = version;
}
public void setSoftware(Software software) {
this.software = software;
}
public void setIgnoreModded(Boolean ignore) {
this.ignore_modded = ignore;
}
public void setOnlyBungeeSpoofable(Boolean only) {
this.only_bungeespoofable = only;
}
public String json() {
return gson.toJson(this);
}
}

View File

@@ -1,33 +0,0 @@
package de.damcraft.serverseeker.ssapi.requests;
import com.google.gson.JsonObject;
import de.damcraft.serverseeker.ServerSeekerSystem;
public class WhereisRequest {
private final String api_key = ServerSeekerSystem.get().apiKey;
private enum PlayerSearchType {
Name,
Uuid
}
private PlayerSearchType playerSearchType;
private String playerSearchValue;
public WhereisRequest() {
}
public void setName(String name) {
playerSearchType = PlayerSearchType.Name;
playerSearchValue = name;
}
public void setUuid(String uuid) {
playerSearchType = PlayerSearchType.Uuid;
playerSearchValue = uuid;
}
public String json() {
JsonObject jo = new JsonObject();
jo.addProperty(playerSearchType.name().toLowerCase(), playerSearchValue);
jo.addProperty("api_key", api_key);
return jo.toString();
}
}

View File

@@ -1,26 +0,0 @@
package de.damcraft.serverseeker.ssapi.responses;
import java.util.List;
public class ServerInfoResponse {
public String error;
public Boolean cracked;
public String description;
public Integer last_seen;
public Integer max_players;
public Integer online_players;
public Integer protocol;
public String version;
public static class Player {
public String name;
public String uuid;
public Integer last_seen;
}
public List<Player> players;
public boolean isError() {
return error != null;
}
}

View File

@@ -1,24 +0,0 @@
package de.damcraft.serverseeker.ssapi.responses;
import java.util.List;
public class ServersResponse {
public String error;
public static class Server {
public String server;
public Boolean cracked;
public String description;
public Integer last_seen;
public Integer max_players;
public Integer online_players;
public Integer protocol;
public String version;
}
public List<Server> data;
public boolean isError() {
return error != null;
}
}

View File

@@ -1,18 +0,0 @@
package de.damcraft.serverseeker.ssapi.responses;
public class UserInfoResponse {
public String error;
public String discord_id;
public String discord_username;
public String discord_avatar_url;
public Integer requests_per_day_server_info;
public Integer requests_per_day_servers;
public Integer requests_per_day_whereis;
public Integer requests_made_server_info;
public Integer requests_made_servers;
public Integer requests_made_whereis;
public boolean isError() {
return error != null;
}
}

View File

@@ -1,20 +0,0 @@
package de.damcraft.serverseeker.ssapi.responses;
import java.util.List;
public class WhereisResponse {
public String error;
public static class Record {
public String server;
public String uuid;
public String name;
public Integer last_seen;
}
public List<Record> data;
public boolean isError() {
return error != null;
}
}

View File

@@ -1,60 +0,0 @@
package de.damcraft.serverseeker.utils;
import de.damcraft.serverseeker.SmallHttp;
import de.damcraft.serverseeker.hud.HistoricPlayersHud;
import de.damcraft.serverseeker.ssapi.requests.ServerInfoRequest;
import de.damcraft.serverseeker.ssapi.responses.ServerInfoResponse;
import meteordevelopment.meteorclient.events.game.GameJoinedEvent;
import meteordevelopment.meteorclient.systems.hud.Hud;
import meteordevelopment.meteorclient.systems.hud.HudElement;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static de.damcraft.serverseeker.ServerSeeker.gson;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class HistoricPlayersUpdater {
@EventHandler
private static void onGameJoinEvent(GameJoinedEvent ignoredEvent) {
// Run in a new thread
new Thread(HistoricPlayersUpdater::update).start();
}
public static void update() {
// If the Hud contains the HistoricPlayersHud, update the players
List<HistoricPlayersHud> huds = new ArrayList<>();
for (HudElement hudElement : Hud.get()) {
if (hudElement instanceof HistoricPlayersHud && hudElement.isActive()) {
huds.add((HistoricPlayersHud) hudElement);
}
}
if (huds.isEmpty()) return;
ClientPlayNetworkHandler networkHandler = mc.getNetworkHandler();
if (networkHandler == null) return;
String address = networkHandler.getConnection().getAddress().toString();
// Split it at "/" and take the second part
String[] addressParts = address.split("/");
if (addressParts.length < 2) return;
String ip = addressParts[1].split(":")[0];
Integer port = Integer.valueOf(addressParts[1].split(":")[1]);
ServerInfoRequest request = new ServerInfoRequest();
request.setIpPort(ip, port);
String jsonResp = SmallHttp.post("https://api.serverseeker.net/server_info", request.json());
ServerInfoResponse resp = gson.fromJson(jsonResp, ServerInfoResponse.class);
for (HistoricPlayersHud hud : huds) {
hud.players = Objects.requireNonNullElseGet(resp.players, List::of);
hud.isCracked = resp.cracked != null && resp.cracked;
}
}
}

View File

@@ -1,94 +0,0 @@
package de.damcraft.serverseeker.utils;
import java.util.HashMap;
public class MCVersionUtil {
private static final HashMap<String, Integer> versions = new HashMap<>() {
{
put("1.20.6", 766);
put("1.20.5", 766);
put("1.20.4", 765);
put("1.20.3", 765);
put("1.20.2", 764);
put("1.20.1", 763);
put("1.20", 763);
put("1.19.4", 762);
put("1.19.3", 761);
put("1.19.2", 760);
put("1.19.1", 760);
put("1.19", 759);
put("1.18.2", 758);
put("1.18.1", 757);
put("1.18", 757);
put("1.17.1", 756);
put("1.17", 755);
put("1.16.5", 754);
put("1.16.4", 754);
put("1.16.3", 753);
put("1.16.2", 751);
put("1.16.1", 736);
put("1.16", 735);
put("1.15.2", 578);
put("1.15.1", 575);
put("1.15", 753);
put("1.14.4", 498);
put("1.14.3", 490);
put("1.14.2", 485);
put("1.14.1", 480);
put("1.14", 477);
put("1.13.2", 404);
put("1.13.1", 401);
put("1.13", 393);
put("1.12.2", 340);
put("1.12.1", 338);
put("1.12", 335);
put("1.11.2", 316);
put("1.11.1", 316);
put("1.11", 316);
put("1.10.2", 210);
put("1.10.1", 210);
put("1.10", 210);
put("1.9.4", 110);
put("1.9.3", 110);
put("1.9.2", 109);
put("1.9.1", 108);
put("1.9", 107);
put("1.8.9", 47);
put("1.8.8", 47);
put("1.8.7", 47);
put("1.8.6", 47);
put("1.8.5", 47);
put("1.8.4", 47);
put("1.8.3", 47);
put("1.8.2", 47);
put("1.8.1", 47);
put("1.8", 47);
put("1.7.10", 5);
put("1.7.9", 5);
put("1.7.8", 5);
put("1.7.7", 5);
put("1.7.6", 5);
put("1.7.5", 4);
put("1.7.4", 4);
put("1.7.2", 3);
put("1.7.1", 3);
}
};
public static Integer versionToProtocol(String versionString) {
return versions.get(versionString);
}
}

View File

@@ -1,43 +0,0 @@
package de.damcraft.serverseeker.utils;
import de.damcraft.serverseeker.mixin.MultiplayerScreenAccessor;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.network.ServerInfo;
public class MultiplayerScreenUtil {
public static void addInfoToServerList(MultiplayerScreen mps, ServerInfo info) {
MultiplayerScreenAccessor mpsAccessor = (MultiplayerScreenAccessor) mps;
mps.getServerList().add(info, false);
mps.getServerList().saveFile();
mpsAccessor.getServerListWidget().setServers(mps.getServerList());
}
public static void addInfoToServerList(MultiplayerScreen mps, ServerInfo info, boolean reload) {
MultiplayerScreenAccessor mpsAccessor = (MultiplayerScreenAccessor) mps;
mps.getServerList().add(info, false);
if (reload) mpsAccessor.getServerListWidget().setServers(mps.getServerList());
}
public static void addNameIpToServerList(MultiplayerScreen mps, String name, String ip) {
MultiplayerScreenAccessor mpsAccessor = (MultiplayerScreenAccessor) mps;
ServerInfo info = new ServerInfo(name, ip, ServerInfo.ServerType.OTHER);
mps.getServerList().add(info, false);
mpsAccessor.getServerListWidget().setServers(mps.getServerList());
mps.getServerList().saveFile();
}
public static void addNameIpToServerList(MultiplayerScreen mps, String name, String ip, boolean reload) {
MultiplayerScreenAccessor mpsAccessor = (MultiplayerScreenAccessor) mps;
ServerInfo info = new ServerInfo(name, ip, ServerInfo.ServerType.OTHER);
mps.getServerList().add(info, false);
if (reload) mpsAccessor.getServerListWidget().setServers(mps.getServerList());
}
public static void reloadServerList(MultiplayerScreen mps) {
MultiplayerScreenAccessor mpsAccessor = (MultiplayerScreenAccessor) mps;
mpsAccessor.getServerListWidget().setServers(mps.getServerList());
}
public static void saveList(MultiplayerScreen mps) {
mps.getServerList().saveFile();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

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