diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index be9a31a..0000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: Build Plugin
-
-on:
- push:
- workflow_dispatch:
-
-permissions:
- contents: write
-
-jobs:
- build:
- if: ${{ github.ref == 'refs/heads/main' }}
- concurrency:
- group: ${{ github.workflow }}
- cancel-in-progress: true
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup Java
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: '17'
-
- - name: Build Jar
- run: |
- gradle wrapper
- ./gradlew clean shadowJar
-
- - name: Publish Jar
- uses: actions/upload-artifact@v4
- with:
- name: OriginBlacklist
- path: build/libs/OriginBlacklist.jar
-
- - name: Extract Version
- id: version
- run: |
- VERSION="$(./gradlew -q properties | sed -n 's/^version: //p' | head -n 1)"
- echo "version=$VERSION" >> "$GITHUB_OUTPUT"
-
- - name: Remove Existing Release
- run: |
- gh release delete v${{ steps.version.outputs.version }} -y || true
- git push origin :refs/tags/v${{ steps.version.outputs.version }} || true
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Create Release
- id: create_release
- uses: softprops/action-gh-release@v1
- with:
- tag_name: v${{ steps.version.outputs.version }}
- name: ${{ steps.version.outputs.version }}
- draft: false
- prerelease: false
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - uses: actions/upload-release-asset@v1.0.1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./build/libs/OriginBlacklist.jar
- asset_name: OriginBlacklist_${{ steps.version.outputs.version }}.jar
- asset_content_type: application/java-archive
-
- - uses: eregon/publish-release@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- release_id: ${{ steps.create_release.outputs.id }}
\ No newline at end of file
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000..894161e
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,69 @@
+name: Build Plugin
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ main ]
+
+permissions:
+ contents: write
+ actions: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 21
+
+ - uses: gradle/actions/setup-gradle@v4
+
+ - run: |
+ gradle wrapper
+ chmod +x ./gradlew
+ ./gradlew shadowJar
+
+ - id: vars
+ run: |
+ echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
+ ./gradlew -q printVars | sed 's/ = /=/g' >> "$GITHUB_OUTPUT"
+
+ - id: ghck
+ run: |
+ CODE=$(curl -s -o /dev/null -w "%{http_code}" \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ "https://api.github.com/repos/${{ github.repository }}/releases/tags/v${{ steps.vars.outputs.VERS }}")
+ if [ "$CODE" = "200" ]; then
+ echo "EXISTS=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "EXISTS=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ - run: |
+ mkdir -p dist
+ cp "./build/libs/${{ steps.vars.outputs.AFCT }}" ./dist
+
+ - uses: actions/upload-artifact@v4
+ with:
+ path: dist/${{ steps.vars.outputs.AFCT }}
+ name: ${{ steps.vars.outputs.AFCT }}
+
+ - if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ format('v{0}{1}', steps.vars.outputs.VERS, steps.ghck.outputs.EXISTS == 'true' && format('+{0}', steps.vars.outputs.COMMIT_HASH) || '') }}
+ name: ${{ format('{0}{1}', steps.ghck.outputs.EXISTS == 'true' && 'Snapshot ' || 'v', steps.ghck.outputs.EXISTS == 'true' && steps.vars.outputs.COMMIT_HASH || steps.vars.outputs.VERS) }}
+ files: dist/${{ steps.vars.outputs.AFCT }}
+ prerelease: ${{ steps.ghck.outputs.EXISTS == 'true' }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ae55814..583b97b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,7 @@
-**/.DS_Store
-.idea
-.gradle
-gradle
-build
-run
-bin
-gradlew
-gradlew.bat
\ No newline at end of file
+/.gradle/
+/.vscode/
+/gradle/
+/build/
+/run/
+/gradlew
+/gradlew.bat
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..0ad25db
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/README.md b/README.md
index 4d9eff9..66b2945 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,28 @@
-# OriginBlacklist
+
-basically just a reimplementation of originblacklist but for eaglerxserver
+Features
-> [!WARNING]
-> **Velocity is the main platform I'm developing this for, bungee & bukkit will still work but will probably have some bugs and will receive less support!**
+- [x] Origin based blacklisting
+- [x] Client brand based blacklisting
+- [x] Username based blacklisting
+- [x] IP based blacklisting
+- [x] Kick message customization
+- [x] Blacklist MOTD customization
+- [x] MiniMessage and legacy formattings supported
+- [x] Plugin update checker
+- [ ] Send blacklist logs to a webhook
+- [ ] Subscribe to an auto-updating blacklist
+- [ ] Reverse blacklist (whitelist)
-### Features
-- [x] Origin Blacklisting
-- [x] Brand Blacklisting
-- [x] Username blacklisting
-- [x] IP blacklisting
-- [x] Custom kick message
-- [x] Custom blacklist MOTD
-- [x] MiniMessage formatting for messages
-- [x] Velocity, Bungee, and Bukkit support
-- [x] Send blacklists to a discord webhook
-- [x] Simple blacklist command
-- [ ] Blacklist subscription URLs
-- [ ] Blacklist -> Whitelist
-- [ ] Update system
+Download
+The latest release can be found at https://github.com/WebMCDevelopment/originblacklist/releases/tag/v1.1.3
-### Download
-**[https://github.com/colbster937/originblacklist/releases/latest/](https://github.com/colbster937/originblacklist/releases/latest/)**
-
-### Building
-```
-$ git clone https://github.com/colbster937/originblacklist.git
+Building
+```sh
+$ git clone https://github.com/WebMCDevelopment/originblacklist
$ cd originblacklist
$ gradle wrapper
$ ./gradlew shadowJar
diff --git a/build.gradle.kts b/build.gradle.kts
index 3b30e90..2b0f607 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,17 +1,48 @@
+import org.gradle.api.tasks.compile.JavaCompile
+import org.gradle.language.jvm.tasks.ProcessResources
+import xyz.jpenilla.runpaper.task.RunServer
+import xyz.jpenilla.runwaterfall.task.RunWaterfall
+import xyz.jpenilla.runvelocity.task.RunVelocity
+
+
+
+
+val PLUGIN_NAME = "OriginBlacklist"
+val PLUGIN_IDEN = "originblacklist"
+val PLUGIN_DOMN = "xyz.webmc"
+val PLUGIN_DESC = "An eaglercraft client blacklist plugin."
+val PLUGIN_VERS = "2.0.0"
+val PLUGIN_SITE = "https://github.com/WebMCDevelopment/$PLUGIN_IDEN"
+val PLUGIN_DEPA = listOf("EaglercraftXServer")
+val PLUGIN_DEPB = listOf("EaglercraftXServer")
+val PLUGIN_DEPC = listOf("eaglerxserver")
+val PLUGIN_SDPA = listOf("PlaceholderAPI")
+val PLUGIN_SDPB = listOf("PAPIProxyBridge")
+val PLUGIN_SDPC = listOf("papiproxybridge")
+val PLUGIN_PROV = emptyList()
+val PLUGIN_ATHR = listOf("Colbster937")
+val PLUGIN_CTBR = emptyList()
+
+
+
+
+val PLUGIN_DEPA_J = getJSONObj(PLUGIN_DEPA)
+val PLUGIN_DEPB_J = getJSONObj(PLUGIN_DEPB)
+val PLUGIN_DEPC_J = getJSONObjMerge(PLUGIN_DEPC, PLUGIN_SDPC)
+val PLUGIN_SDPA_J = getJSONObj(PLUGIN_SDPA)
+val PLUGIN_SDPB_J = getJSONObj(PLUGIN_SDPB)
+val PLUGIN_PROV_J = getJSONObj(PLUGIN_PROV)
+val PLUGIN_ATHR_J = getJSONObj(PLUGIN_ATHR)
+val PLUGIN_CTBR_J = getJSONObj(PLUGIN_CTBR)
+
plugins {
id("java")
- id("eclipse")
- id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.8"
id("com.gradleup.shadow") version "9.3.0"
- id("xyz.jpenilla.run-velocity") version "2.3.1"
+ id("xyz.jpenilla.run-paper") version "3.0.2"
+ id("xyz.jpenilla.run-waterfall") version "3.0.2"
+ id("xyz.jpenilla.run-velocity") version "3.0.2"
}
-group = "dev.colbster937"
-version = "1.1.3"
-description = "A reimplementation of OriginBlacklist for EaglerXServer"
-
-val targetJavaVersion = 17
-
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
@@ -20,51 +51,131 @@ repositories {
maven("https://repo.md-5.net/content/repositories/releases/")
maven("https://repo.aikar.co/nexus/content/groups/aikar/")
maven("https://repo.lax1dude.net/repository/releases/")
+ maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
+ maven("https://repo.william278.net/releases/")
}
dependencies {
compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
compileOnly("org.bukkit:bukkit:1.8-R0.1-SNAPSHOT")
- compileOnly("net.md-5:bungeecord-api:1.8-SNAPSHOT")
+ compileOnly("net.md-5:bungeecord-api:1.21-R0.5-SNAPSHOT")
compileOnly("net.lax1dude.eaglercraft.backend:api-velocity:1.0.0")
compileOnly("net.lax1dude.eaglercraft.backend:api-bungee:1.0.0")
compileOnly("net.lax1dude.eaglercraft.backend:api-bukkit:1.0.0")
- implementation("org.yaml:snakeyaml:2.2")
- implementation("net.kyori:adventure-text-serializer-legacy:4.20.0")
- implementation("net.kyori:adventure-text-minimessage:4.20.0")
- implementation("com.github.seancfoley:ipaddress:5.3.4")
- annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
+ compileOnly("me.clip:placeholderapi:2.11.7")
+ compileOnly("net.william278:papiproxybridge:1.8.4")
+ implementation("org.semver4j:semver4j:6.0.0")
+ implementation("de.marhali:json5-java:3.0.0")
+ implementation("net.kyori:adventure-text-minimessage:4.26.1")
+ implementation("net.kyori:adventure-text-serializer-legacy:4.26.1")
+ implementation("com.github.seancfoley:ipaddress:5.5.1")
+ implementation("org.bstats:bstats-bukkit:3.1.0")
+ implementation("org.bstats:bstats-bungeecord:3.1.0")
+ implementation("org.bstats:bstats-velocity:3.1.0")
}
-tasks {
- named("runVelocity") {
- velocityVersion("3.4.0-SNAPSHOT")
- }
+sourceSets {
+ named("main") {
+ java.srcDir("./src/main/java")
+ resources.srcDir("./src/main/resources")
+ }
}
java {
- toolchain.languageVersion.set(JavaLanguageVersion.of(targetJavaVersion))
-}
-
-tasks.processResources {
- filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "Base.java")) {
- expand(
- mapOf(
- "version" to project.version,
- "description" to project.description
- )
- )
- }
-}
-
-tasks.shadowJar {
- relocate("org.yaml.snakeyaml", "dev.colbster937.shaded.snakeyaml")
- relocate("inet.ipaddr", "dev.colbster937.shaded.ipaddr")
- archiveVersion.set("")
- archiveClassifier.set("")
+ toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
tasks.withType().configureEach {
options.encoding = "UTF-8"
- options.release.set(targetJavaVersion)
+ options.release.set(17)
+}
+
+tasks.withType() {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ outputs.upToDateWhen { false }
+ doFirst {
+ filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) {
+ expand(mapOf(
+ "plugin_name" to PLUGIN_NAME,
+ "plugin_iden" to PLUGIN_IDEN,
+ "plugin_desc" to PLUGIN_DESC,
+ "plugin_vers" to PLUGIN_VERS,
+ "plugin_site" to PLUGIN_SITE,
+ "plugin_depa" to PLUGIN_DEPA_J,
+ "plugin_depb" to PLUGIN_DEPB_J,
+ "plugin_depc" to PLUGIN_DEPC_J,
+ "plugin_sdpa" to PLUGIN_SDPA_J,
+ "plugin_sdpb" to PLUGIN_SDPB_J,
+ "plugin_prov" to PLUGIN_PROV_J,
+ "plugin_athr" to PLUGIN_ATHR_J,
+ "plugin_ctbr" to PLUGIN_CTBR_J,
+ ))
+ }
+ }
+
+ inputs.files(tasks.named("compileJava").map { it.outputs.files })
+}
+
+tasks.withType() {
+ minecraftVersion("1.12.2")
+ runDirectory.set(layout.projectDirectory.dir("run/paper"))
+ downloadPlugins {
+ github("lax1dude", "eaglerxserver", "v1.0.8", "EaglerXServer.jar")
+ modrinth("placeholderapi", "2.11.7")
+ }
+}
+
+tasks.withType() {
+ waterfallVersion("1.21")
+ runDirectory.set(layout.projectDirectory.dir("run/waterfall"))
+ downloadPlugins {
+ github("lax1dude", "eaglerxserver", "v1.0.8", "EaglerXServer.jar")
+ }
+}
+
+tasks.withType() {
+ velocityVersion("3.4.0-SNAPSHOT")
+ runDirectory.set(layout.projectDirectory.dir("run/velocity"))
+ downloadPlugins {
+ github("lax1dude", "eaglerxserver", "v1.0.8", "EaglerXServer.jar")
+ modrinth("miniplaceholders", "3.1.0")
+ }
+}
+
+tasks.jar {
+ archiveFileName.set("$PLUGIN_NAME-$PLUGIN_VERS.jar")
+}
+
+tasks.shadowJar {
+ relocate("org.bstats", "$PLUGIN_DOMN.$PLUGIN_IDEN.shaded.bstats")
+ relocate("de.marhali.json5", "$PLUGIN_DOMN.$PLUGIN_IDEN.shaded.json5")
+ relocate("org.semver4j.semver4j", "$PLUGIN_DOMN.$PLUGIN_IDEN.shaded.semver4j")
+ // relocate("net.kyori.adventure", "$PLUGIN_DOMN.$PLUGIN_IDEN.shaded.adventure")
+ archiveFileName.set("$PLUGIN_NAME-$PLUGIN_VERS.jar")
+}
+
+tasks.register("printVars") {
+ group = "help"
+ doLast {
+ println("VERS = " + PLUGIN_VERS)
+ println("AFCT = " + tasks.named("shadowJar").get().outputs.files.singleFile.name)
+ }
+}
+
+fun getJSONObj(list: List): String {
+ return if (list.isNotEmpty()) {
+ list.joinToString(
+ separator = "\", \"",
+ prefix = "\"",
+ postfix = "\""
+ )
+ } else {
+ ""
+ }
+}
+
+fun getJSONObjMerge(a: List, b: List): String {
+ val c = a.joinToString(", ") { "{\"id\":\"$it\",\"optional\":false}" }
+ val d = b.joinToString(", ") { "{\"id\":\"$it\",\"optional\":true}" }
+ return listOf(c, d).filter { it.isNotEmpty() }.joinToString(",")
}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..5eb6697
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs = -Xmx1G
+org.gradle.parallel = true
+org.gradle.problems.report = false
\ No newline at end of file
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..c18b01b
Binary files /dev/null and b/icon.png differ
diff --git a/src/main/java/dev/colbster937/originblacklist/base/Base.java b/src/main/java/dev/colbster937/originblacklist/base/Base.java
deleted file mode 100644
index 6165432..0000000
--- a/src/main/java/dev/colbster937/originblacklist/base/Base.java
+++ /dev/null
@@ -1,316 +0,0 @@
-package dev.colbster937.originblacklist.base;
-
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.minimessage.MiniMessage;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-import net.lax1dude.eaglercraft.backend.server.api.*;
-import net.lax1dude.eaglercraft.backend.server.api.event.IEaglercraftLoginEvent;
-import net.lax1dude.eaglercraft.backend.server.api.event.IEaglercraftMOTDEvent;
-import net.lax1dude.eaglercraft.backend.server.api.query.IMOTDConnection;
-
-import javax.imageio.ImageIO;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
-
-public class Base {
- private static LoggerAdapter adapter;
- private static IEaglerXServerAPI api;
- private static IPBlacklist ipblacklist;
-
- public static void setLogger(LoggerAdapter log) {
- adapter = log;
- }
-
- public static void setApi(IEaglerXServerAPI api1) {
- api = api1;
- }
-
- public static ConfigManager config;
-
- public static String pluginVer = "1.0.2";
-
- public static boolean checkVer(String v1, String v2) {
- String[] c = v1.split("\\.");
- String[] r = v2.split("\\.");
- for (int i = 0; i < Math.max(c.length, r.length); i++) {
- int c1 = i < c.length ? Integer.parseInt(c[i]) : 0;
- int r1 = i < r.length ? Integer.parseInt(r[i]) : 0;
- if (c1 < r1)
- return false;
- if (c1 > r1)
- return true;
- }
- return true;
- }
-
- public static LoggerAdapter getLogger() {
- if (adapter == null)
- throw new IllegalStateException("Logger not initialized!");
- return adapter;
- }
-
- public interface LoggerAdapter {
- void info(String msg);
-
- void warn(String msg);
-
- void error(String msg);
- }
-
- public static void handleConnection(IEaglercraftLoginEvent e) {
- IEaglerLoginConnection conn = e.getLoginConnection();
- String origin = conn.getWebSocketHeader(EnumWebSocketHeader.HEADER_ORIGIN);
- String brand = conn.getEaglerBrandString();
- String name = conn.getUsername();
- String ip = getAddr(conn);
- String notAllowed1 = "not allowed on the server";
- String notAllowed2 = "not allowed";
-
- if (origin != null && !origin.equals("null")) {
- for (String origin1 : config.blacklist.origins) {
- if (matches(origin, origin1)) {
- setKick(e, formatKickMessage("origin", "website", notAllowed1, notAllowed2, origin, conn.getWebSocketHost()));
- webhook(conn, origin, brand, "origin");
- return;
- }
- }
- }
-
- if (brand != null && !brand.equals("null")) {
- for (String brand1 : config.blacklist.brands) {
- if (matches(brand, brand1)) {
- setKick(e, formatKickMessage("brand", "client", notAllowed1, notAllowed2, brand, conn.getWebSocketHost()));
- webhook(conn, origin, brand, "brand");
- return;
- }
- }
- }
-
- if (name != null && !name.equals("null")) {
- for (String name1 : config.blacklist.players) {
- if (matches(name, name1) || (name.length() > 16 || name.length() < 3)) {
- setKick(e, formatKickMessage("player", "username", notAllowed1, notAllowed2, name, conn.getWebSocketHost()));
- webhook(conn, origin, brand, "player");
- return;
- }
- }
- }
-
- if (ip != null && !ip.equalsIgnoreCase("null")) {
- if (ipblacklist.check(ip)) {
- setKick(e, formatKickMessage("ip address", "ip", notAllowed1, notAllowed2, ip, conn.getWebSocketHost()));
- webhook(conn, origin, brand, "ip");
- }
- }
- }
-
- public static void setKick(IEaglercraftLoginEvent e, Component msg) {
- try {
- getLogger().info("Kicked " + e.getProfileUsername());
- e.setKickMessage(msg);
- } catch (Throwable ignored) {
- String msg1 = LegacyComponentSerializer.legacySection().serialize(msg);
- e.setKickMessage(msg1);
- }
- }
-
- public static void handleMOTD(IEaglercraftMOTDEvent e) {
- if (!config.messages.motd.enabled) return;
-
- IMOTDConnection conn = e.getMOTDConnection();
- String origin = conn.getWebSocketHeader(EnumWebSocketHeader.HEADER_ORIGIN);
- String host = conn.getWebSocketHost() != null ? conn.getWebSocketHost() : "";
- String ip = getAddr(conn);
-
- String blocktype1 = null;
- String easyblocktype1 = null;
- String blocked1 = null;
-
- if (origin != null && !"null".equals(origin)) {
- for (String origin2 : config.blacklist.origins) {
- if (matches(origin, origin2)) {
- blocktype1 = "origin";
- easyblocktype1 = "website";
- blocked1 = origin;
- break;
- }
- }
- }
-
- if (blocktype1 == null && ip != null && !"null".equalsIgnoreCase(ip)) {
- boolean blocked = ipblacklist != null && ipblacklist.check(ip);
- if (!blocked) {
- for (String ip2 : config.blacklist.ips) {
- if (matches(ip, ip2)) { blocked = true; break; }
- }
- }
- if (blocked) {
- blocktype1 = "ip address";
- easyblocktype1 = "ip";
- blocked1 = ip;
- }
- }
-
- if (blocktype1 == null) return;
-
- final String finalBlocktype = blocktype1;
- final String finalEasyblocktype = easyblocktype1;
- final String finalBlocked = blocked1;
-
- List m = List.of(config.messages.motd.text.split("\n")).stream()
- .map(line -> line
- .replace("%blocktype%", finalBlocktype)
- .replace("%easyblocktype%", finalEasyblocktype)
- .replace("%notallowed1%", "blacklisted")
- .replace("%notallowed2%", "blacklisted")
- .replace("%blocked%", finalBlocked)
- .replace("%host%", host))
- .map(line -> LegacyComponentSerializer.legacySection()
- .serialize(MiniMessage.miniMessage().deserialize(line)))
- .collect(Collectors.toList());
-
- setMOTD(conn, m);
- }
-
- public static void setMOTD(IMOTDConnection conn, List m) {
- conn.setServerMOTD(m);
- conn.setPlayerTotal(0);
- conn.setPlayerMax(0);
- conn.setPlayerList(List.of());
-
- if (config.messages.motd.icon != null && !config.messages.motd.icon.isEmpty()) {
- try {
- BufferedImage img = ImageIO.read(new File(config.messages.motd.icon));
- if (img.getWidth() != 64 || img.getHeight() != 64) {
- getLogger().warn("Icon must be 64x64");
- return;
- }
-
- byte[] bytes = new byte[64 * 64 * 4];
- for (int y = 0; y < 64; y++) {
- for (int x = 0; x < 64; x++) {
- int pixel = img.getRGB(x, y);
- int i = (y * 64 + x) * 4;
- bytes[i] = (byte) ((pixel >> 16) & 0xFF);
- bytes[i + 1] = (byte) ((pixel >> 8) & 0xFF);
- bytes[i + 2] = (byte) (pixel & 0xFF);
- bytes[i + 3] = (byte) ((pixel >> 24) & 0xFF);
- }
- }
- conn.setServerIcon(bytes);
- } catch (IOException ex) {
- getLogger().error(ex.toString());
- }
- }
- conn.sendToUser();
- conn.disconnect();
- }
-
- public static boolean matches(String text1, String text2) {
- return text1.toLowerCase().matches(text2.replace(".", "\\.").replaceAll("\\*", ".*").toLowerCase());
- }
-
- public static Component formatKickMessage(String type, String easytype, String notAllowed1, String notAllowed2, String value, String host) {
- String help = "";
- if ("player".equals(type)) {
- help = config.messages.help.player;
- } else if ("ip address".equals(type)) {
- help = config.messages.help.ip;
- } else {
- help = config.messages.help.generic;
- }
- return MiniMessage.miniMessage().deserialize(
- config.messages.kick
- .replaceAll("%help%", help)
- .replaceAll("%blocktype%", type)
- .replaceAll("%easyblocktype%", easytype)
- .replaceAll("%notallowed1%", notAllowed1)
- .replaceAll("%notallowed2%", notAllowed2)
- .replaceAll("%blocked%", value)
- .replaceAll("%host%", host));
- }
-
- public static void webhook(IEaglerLoginConnection plr, String origin, String brand, String type) {
- String webhook = config.discord.webhook;
- if (webhook == null || webhook.isBlank())
- return;
-
- CompletableFuture.runAsync(() -> {
- String addr = getAddr(plr);
- int protocol = !plr.isEaglerXRewindPlayer() ? plr.getMinecraftProtocol() : plr.getRewindProtocolVersion();
- String host = plr.getWebSocketHost();
- String userAgent = plr.getWebSocketHeader(EnumWebSocketHeader.HEADER_USER_AGENT);
- Boolean rewind = plr.isEaglerXRewindPlayer();
- if (userAgent == null || userAgent.isEmpty())
- userAgent = "undefined";
-
- String payload = String.format(
- """
- {
- "content": "Blocked a blacklisted %s from joining",
- "embeds": [
- {
- "title": "Player Information",
- "description": "🎮 **Name:** %s\\n🏠 **IP:** %s\\n🌄 **PVN:** %s\\n🌐 **Origin:** %s\\n🔋 **Brand:** %s\\n🪑 **Host:** %s\\n🧊 **UA:** %s\\n⏪ **Rewind:** %s"
- }
- ]
- }
- """,
- type, plr.getUsername(), addr, protocol, origin, brand, plr.isWebSocketSecure() ? "wss://" : "ws://" + host, userAgent, rewind ? "Yes" : "No");
-
- try {
- HttpURLConnection conn = (HttpURLConnection) new URL(webhook).openConnection();
- conn.setRequestMethod("POST");
- conn.setRequestProperty("Content-Type", "application/json");
- conn.setDoOutput(true);
- conn.setConnectTimeout(5000);
- conn.setReadTimeout(5000);
-
- try (OutputStream os = conn.getOutputStream()) {
- os.write(payload.getBytes());
- }
-
- conn.getInputStream().close();
- } catch (Exception e) {
- getLogger().warn("Failed to send webhook: " + e);
- }
- });
- }
-
- public static String getAddr(IEaglerLoginConnection conn) {
- var addr1 = conn.getPlayerAddress() != null ? conn.getPlayerAddress().toString().substring(1) : "0.0.0.0:0";
- var addr2 = addr1.lastIndexOf(':') != -1 ? addr1.substring(0, addr1.lastIndexOf(':')) : addr1;
- return addr2;
- }
-
- public static String getAddr(IMOTDConnection conn) {
- var addr1 = conn.getSocketAddress() != null ? conn.getSocketAddress().toString().substring(1) : "0.0.0.0:0";
- var addr2 = addr1.lastIndexOf(':') != -1 ? addr1.substring(0, addr1.lastIndexOf(':')) : addr1;
- return addr2;
- }
-
- public static void init() {
- File motdIcon = new File(config.messages.motd.icon);
- if (!motdIcon.exists()) {
- try (InputStream in = ConfigManager.class.getResourceAsStream("/server-blocked.png")) {
- if (in != null) {
- Files.copy(in, motdIcon.toPath(), StandardCopyOption.REPLACE_EXISTING);
- }
- } catch (IOException e) {
- getLogger().warn(e.toString());
- }
- }
- ipblacklist = new IPBlacklist();
- }
-
- public static void reloadConfig() {
- config = ConfigManager.loadConfig(adapter);
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/base/Command.java b/src/main/java/dev/colbster937/originblacklist/base/Command.java
deleted file mode 100644
index f275ce0..0000000
--- a/src/main/java/dev/colbster937/originblacklist/base/Command.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package dev.colbster937.originblacklist.base;
-
-import org.yaml.snakeyaml.Yaml;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import static dev.colbster937.originblacklist.base.Base.config;
-
-public class Command {
- private static final String permission = "You do not have permission to use this command.";
-
- public interface CommandContext {
- String getName();
- void reply(String message);
- boolean hasPermission(String permission);
- String[] getArgs();
- }
-
- public static void usage(CommandContext ctx) {
- ctx.reply("Commands:");
- ctx.reply(" - /originblacklist reload");
- ctx.reply(" - /originblacklist add ");
- ctx.reply(" - /originblacklist remove ");
- ctx.reply(" - /originblacklist list");
- }
-
- public static void handle(CommandContext ctx) {
- String[] args = ctx.getArgs();
- if (!ctx.hasPermission("originblacklist.command")) {
- ctx.reply(permission);
- return;
- } else if (args.length == 0) {
- usage(ctx);
- return;
- }
-
- String sub = args[0].toLowerCase();
- String sub1 = args.length > 1 ? args[1].toLowerCase() : "";
- String sub2 = args.length > 2 ? args[2].toLowerCase() : "";
-
- switch (sub) {
- case "reload" -> {
- if (ctx.hasPermission("originblacklist.reload")) {
- Base.reloadConfig();
- ctx.reply("Reloaded.");
- } else {
- ctx.reply(permission);
- return;
- }
- }
-
- case "add" -> {
- if (!ctx.hasPermission("originblacklist.add")) {
- ctx.reply(permission);
- return;
- }
-
- if (sub1.isEmpty() || sub2.isEmpty()) {
- usage(ctx);
- return;
- }
-
- List list = switch (sub1) {
- case "brand" -> Base.config.blacklist.brands;
- case "origin" -> Base.config.blacklist.origins;
- case "player" -> Base.config.blacklist.players;
- case "ip" -> Base.config.blacklist.ips;
- default -> null;
- };
-
- if (list == null) {
- usage(ctx);
- return;
- }
-
- if (!list.contains(sub2)) {
- list.add(sub2);
- ctx.reply("Added " + sub2 + " to " + sub1 + " blacklist.");
- } else {
- ctx.reply("Already blacklisted.");
- }
- try {
- config.saveConfig(Base.config.toMap(), new File("plugins/originblacklist/config.yml"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- Base.reloadConfig();
- }
-
- case "remove" -> {
- if (!ctx.hasPermission("originblacklist.remove")) {
- ctx.reply(permission);
- return;
- }
-
- if (sub1.isEmpty() || sub2.isEmpty()) {
- usage(ctx);
- return;
- }
-
- List list = switch (sub1) {
- case "brand" -> Base.config.blacklist.brands;
- case "origin" -> Base.config.blacklist.origins;
- case "player" -> Base.config.blacklist.players;
- case "ip" -> Base.config.blacklist.ips;
- default -> null;
- };
-
- if (list == null) {
- usage(ctx);
- return;
- }
-
- if (list.remove(sub2)) {
- ctx.reply("Removed " + sub2 + " from " + sub1 + " blacklist.");
- } else {
- ctx.reply("Entry not found in " + sub1 + ".");
- }
- try {
- config.saveConfig(Base.config.toMap(), new File("plugins/originblacklist/config.yml"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- Base.reloadConfig();
- }
-
- case "list" -> {
- if (!ctx.hasPermission("originblacklist.view")) {
- ctx.reply(permission);
- return;
- }
-
- ctx.reply("Blacklist:");
-
- ctx.reply(" Brands:");
- for (String s : Base.config.blacklist.brands) ctx.reply(" - " + s + "");
-
- ctx.reply(" Origins:");
- for (String s : Base.config.blacklist.origins) ctx.reply(" - " + s + "");
-
- ctx.reply(" Players:");
- for (String s : Base.config.blacklist.players) ctx.reply(" - " + s + "");
-
- ctx.reply(" IPs:");
- for (String s : Base.config.blacklist.ips) ctx.reply(" - " + s + "");
- }
-
- default -> usage(ctx);
- }
- }
-
- public static List suggest(CommandContext ctx) {
- String[] args = ctx.getArgs();
-
- if (args.length == 1) {
- return List.of("reload", "add", "remove", "list");
- }
-
- if (args.length == 2 && args[0].equalsIgnoreCase("add")) {
- return List.of("brand", "origin", "player", "ip");
- }
-
- return List.of();
- }
-
- public Map toMap() {
- Yaml yaml = new Yaml();
- return yaml.load(yaml.dump(this));
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/base/ConfigManager.java b/src/main/java/dev/colbster937/originblacklist/base/ConfigManager.java
deleted file mode 100644
index f9d548d..0000000
--- a/src/main/java/dev/colbster937/originblacklist/base/ConfigManager.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package dev.colbster937.originblacklist.base;
-
-import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.LoaderOptions;
-import org.yaml.snakeyaml.constructor.Constructor;
-import org.yaml.snakeyaml.DumperOptions;
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import inet.ipaddr.IPAddress;
-
-public class ConfigManager {
- public Messages messages = new Messages();
- //public List subscriptions = List.of();
- public Blacklist blacklist = new Blacklist();
- public Discord discord = new Discord();
-
- public static ConfigManager loadConfig(Base.LoggerAdapter logger) {
- File f = new File("plugins/originblacklist/config.yml");
-
- try {
- if (!f.exists()) {
- f.getParentFile().mkdirs();
- try (InputStream in = ConfigManager.class.getResourceAsStream("/config.yml")) {
- if (in != null) Files.copy(in, f.toPath(), StandardCopyOption.REPLACE_EXISTING);
- }
- }
-
- Constructor constructor = new Constructor(ConfigManager.class, new LoaderOptions());
- constructor.setPropertyUtils(new org.yaml.snakeyaml.introspector.PropertyUtils() {{
- setSkipMissingProperties(true);
- }});
- Yaml y = new Yaml(constructor);
- ConfigManager l = null;
-
- try (InputStream in = new FileInputStream(f)) {
- l = y.load(in);
- } catch (Exception ex) {
- logger.warn("Error loading config: " + ex.getMessage());
- }
-
- if (l == null) {
- l = new ConfigManager();
- }
-
- try {
- Yaml raw = new Yaml();
- Map u = raw.load(new FileInputStream(f));
- Map d = raw.load(ConfigManager.class.getResourceAsStream("/config.yml"));
- if (mergeConfig(u, d)) saveConfig(u, f);
- } catch (Exception ex) {
- logger.warn("YAML merge error: " + ex.getMessage());
- }
-
- l.blacklist.resolveIPS(logger);
-
- return l;
- } catch (IOException e) {
- return new ConfigManager();
- }
- }
-
- @SuppressWarnings("unchecked")
- private static boolean mergeConfig(Map u, Map d) {
- boolean c = false;
- for (String k : d.keySet()) {
- if (!u.containsKey(k)) {
- u.put(k, d.get(k));
- c = true;
- } else if (u.get(k) instanceof Map && d.get(k) instanceof Map)
- c |= mergeConfig((Map) u.get(k), (Map) d.get(k));
- }
- return c;
- }
-
- public static void saveConfig(Map m, File f) throws IOException {
- DumperOptions o = new DumperOptions();
- o.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
- o.setPrettyFlow(true);
- new Yaml(o).dump(m, new FileWriter(f));
- }
-
- public Map toMap() {
- DumperOptions options = new DumperOptions();
- options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
- options.setPrettyFlow(true);
- options.setAllowReadOnlyProperties(true);
-
- Yaml yaml = new Yaml(options);
- String yaml1 = yaml.dumpAsMap(this);
- Yaml parser = new Yaml();
- Object yaml2 = parser.load(yaml1);
-
- return (Map) yaml2;
- }
-
- public static class Blacklist {
- public List origins;
- public List brands;
- public List players;
- public List ips = List.of();
- public transient Set ips1 = new CopyOnWriteArraySet<>();
- public boolean missing_origin;
- //public String blacklist_redirect;
-
- public void resolveIPS(Base.LoggerAdapter logger) {
- for (String line : ips) {
- try {
- ips1.add(new inet.ipaddr.IPAddressString(line).toAddress());
- } catch (Throwable ignored) {}
- }
- }
- }
-
- public static class Discord {
- public String webhook;
- }
-
- public static class Messages {
- public String kick;
- public MOTD motd;
- public Help help;
- }
-
- public static class MOTD {
- public boolean enabled;
- public String text;
- public String icon;
- }
-
- public static class Help {
- public String generic;
- public String player;
- public String ip;
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/base/IPBlacklist.java b/src/main/java/dev/colbster937/originblacklist/base/IPBlacklist.java
deleted file mode 100644
index 72cb01b..0000000
--- a/src/main/java/dev/colbster937/originblacklist/base/IPBlacklist.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package dev.colbster937.originblacklist.base;
-
-import java.util.logging.Logger;
-
-import inet.ipaddr.AddressStringException;
-import inet.ipaddr.IPAddress;
-import inet.ipaddr.IPAddressString;
-
-import static dev.colbster937.originblacklist.base.Base.config;
-
-public class IPBlacklist {
- private Logger logger = null;
-
- public IPBlacklist() {
- this.logger = logger;
- }
-
- public boolean check(String addr) {
- IPAddress ip;
- String addr1 = addr;
- try {
- if (addr.startsWith("/")) {
- addr1 = addr.substring(1);
- }
- if (addr1.startsWith("[") && addr1.endsWith("]")) {
- addr1 = addr1.substring(1, addr1.length() - 1);
- }
- ip = new IPAddressString(addr1).toAddress();
- } catch (AddressStringException e) {
- throw new RuntimeException("Invalid IP address: " + addr, e);
- }
-
- return config.blacklist.ips1.stream().anyMatch(s -> {
- try {
- return s.contains(ip);
- } catch (Exception e) {
- return false;
- }
- });
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/bukkit/CommandBukkit.java b/src/main/java/dev/colbster937/originblacklist/bukkit/CommandBukkit.java
deleted file mode 100644
index 6af3221..0000000
--- a/src/main/java/dev/colbster937/originblacklist/bukkit/CommandBukkit.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package dev.colbster937.originblacklist.bukkit;
-
-import dev.colbster937.originblacklist.base.Command.CommandContext;
-import net.kyori.adventure.text.minimessage.MiniMessage;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-
-public class CommandBukkit implements CommandExecutor {
-
- @Override
- public boolean onCommand(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) {
- dev.colbster937.originblacklist.base.Command.handle(new CommandContext() {
- @Override
- public String getName() {
- return sender.getName();
- }
-
- @Override
- public void reply(String msg) {
- sender.sendMessage(LegacyComponentSerializer.legacySection()
- .serialize(MiniMessage.miniMessage().deserialize(msg)));
- }
-
- @Override
- public boolean hasPermission(String permission) {
- return sender.hasPermission(permission);
- }
-
- @Override
- public String[] getArgs() {
- return args;
- }
- });
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/dev/colbster937/originblacklist/bukkit/OriginBlacklistBukkit.java b/src/main/java/dev/colbster937/originblacklist/bukkit/OriginBlacklistBukkit.java
deleted file mode 100644
index f3051a9..0000000
--- a/src/main/java/dev/colbster937/originblacklist/bukkit/OriginBlacklistBukkit.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package dev.colbster937.originblacklist.bukkit;
-
-import dev.colbster937.originblacklist.base.Base;
-import net.lax1dude.eaglercraft.backend.server.api.bukkit.EaglerXServerAPI;
-import net.lax1dude.eaglercraft.backend.server.api.bukkit.event.EaglercraftLoginEvent;
-import net.lax1dude.eaglercraft.backend.server.api.bukkit.event.EaglercraftMOTDEvent;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
-import org.bukkit.event.Listener;
-import org.bukkit.plugin.Plugin;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.bukkit.plugin.Plugin;
-
-public class OriginBlacklistBukkit extends JavaPlugin implements Listener {
-
- @Override
- public void onEnable() {
- Plugin plugin = getServer().getPluginManager().getPlugin("EaglercraftXServer");
- if (plugin != null) {
- String version = plugin.getDescription().getVersion();
- if (!Base.checkVer(version, Base.pluginVer)) {
- getLogger().severe("EaglerXServer " + Base.pluginVer + " is required!");
- throw new RuntimeException("Incompatible plugin version");
- }
- } else {
- throw new RuntimeException("Missing EaglerXServer");
- }
-
-
- Base.setLogger(new Base.LoggerAdapter() {
- @Override public void info(String msg) { getLogger().info(msg); }
- @Override public void warn(String msg) { getLogger().warning(msg); }
- @Override public void error(String msg) { getLogger().severe(msg); }
- });
-
- Base.setApi(EaglerXServerAPI.instance());
- Base.reloadConfig();
- Base.init();
-
- getCommand("originblacklist").setExecutor(new CommandBukkit());
- getServer().getPluginManager().registerEvents(this, this);
-
- getLogger().info("Loaded Bukkit plugin");
- }
-
- @EventHandler
- public void onLogin(EaglercraftLoginEvent event) {
- Base.handleConnection(event);
- }
-
- @EventHandler(priority = EventPriority.MONITOR)
- public void onMOTD(EaglercraftMOTDEvent event) {
- Base.handleMOTD(event);
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/bungee/CommandBungee.java b/src/main/java/dev/colbster937/originblacklist/bungee/CommandBungee.java
deleted file mode 100644
index 05da43e..0000000
--- a/src/main/java/dev/colbster937/originblacklist/bungee/CommandBungee.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package dev.colbster937.originblacklist.bungee;
-
-import dev.colbster937.originblacklist.base.Command.CommandContext;
-import net.md_5.bungee.api.CommandSender;
-import net.md_5.bungee.api.chat.TextComponent;
-
-public class CommandBungee extends net.md_5.bungee.api.plugin.Command {
-
- public CommandBungee() {
- super("originblacklist", "originblacklist.use");
- }
-
- @Override
- public void execute(CommandSender sender, String[] args) {
- dev.colbster937.originblacklist.base.Command.handle(new CommandContext() {
- public String getName() {
- return sender.getName();
- }
-
- public void reply(String msg) {
- sender.sendMessage(TextComponent.fromLegacyText(msg));
- }
-
- public boolean hasPermission(String permission) {
- return sender.hasPermission(permission);
- }
-
- public String[] getArgs() {
- return args;
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/main/java/dev/colbster937/originblacklist/bungee/OriginBlacklistBungee.java b/src/main/java/dev/colbster937/originblacklist/bungee/OriginBlacklistBungee.java
deleted file mode 100644
index 113888a..0000000
--- a/src/main/java/dev/colbster937/originblacklist/bungee/OriginBlacklistBungee.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package dev.colbster937.originblacklist.bungee;
-
-import dev.colbster937.originblacklist.base.Base;
-import net.lax1dude.eaglercraft.backend.server.api.bungee.EaglerXServerAPI;
-import net.lax1dude.eaglercraft.backend.server.api.bungee.event.EaglercraftLoginEvent;
-import net.lax1dude.eaglercraft.backend.server.api.bungee.event.EaglercraftMOTDEvent;
-import net.md_5.bungee.api.event.PreLoginEvent;
-import net.md_5.bungee.api.plugin.Plugin;
-import net.md_5.bungee.api.plugin.Listener;
-import net.md_5.bungee.event.EventHandler;
-import net.md_5.bungee.event.EventPriority;
-
-public class OriginBlacklistBungee extends Plugin implements Listener {
-
- @Override
- public void onEnable() {
- Plugin plugin = getProxy().getPluginManager().getPlugin("EaglercraftXServer");
- if (plugin != null) {
- String version = plugin.getDescription().getVersion();
- if (!Base.checkVer(version, Base.pluginVer)) {
- getLogger().severe("EaglerXServer " + Base.pluginVer + " is required!");
- throw new RuntimeException("Incompatible plugin version");
- }
- } else {
- throw new RuntimeException("Missing EaglerXServer");
- }
-
-
- Base.setLogger(new Base.LoggerAdapter() {
- @Override public void info(String msg) { getLogger().info(msg); }
- @Override public void warn(String msg) { getLogger().warning(msg); }
- @Override public void error(String msg) { getLogger().severe(msg); }
- });
-
- Base.setApi(EaglerXServerAPI.instance());
- Base.reloadConfig();
- Base.init();
-
- getProxy().getPluginManager().registerCommand(this, new CommandBungee());
- getProxy().getPluginManager().registerListener(this, this);
-
- getLogger().info("Loaded Bungee plugin");
- }
-
- @EventHandler
- public void onLogin(EaglercraftLoginEvent event) {
- Base.handleConnection(event);
- }
-
- @EventHandler(priority = EventPriority.HIGHEST)
- public void onMOTD(EaglercraftMOTDEvent event) {
- Base.handleMOTD(event);
- }
-}
diff --git a/src/main/java/dev/colbster937/originblacklist/velocity/CommandVelocity.java b/src/main/java/dev/colbster937/originblacklist/velocity/CommandVelocity.java
deleted file mode 100644
index a6a29b6..0000000
--- a/src/main/java/dev/colbster937/originblacklist/velocity/CommandVelocity.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package dev.colbster937.originblacklist.velocity;
-
-import com.velocitypowered.api.command.SimpleCommand;
-import dev.colbster937.originblacklist.base.Command;
-import dev.colbster937.originblacklist.base.Command.CommandContext;
-import net.kyori.adventure.text.minimessage.MiniMessage;
-
-import java.util.List;
-
-public class CommandVelocity implements SimpleCommand {
-
- @Override
- public void execute(Invocation invocation) {
- Command.handle(new VelocityCommandContext(invocation));
- }
-
- @Override
- public List suggest(Invocation invocation) {
- return Command.suggest(new VelocityCommandContext(invocation));
- }
-
- public static class VelocityCommandContext implements CommandContext {
- private final Invocation invocation;
-
- public VelocityCommandContext(Invocation invocation) {
- this.invocation = invocation;
- }
-
- @Override
- public String getName() {
- return invocation.source().toString();
- }
-
- @Override
- public void reply(String message) {
- invocation.source().sendMessage(MiniMessage.miniMessage().deserialize(message));
- }
-
- @Override
- public boolean hasPermission(String permission) {
- return invocation.source().hasPermission(permission);
- }
-
- @Override
- public String[] getArgs() {
- return invocation.arguments();
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/dev/colbster937/originblacklist/velocity/OriginBlacklistVelocity.java b/src/main/java/dev/colbster937/originblacklist/velocity/OriginBlacklistVelocity.java
deleted file mode 100644
index 9414625..0000000
--- a/src/main/java/dev/colbster937/originblacklist/velocity/OriginBlacklistVelocity.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package dev.colbster937.originblacklist.velocity;
-
-import com.google.inject.Inject;
-import com.velocitypowered.api.event.PostOrder;
-import com.velocitypowered.api.event.connection.PreLoginEvent;
-import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
-import com.velocitypowered.api.event.Subscribe;
-import com.velocitypowered.api.proxy.ProxyServer;
-import dev.colbster937.originblacklist.base.Base;
-import net.kyori.adventure.text.Component;
-import net.lax1dude.eaglercraft.backend.server.api.velocity.EaglerXServerAPI;
-import net.lax1dude.eaglercraft.backend.server.api.velocity.event.EaglercraftLoginEvent;
-import net.lax1dude.eaglercraft.backend.server.api.velocity.event.EaglercraftMOTDEvent;
-import java.net.InetAddress;
-import org.slf4j.Logger;
-
-public class OriginBlacklistVelocity {
-
- private final ProxyServer proxy;
- private final Base.LoggerAdapter logger;
-
- @Inject
- public OriginBlacklistVelocity(ProxyServer proxy1, Logger logger1) {
- this.proxy = proxy1;
- this.logger = new Base.LoggerAdapter() {
- @Override public void info(String msg) { logger1.info(msg); }
- @Override public void warn(String msg) { logger1.warn(msg); }
- @Override public void error(String msg) { logger1.error(msg); }
- };
- Base.setLogger(this.logger);
- }
-
- @Subscribe
- public void onProxyInitialization(ProxyInitializeEvent event) {
- proxy.getPluginManager().getPlugin("eaglerxserver").ifPresentOrElse(plugin -> {
- if (!Base.checkVer(plugin.getDescription().getVersion().orElse("1.0.0"), Base.pluginVer)) {
- logger.error("EaglerXServer " + Base.pluginVer + " is required!");
- throw new RuntimeException("Incompatible plugin version");
- }
- }, () -> {
- throw new RuntimeException("Missing EaglerXServer");
- });
- Base.setApi(EaglerXServerAPI.instance());
- Base.reloadConfig();
- Base.init();
- proxy.getCommandManager().register("originblacklist", new CommandVelocity());
- logger.info("Loaded Velocity plugin");
- }
-
- @Subscribe
- public void onLogin(EaglercraftLoginEvent event) {
- Base.handleConnection(event);
- }
-
- @Subscribe(order = PostOrder.LAST)
- public void onMOTD(EaglercraftMOTDEvent event) {
- Base.handleMOTD(event);
- }
-}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/OriginBlacklist.java b/src/main/java/xyz/webmc/originblacklist/base/OriginBlacklist.java
new file mode 100644
index 0000000..1911ee7
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/OriginBlacklist.java
@@ -0,0 +1,213 @@
+package xyz.webmc.originblacklist.base;
+
+import xyz.webmc.originblacklist.base.config.OriginBlacklistConfig;
+import xyz.webmc.originblacklist.base.enums.EnumBlacklistType;
+import xyz.webmc.originblacklist.base.enums.EnumLogLevel;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistLoginEvent;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistMOTDEvent;
+import xyz.webmc.originblacklist.base.util.IOriginBlacklistPlugin;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+import xyz.webmc.originblacklist.base.util.UpdateChecker;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.semver4j.Semver;
+
+import de.marhali.json5.Json5Array;
+import de.marhali.json5.Json5Element;
+
+import inet.ipaddr.AddressStringException;
+import inet.ipaddr.IPAddressString;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.lax1dude.eaglercraft.backend.server.api.query.IMOTDConnection;
+
+public final class OriginBlacklist {
+ public static final Semver REQUIRED_API_VER = new Semver("1.0.2");
+ public static final String GENERIC_STR = "generic";
+ public static final String UNKNOWN_STR = "unknown";
+ public static final String PLUGIN_REPO = "WebMCDevelopment/originblacklist";
+ public static final int BSTATS_ID = 28776;
+
+ private final IOriginBlacklistPlugin plugin;
+ private final OriginBlacklistConfig config;
+ private boolean updateAvailable;
+
+ public OriginBlacklist(final IOriginBlacklistPlugin plugin) {
+ this.plugin = plugin;
+ this.config = new OriginBlacklistConfig(plugin);
+ this.checkForUpdate();
+ plugin.scheduleRepeat(() -> {
+ this.checkForUpdate();
+ }, 60, TimeUnit.MINUTES);
+ }
+
+ public final void handleLogin(final OriginBlacklistLoginEvent event) {
+ final OPlayer player = event.getPlayer();
+ final EnumBlacklistType blacklisted = this.testBlacklist(player);
+ if (blacklisted != EnumBlacklistType.NONE) {
+ final String blacklisted_value;
+ if (blacklisted == EnumBlacklistType.ORIGIN) {
+ blacklisted_value = player.getOrigin();
+ } else if (blacklisted == EnumBlacklistType.BRAND) {
+ blacklisted_value = player.getBrand();
+ } else if (blacklisted == EnumBlacklistType.NAME) {
+ blacklisted_value = player.getName();
+ } else if (blacklisted == EnumBlacklistType.ADDR) {
+ blacklisted_value = player.getAddr();
+ } else {
+ blacklisted_value = UNKNOWN_STR;
+ }
+ this.plugin.kickPlayer(this.getBlacklistedComponent("kick", blacklisted.getArrayString(),
+ blacklisted.getAltString(), blacklisted.getString(), "not allowed", "not allowed on the server",
+ blacklisted_value, blacklisted.getActionString()), event);
+ final String name = player.getName();
+ if (isNonNull(name)) {
+ this.plugin.log(EnumLogLevel.INFO, "Prevented blacklisted player " + name + " from joining.");
+ }
+ }
+ }
+
+ public final void handleMOTD(final OriginBlacklistMOTDEvent event) {
+ final OPlayer player = event.getPlayer();
+ final EnumBlacklistType blacklisted = this.testBlacklist(player);
+ if (blacklisted != EnumBlacklistType.NONE) {
+ final String blacklisted_value;
+ if (blacklisted == EnumBlacklistType.ORIGIN) {
+ blacklisted_value = player.getOrigin();
+ } else if (blacklisted == EnumBlacklistType.ADDR) {
+ blacklisted_value = player.getAddr();
+ } else {
+ blacklisted_value = UNKNOWN_STR;
+ }
+ this.plugin.setMOTD(this.getBlacklistedComponent("motd", blacklisted.getArrayString(), blacklisted.getAltString(),
+ blacklisted.getString(), "blacklisted", "blacklisted from the server", blacklisted_value,
+ blacklisted.getActionString()), event);
+ }
+ }
+
+ public final boolean isDebugEnabled() {
+ return this.config.get("debug").getAsBoolean();
+ }
+
+ public final boolean isMetricsEnabled() {
+ return this.config.get("bStats").getAsBoolean();
+ }
+
+ public final OriginBlacklistConfig getConfig() {
+ return this.config;
+ }
+
+ public final void setEaglerMOTD(final Component comp, final OriginBlacklistMOTDEvent event) {
+ final IMOTDConnection conn = event.getEaglerEvent().getMOTDConnection();
+ final List lst = new ArrayList<>();
+ for (String ln : getComponentString(comp).split("\n")) {
+ lst.add(ln);
+ }
+ conn.setServerMOTD(lst);
+ conn.setPlayerTotal(0);
+ conn.setPlayerUnlimited();
+ conn.setPlayerList(List.of());
+ conn.setServerIcon(this.config.getIconBytes());
+ conn.sendToUser();
+ conn.disconnect();
+ }
+
+ private final EnumBlacklistType testBlacklist(final OPlayer player) {
+ final String name = player.getName();
+ final String addr = player.getAddr();
+ final String origin = player.getOrigin();
+ final String brand = player.getBrand();
+
+ if (isNonNull(origin)) {
+ for (final Json5Element element : this.config.get("blacklist.origins").getAsJson5Array()) {
+ if (origin.matches(element.getAsString())) {
+ return EnumBlacklistType.ORIGIN;
+ }
+ }
+ }
+
+ if (isNonNull(brand)) {
+ for (final Json5Element element : this.config.get("blacklist.brands").getAsJson5Array()) {
+ if (brand.matches(element.getAsString())) {
+ return EnumBlacklistType.BRAND;
+ }
+ }
+ }
+
+ if (isNonNull(name)) {
+ for (final Json5Element element : this.config.get("blacklist.player_names").getAsJson5Array()) {
+ this.plugin.log(EnumLogLevel.DEBUG, element.getAsString());
+ if (name.matches(element.getAsString())) {
+ return EnumBlacklistType.NAME;
+ }
+ }
+ }
+
+ if (isNonNull(addr)) {
+ for (final Json5Element element : this.config.get("blacklist.ip_addresses").getAsJson5Array()) {
+ try {
+ if ((new IPAddressString(element.getAsString()).toAddress())
+ .contains((new IPAddressString(addr)).toAddress())) {
+ return EnumBlacklistType.ADDR;
+ }
+ } catch (final AddressStringException exception) {
+ exception.printStackTrace();
+ }
+ }
+ }
+
+ return EnumBlacklistType.NONE;
+ }
+
+ private final Component getBlacklistedComponent(final String type, final String id, final String blockType,
+ final String blockTypeAlt, final String notAllowed, final String notAllowedAlt, final String blockValue,
+ final String action) {
+ final Json5Array arr = this.config.get("messages." + type).getAsJson5Array();
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < arr.size(); i++) {
+ if (i > 0)
+ sb.append("\n");
+ sb.append(arr.get(i).getAsString());
+ }
+ final String str = sb.toString()
+ .replaceAll("%action%", this.config.get("messages.actions." + action).getAsString())
+ .replaceAll("%block_type%", blockType)
+ .replaceAll("%block_type%", blockType)
+ .replaceAll("%not_allowed%", notAllowed)
+ .replaceAll("%not_allowed_alt%", notAllowedAlt)
+ .replaceAll("%blocked_value%", blockValue);
+ return MiniMessage.miniMessage().deserialize(str);
+ }
+
+ private final void checkForUpdate() {
+ (new Thread(() -> {
+ this.updateAvailable = UpdateChecker.checkForUpdate(PLUGIN_REPO, this.plugin.getPluginVersion(),
+ this.config.get("update_checker.allow_snapshots").getAsBoolean());
+ if (this.updateAvailable) {
+ this.plugin.log(EnumLogLevel.INFO, "Update Available! Download at https://github.com/" + PLUGIN_REPO + ".git");
+ }
+ })).run();
+ }
+
+ public static final String getComponentString(final Component comp) {
+ return LegacyComponentSerializer.legacySection().serialize(comp);
+ }
+
+ public static final String getLegacyFromMiniMessage(final String str) {
+ return getComponentString(MiniMessage.miniMessage().deserialize(str));
+ }
+
+ public static final String getPNGBase64FromBytes(final byte[] bytes) {
+ return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
+ }
+
+ public static final boolean isNonNull(final String str) {
+ return str != null && !str.isEmpty() && !str.isBlank() && !str.equals("null");
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/command/CommandContext.java b/src/main/java/xyz/webmc/originblacklist/base/command/CommandContext.java
new file mode 100644
index 0000000..d9f1a03
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/command/CommandContext.java
@@ -0,0 +1,8 @@
+package xyz.webmc.originblacklist.base.command;
+
+public interface CommandContext {
+ String getPlayerName();
+ void reply(final String message);
+ boolean hasPermission(final String permission);
+ String[] getArgs();
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/command/ICommand.java b/src/main/java/xyz/webmc/originblacklist/base/command/ICommand.java
new file mode 100644
index 0000000..5791ab7
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/command/ICommand.java
@@ -0,0 +1,10 @@
+package xyz.webmc.originblacklist.base.command;
+
+import java.util.List;
+
+public interface ICommand {
+ static final String NO_PERMISSION = "You don't have permission to use this command.";
+ boolean execute(final CommandContext ctx);
+ List suggest(final CommandContext ctx);
+ void usage(final CommandContext ctx);
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/command/OriginBlacklistCommand.java b/src/main/java/xyz/webmc/originblacklist/base/command/OriginBlacklistCommand.java
new file mode 100644
index 0000000..b08318b
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/command/OriginBlacklistCommand.java
@@ -0,0 +1,76 @@
+package xyz.webmc.originblacklist.base.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+
+import java.util.List;
+
+import de.marhali.json5.Json5Element;
+
+public class OriginBlacklistCommand implements ICommand {
+ private final OriginBlacklist plugin;
+
+ public OriginBlacklistCommand(OriginBlacklist plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public boolean execute(final CommandContext ctx) {
+ final String[] args = ctx.getArgs();
+ if (ctx.hasPermission("originblacklist.command")) {
+ if (args.length > 0) {
+ final String command = args[0].toLowerCase();
+ if ("reload".equals(command)) {
+ if (ctx.hasPermission("originblacklist.command.reload")) {
+ this.plugin.getConfig().reloadConfig();
+ ctx.reply("Configuration Reloaded");
+ } else {
+ ctx.reply(NO_PERMISSION);
+ }
+ } else if ("list".equals(command)) {
+ if (ctx.hasPermission("originblacklist.command.reload")) {
+ ctx.reply("Blacklist:");
+ ctx.reply(" - Origins:");
+ for (final Json5Element element : this.plugin.getConfig().get("blacklist.origins").getAsJson5Array()) {
+ ctx.reply(" - " + element.getAsString() + "");
+ }
+ ctx.reply(" - Brands:");
+ for (final Json5Element element : this.plugin.getConfig().get("blacklist.brands").getAsJson5Array()) {
+ ctx.reply(" - " + element.getAsString() + "");
+ }
+ ctx.reply(" - Players:");
+ for (final Json5Element element : this.plugin.getConfig().get("blacklist.player_names").getAsJson5Array()) {
+ ctx.reply(" - " + element.getAsString() + "");
+ }
+ ctx.reply(" - IPs:");
+ for (final Json5Element element : this.plugin.getConfig().get("blacklist.ip_addresses").getAsJson5Array()) {
+ ctx.reply(" - " + element.getAsString() + "");
+ }
+ } else {
+ ctx.reply(NO_PERMISSION);
+ }
+ } else {
+ this.usage(ctx);
+ }
+ } else {
+ this.usage(ctx);
+ }
+ } else {
+ ctx.reply("");
+ }
+ return true;
+ }
+
+ @Override
+ public List suggest(final CommandContext ctx) {
+ return List.of();
+ }
+
+ @Override
+ public void usage(CommandContext ctx) {
+ ctx.reply("Commands:");
+ ctx.reply(" - /originblacklist reload");
+ //ctx.reply(" - /originblacklist add ");
+ //ctx.reply(" - /originblacklist remove ");
+ ctx.reply(" - /originblacklist list");
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/config/OriginBlacklistConfig.java b/src/main/java/xyz/webmc/originblacklist/base/config/OriginBlacklistConfig.java
new file mode 100644
index 0000000..03407e6
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/config/OriginBlacklistConfig.java
@@ -0,0 +1,257 @@
+package xyz.webmc.originblacklist.base.config;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.util.IOriginBlacklistPlugin;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import javax.imageio.ImageIO;
+
+import de.marhali.json5.Json5;
+import de.marhali.json5.Json5Array;
+import de.marhali.json5.Json5Element;
+import de.marhali.json5.Json5Object;
+import de.marhali.json5.Json5Primitive;
+
+public final class OriginBlacklistConfig {
+ private final Json5 json5;
+ private final File file;
+ private final Path filePath;
+ private final File iconFile;
+ private final Path iconPath;
+ private Json5Object config;
+ private byte[] icon;
+ private String icon64;
+
+ public OriginBlacklistConfig(IOriginBlacklistPlugin plugin) {
+ this.json5 = Json5.builder(builder -> builder
+ .quoteless()
+ .quoteSingle()
+ .parseComments()
+ .writeComments()
+ .prettyPrinting()
+ .build());
+ final String dir = "plugins/" + plugin.getPluginId();
+ this.file = new File(dir + "/config.json5");
+ this.filePath = file.toPath();
+ this.iconFile = new File(dir + "/blacklisted.png");
+ this.iconPath = iconFile.toPath();
+ this.loadConfig();
+ }
+
+ private final void loadConfig() {
+ try {
+ this.reloadConfigUnsafe();
+ } catch (final IOException exception) {
+ throw new RuntimeException("Failed to load config.", exception);
+ }
+ this.reloadIconImage();
+ }
+
+ public final void reloadConfig() {
+ try {
+ this.reloadConfigUnsafe();
+ } catch (final IOException exception) {
+ exception.printStackTrace();
+ }
+ this.reloadIconImage();
+ }
+
+ private final void reloadConfigUnsafe() throws IOException {
+ if (this.file.exists()) {
+ String text = Files.readString(this.file.toPath(), StandardCharsets.UTF_8);
+ Json5Element parsed = this.json5.parse(text);
+ if (parsed instanceof Json5Object) {
+ this.config = (Json5Object) parsed;
+ if (merge(this.config, getDefaultConfig())) {
+ this.saveConfig();
+ }
+ } else {
+ throw new IOException("Config must be an object!");
+ }
+ } else {
+ this.config = getDefaultConfig();
+ this.saveConfig();
+ }
+ }
+
+ private final void reloadIconImage() {
+ try {
+ if (!this.iconFile.exists()) {
+ this.iconFile.getParentFile().mkdirs();
+ final InputStream in = OriginBlacklist.class.getResourceAsStream("/blacklisted.png");
+ Files.copy(in, iconPath, StandardCopyOption.REPLACE_EXISTING);
+ in.close();
+ }
+
+ final BufferedImage img = ImageIO.read(iconFile);
+
+ if (img.getWidth() == 64 && img.getHeight() == 64) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ImageIO.write(img, "png", baos);
+ this.icon64 = OriginBlacklist.getPNGBase64FromBytes(baos.toByteArray());
+ byte[] bytes = new byte[64 * 64 * 4];
+ for (int y = 0; y < 64; y++) {
+ for (int x = 0; x < 64; x++) {
+ int pixel = img.getRGB(x, y);
+ int i = (y * 64 + x) * 4;
+ bytes[i] = (byte) ((pixel >> 16) & 0xFF);
+ bytes[i + 1] = (byte) ((pixel >> 8) & 0xFF);
+ bytes[i + 2] = (byte) (pixel & 0xFF);
+ bytes[i + 3] = (byte) ((pixel >> 24) & 0xFF);
+ }
+ this.icon = bytes;
+ }
+ } else {
+ throw new IOException("Icon must be 64x64!");
+ }
+ } catch (final IOException exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ public final void saveConfig() {
+ try {
+ this.file.getParentFile().mkdirs();
+ Files.write(this.filePath, this.json5.serialize(this.config).getBytes(StandardCharsets.UTF_8));
+ } catch (final IOException exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ public final Json5Element get(final String key) {
+ Json5Element element = null;
+
+ if (this.config != null && OriginBlacklist.isNonNull(key)) {
+ element = this.config;
+ final String[] parts = key.split("\\.");
+
+ for (final String part : parts) {
+ if (element instanceof Json5Object) {
+ final Json5Object obj = (Json5Object) element;
+ if (obj.has(part)) {
+ element = obj.get(part);
+ } else {
+ element = null;
+ }
+ } else {
+ element = null;
+ }
+
+ if (element == null) {
+ break;
+ }
+ }
+ }
+
+ return element;
+ }
+
+ public final byte[] getIconBytes() {
+ return this.icon;
+ }
+
+ public final String getIconBase64URI() {
+ return this.icon64;
+ }
+
+ private static final Json5Object getDefaultConfig() {
+ final Json5Object obj = new Json5Object();
+ addJSONObj(obj, "debug", Json5Primitive.fromBoolean(false), null);
+ final Json5Object mobj = new Json5Object();
+ final Json5Array kick = new Json5Array();
+ kick.add("This %block_type% is %not_allowed_alt%!");
+ kick.add("» %blocked_value% «");
+ kick.add("");
+ kick.add("%action%");
+ kick.add("");
+ kick.add("Think this is a mistake? Join our discord:");
+ kick.add("discord.gg/changethisintheconfig");
+ addJSONObj(mobj, "kick", kick, null);
+ final Json5Array motd = new Json5Array();
+ motd.add("This %block_type% is %not_allowed%!");
+ motd.add("» %blocked_value%");
+ addJSONObj(mobj, "motd", motd, null);
+ final Json5Object actions = new Json5Object();
+ actions.add("generic", Json5Primitive.fromString("Please switch to a different %block_type%."));
+ actions.add("player_name", Json5Primitive.fromString("Please change your %block_type%."));
+ actions.add("ip_address", Json5Primitive.fromString("Please contact staff for assistance."));
+ addJSONObj(mobj, "actions", actions, null);
+ addJSONObj(obj, "messages", mobj, null);
+ final Json5Object bobj = new Json5Object();
+ final Json5Array origins = new Json5Array();
+ origins.add(".*eaglerhackedclients\\.vercel\\.app.*");
+ origins.add(".*eaglerhacks\\.github\\.io.*");
+ origins.add(".*mcproject\\.vercel\\.app.*");
+ origins.add(".*wurst-b2\\.vercel\\.app.*");
+ origins.add(".*flqmedev\\.github\\.io.*");
+ origins.add(".*wurst2\\.vercel\\.app.*");
+ origins.add(".*dhyeybg7\\.vercel\\.app.*");
+ origins.add(".*uec\\.vercel\\.app.*");
+ origins.add(".*valux-game\\.github\\.io.*");
+ origins.add(".*project516\\.dev.*");
+ addJSONObj(bobj, "origins", origins, null);
+ final Json5Array brands = new Json5Array();
+ brands.add(".*dragonx.*");
+ brands.add(".*piclient.*");
+ brands.add(".*justin.*");
+ brands.add(".*wurstx.*");
+ brands.add(".*moonlight.*");
+ addJSONObj(bobj, "brands", brands, null);
+ final Json5Array players = new Json5Array();
+ players.add("Admin");
+ addJSONObj(bobj, "player_names", players, null);
+ final Json5Array ips = new Json5Array();
+ ips.add("192.0.2.0/24");
+ addJSONObj(bobj, "ip_addresses", ips, null);
+ addJSONObj(obj, "blacklist", bobj, null);
+ final Json5Object dobj = new Json5Object();
+ addJSONObj(dobj, "enabled", Json5Primitive.fromBoolean(false), null);
+ addJSONObj(dobj, "webhook_urls", new Json5Array(), null);
+ addJSONObj(obj, "discord", dobj, null);
+ final Json5Object uobj = new Json5Object();
+ addJSONObj(uobj, "enabled", Json5Primitive.fromBoolean(true), null);
+ addJSONObj(uobj, "allow_snapshots", Json5Primitive.fromBoolean(false), null);
+ addJSONObj(uobj, "auto_update", Json5Primitive.fromBoolean(false), null);
+ addJSONObj(obj, "update_checker", uobj, null);
+ addJSONObj(obj, "bStats", Json5Primitive.fromBoolean(true), null);
+ return obj;
+ }
+
+ private static final boolean merge(final Json5Object a, final Json5Object b) {
+ boolean changed = false;
+
+ for (String key : b.keySet()) {
+ Json5Element element = b.get(key);
+ if (!a.has(key)) {
+ a.add(key, element.deepCopy());
+ changed = true;
+ } else {
+ final Json5Element _element = a.get(key);
+ if (_element instanceof Json5Object objA && element instanceof Json5Object objB) {
+ if (merge(objA, objB)) {
+ changed = true;
+ }
+ }
+ }
+ }
+
+ return changed;
+ }
+
+ private static final void addJSONObj(final Json5Object obj, final String key, final Json5Element value,
+ final String comment) {
+ if (OriginBlacklist.isNonNull(comment)) {
+ value.setComment(comment);
+ }
+ obj.add(key, value);
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/enums/EnumBlacklistType.java b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumBlacklistType.java
new file mode 100644
index 0000000..1342572
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumBlacklistType.java
@@ -0,0 +1,39 @@
+package xyz.webmc.originblacklist.base.enums;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+
+public enum EnumBlacklistType {
+ ORIGIN("origin", "website", "origins", null),
+ BRAND("brand", "client", "brands", null),
+ NAME("name", "username", "player_names", "player_name"),
+ ADDR("addr", "ip address", "ip_addresses", "ip_address"),
+ NONE(null, null, null, null);
+
+ private final String str;
+ private final String alt;
+ private final String arr;
+ private final String act;
+
+ private EnumBlacklistType(final String str, final String alt, final String arr, final String act) {
+ this.str = str;
+ this.alt = alt;
+ this.arr = arr;
+ this.act = OriginBlacklist.isNonNull(act) ? act : OriginBlacklist.GENERIC_STR;
+ }
+
+ public final String getString() {
+ return this.str;
+ }
+
+ public final String getAltString() {
+ return this.alt;
+ }
+
+ public final String getArrayString() {
+ return this.arr;
+ }
+
+ public final String getActionString() {
+ return this.act;
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/enums/EnumConnectionType.java b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumConnectionType.java
new file mode 100644
index 0000000..f647c41
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumConnectionType.java
@@ -0,0 +1,6 @@
+package xyz.webmc.originblacklist.base.enums;
+
+public enum EnumConnectionType {
+ JAVA,
+ EAGLER
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/enums/EnumLogLevel.java b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumLogLevel.java
new file mode 100644
index 0000000..5a57d13
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/enums/EnumLogLevel.java
@@ -0,0 +1,8 @@
+package xyz.webmc.originblacklist.base.enums;
+
+public enum EnumLogLevel {
+ INFO,
+ WARN,
+ ERROR,
+ DEBUG
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistEvent.java b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistEvent.java
new file mode 100644
index 0000000..4053fc1
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistEvent.java
@@ -0,0 +1,36 @@
+package xyz.webmc.originblacklist.base.events;
+
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+
+import net.lax1dude.eaglercraft.backend.server.api.event.IBaseServerEvent;
+
+public abstract class OriginBlacklistEvent {
+ private final EnumConnectionType connectionType;
+ private final IBaseServerEvent eaglerEvent;
+ private final Object javaEvent;
+ private final OPlayer player;
+
+ protected OriginBlacklistEvent(final IBaseServerEvent eaglerEvent, final Object javaEvent, final EnumConnectionType connectionType, final OPlayer player) {
+ this.eaglerEvent = eaglerEvent;
+ this.javaEvent = javaEvent;
+ this.connectionType = connectionType;
+ this.player = player;
+ }
+
+ protected IBaseServerEvent getEaglerEvent() {
+ return this.eaglerEvent;
+ }
+
+ public final Object getJavaEvent() {
+ return this.javaEvent;
+ }
+
+ public final EnumConnectionType getConnectionType() {
+ return this.connectionType;
+ }
+
+ public final OPlayer getPlayer() {
+ return this.player;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistLoginEvent.java b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistLoginEvent.java
new file mode 100644
index 0000000..548cb7e
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistLoginEvent.java
@@ -0,0 +1,18 @@
+package xyz.webmc.originblacklist.base.events;
+
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+
+import net.lax1dude.eaglercraft.backend.server.api.event.IEaglercraftLoginEvent;
+
+public final class OriginBlacklistLoginEvent extends OriginBlacklistEvent {
+ public OriginBlacklistLoginEvent(final IEaglercraftLoginEvent eaglerEvent, final Object javaEvent,
+ final EnumConnectionType connectionType, final OPlayer player) {
+ super(eaglerEvent, javaEvent, connectionType, player);
+ }
+
+ @Override
+ public final IEaglercraftLoginEvent getEaglerEvent() {
+ return (IEaglercraftLoginEvent) super.getEaglerEvent();
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistMOTDEvent.java b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistMOTDEvent.java
new file mode 100644
index 0000000..0f6e51a
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/events/OriginBlacklistMOTDEvent.java
@@ -0,0 +1,18 @@
+package xyz.webmc.originblacklist.base.events;
+
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+
+import net.lax1dude.eaglercraft.backend.server.api.event.IEaglercraftMOTDEvent;
+
+public final class OriginBlacklistMOTDEvent extends OriginBlacklistEvent {
+ public OriginBlacklistMOTDEvent(final IEaglercraftMOTDEvent eaglerEvent, final Object javaEvent,
+ final EnumConnectionType connectionType, final OPlayer player) {
+ super(eaglerEvent, javaEvent, connectionType, player);
+ }
+
+ @Override
+ public final IEaglercraftMOTDEvent getEaglerEvent() {
+ return (IEaglercraftMOTDEvent) super.getEaglerEvent();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/webmc/originblacklist/base/util/ChatFormat.java b/src/main/java/xyz/webmc/originblacklist/base/util/ChatFormat.java
new file mode 100644
index 0000000..3b45208
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/util/ChatFormat.java
@@ -0,0 +1,29 @@
+package xyz.webmc.originblacklist.base.util;
+
+public final class ChatFormat {
+ private static final String SYMBOL = "§";
+
+ public static final String BLACK = SYMBOL + "0";
+ public static final String DARK_BLUE = SYMBOL + "1";
+ public static final String DARK_GREEN = SYMBOL + "2";
+ public static final String CYAN = SYMBOL + "3";
+ public static final String DARK_RED = SYMBOL + "4";
+ public static final String DARK_PURPLE = SYMBOL + "5";
+ public static final String GOLD = SYMBOL + "6";
+ public static final String GRAY = SYMBOL + "7";
+ public static final String DARK_GRAY = SYMBOL + "8";
+ public static final String BLUE = SYMBOL + "9";
+ public static final String GREEN = SYMBOL + "a";
+ public static final String AQUA = SYMBOL + "b";
+ public static final String RED = SYMBOL + "c";
+ public static final String LIGHT_PURPLE = SYMBOL + "d";
+ public static final String YELLOW = SYMBOL + "e";
+ public static final String WHITE = SYMBOL + "f";
+
+ public static final String OBFUSCATED = SYMBOL + "k";
+ public static final String BOLD = SYMBOL + "l";
+ public static final String STRIKETHROUGH = SYMBOL + "m";
+ public static final String UNDERLINE = SYMBOL + "n";
+ public static final String ITALIC = SYMBOL + "o";
+ public static final String RESET = SYMBOL + "r";
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/webmc/originblacklist/base/util/IOriginBlacklistPlugin.java b/src/main/java/xyz/webmc/originblacklist/base/util/IOriginBlacklistPlugin.java
new file mode 100644
index 0000000..f15d729
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/util/IOriginBlacklistPlugin.java
@@ -0,0 +1,22 @@
+package xyz.webmc.originblacklist.base.util;
+
+import xyz.webmc.originblacklist.base.enums.EnumLogLevel;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistLoginEvent;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistMOTDEvent;
+
+import java.util.concurrent.TimeUnit;
+
+import org.semver4j.Semver;
+
+import net.kyori.adventure.text.Component;
+
+public interface IOriginBlacklistPlugin {
+ public String getPluginId();
+ public Semver getPluginVersion();
+ public void log(final EnumLogLevel level, final String txt);
+ public void kickPlayer(final Component txt, final OriginBlacklistLoginEvent event);
+ public void setMOTD(final Component txt, final OriginBlacklistMOTDEvent event);
+ public String parsePlaceholders(final OPlayer player, final String str);
+ public void scheduleRepeat(final Runnable task, final int period, final TimeUnit unit);
+ public void shutdown();
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/util/IncompatibleDependencyException.java b/src/main/java/xyz/webmc/originblacklist/base/util/IncompatibleDependencyException.java
new file mode 100644
index 0000000..f1d07e6
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/util/IncompatibleDependencyException.java
@@ -0,0 +1,14 @@
+package xyz.webmc.originblacklist.base.util;
+
+import org.semver4j.Semver;
+
+public final class IncompatibleDependencyException extends RuntimeException {
+ public IncompatibleDependencyException(final String name, final Semver requiredVersion, final Semver currentVersion) {
+ super("Incompatible version of " + name + " is present! Required " + requiredVersion + ", but found "
+ + currentVersion + ".");
+ }
+
+ public IncompatibleDependencyException(final String name) {
+ super("Missing dependency " + name + "!");
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/util/OPlayer.java b/src/main/java/xyz/webmc/originblacklist/base/util/OPlayer.java
new file mode 100644
index 0000000..f82c1c6
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/util/OPlayer.java
@@ -0,0 +1,100 @@
+package xyz.webmc.originblacklist.base.util;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.UUID;
+
+import net.lax1dude.eaglercraft.backend.server.api.EnumWebSocketHeader;
+import net.lax1dude.eaglercraft.backend.server.api.IEaglerConnection;
+import net.lax1dude.eaglercraft.backend.server.api.IEaglerLoginConnection;
+
+public final class OPlayer {
+ private final String origin;
+ private final String addr;
+ private final String name;
+ private final UUID uuid;
+ private final String brand;
+
+ public OPlayer(final IEaglerConnection conn, final String name, final UUID uuid, final String addr,
+ final String brand) {
+ this.name = name;
+ this.uuid = uuid;
+ if (conn != null) {
+ this.origin = conn.getWebSocketHeader(EnumWebSocketHeader.HEADER_ORIGIN);
+ this.addr = formatSocketAddress(conn.getSocketAddress());
+ if (conn instanceof IEaglerLoginConnection) {
+ this.brand = ((IEaglerLoginConnection) conn).getEaglerBrandString();
+ } else {
+ this.brand = OriginBlacklist.UNKNOWN_STR;
+ }
+ } else {
+ this.origin = OriginBlacklist.UNKNOWN_STR;
+ this.addr = formatIPAddress(addr);
+ this.brand = brand;
+ }
+ }
+
+ public OPlayer(final IEaglerConnection conn, final String name, final UUID uuid) {
+ this(conn, name, uuid, null, null);
+ }
+
+ public final String getOrigin() {
+ return this.origin;
+ }
+
+ public final String getAddr() {
+ return this.addr;
+ }
+
+ public final String getName() {
+ return this.name;
+ }
+
+ public final UUID getUUID() {
+ return this.uuid;
+ }
+
+ public final String getBrand() {
+ return this.brand;
+ }
+
+ private static final String formatIPAddress(String addr) {
+ if (addr.startsWith("/")) {
+ addr = addr.substring(1);
+ }
+
+ int i = addr.lastIndexOf('/');
+ if (i != -1) {
+ addr = addr.substring(i + 1);
+ }
+
+ if (addr.startsWith("[")) {
+ i = addr.indexOf(']');
+ if (i != -1)
+ return addr.substring(1, i);
+ return addr.substring(1);
+ }
+
+ i = addr.lastIndexOf(':');
+ if (i != -1) {
+ addr = addr.substring(0, i);
+ }
+
+ return addr;
+ }
+
+ private static final String formatSocketAddress(final SocketAddress saddr) {
+ if (saddr instanceof InetSocketAddress) {
+ final InetSocketAddress isa = (InetSocketAddress) saddr;
+ if (isa.getAddress() != null) {
+ return isa.getAddress().getHostAddress();
+ } else {
+ return isa.getHostString();
+ }
+ } else {
+ return formatIPAddress(saddr.toString());
+ }
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/base/util/UpdateChecker.java b/src/main/java/xyz/webmc/originblacklist/base/util/UpdateChecker.java
new file mode 100644
index 0000000..f11ad5f
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/base/util/UpdateChecker.java
@@ -0,0 +1,47 @@
+package xyz.webmc.originblacklist.base.util;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.semver4j.Semver;
+
+import de.marhali.json5.Json5;
+import de.marhali.json5.Json5Array;
+import de.marhali.json5.Json5Element;
+import de.marhali.json5.Json5Object;
+
+public class UpdateChecker {
+ private static final Json5 json5 = Json5.builder(builder -> builder.build());
+
+ public static final boolean checkForUpdate(final String repo, final Semver currentVersion, final boolean allowPreRelease) {
+ try {
+ final URL url = new URL("https://api.github.com/repos/" + repo + "/releases");
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setConnectTimeout(5000);
+ conn.setReadTimeout(5000);
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ Json5Element element = json5.parse(reader);
+ if (element instanceof Json5Array) {
+ final Json5Array arr = element.getAsJson5Array();
+ if (arr.size() > 0) {
+ element = arr.get(0);
+ if (element instanceof Json5Object) {
+ final Json5Object obj = element.getAsJson5Object();
+ final String tag = obj.get("tag_name").getAsString();
+ final Semver ver = new Semver(tag.substring(1));
+ if (ver.isGreaterThan(currentVersion)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ } catch (final Throwable t) {
+ t.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bukkit/OriginBlacklistBukkit.java b/src/main/java/xyz/webmc/originblacklist/bukkit/OriginBlacklistBukkit.java
new file mode 100644
index 0000000..855b1f9
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bukkit/OriginBlacklistBukkit.java
@@ -0,0 +1,235 @@
+package xyz.webmc.originblacklist.bukkit;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.enums.EnumLogLevel;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistLoginEvent;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistMOTDEvent;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+import xyz.webmc.originblacklist.base.util.IOriginBlacklistPlugin;
+import xyz.webmc.originblacklist.base.util.IncompatibleDependencyException;
+import xyz.webmc.originblacklist.bukkit.command.OriginBlacklistCommandBukkit;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import org.semver4j.Semver;
+import org.bstats.bukkit.Metrics;
+import org.bstats.charts.AdvancedPie;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.server.ServerListPingEvent;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.util.CachedServerIcon;
+
+import net.kyori.adventure.text.Component;
+import net.lax1dude.eaglercraft.backend.server.api.IEaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.bukkit.EaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.bukkit.event.EaglercraftLoginEvent;
+import net.lax1dude.eaglercraft.backend.server.api.bukkit.event.EaglercraftMOTDEvent;
+
+public final class OriginBlacklistBukkit extends JavaPlugin implements Listener, IOriginBlacklistPlugin {
+ private boolean papiPlaceholdersEnabled;
+ private Object papi;
+ private OriginBlacklist blacklist;
+ private IEaglerXServerAPI eaglerAPI;
+ private Metrics metrics;
+
+ private CachedServerIcon iconCache;
+
+ @Override
+ public final void onEnable() {
+ final Plugin eagx = this.getServer().getPluginManager().getPlugin("EaglercraftXServer");
+ if (eagx == null) {
+ throw new IncompatibleDependencyException("EaglercraftXServer");
+ } else {
+ final Semver version = new Semver(eagx.getDescription().getVersion());
+ if (version.isLowerThan(OriginBlacklist.REQUIRED_API_VER)) {
+ throw new IncompatibleDependencyException("EaglerXServer", OriginBlacklist.REQUIRED_API_VER, version);
+ }
+ }
+ this.papiPlaceholdersEnabled = this.getServer().getPluginManager().getPlugin("PlaceholderAPI") != null;
+ if (this.papiPlaceholdersEnabled) {
+ try {
+ this.papi = Class.forName("me.clip.placeholderapi.PlaceholderAPI");
+ } catch (final Throwable t) {
+ this.papi = null;
+ this.papiPlaceholdersEnabled = false;
+ }
+ } else {
+ this.papi = null;
+ }
+ this.blacklist = new OriginBlacklist(this);
+ this.eaglerAPI = EaglerXServerAPI.instance();
+ this.getCommand("originblacklist").setExecutor(new OriginBlacklistCommandBukkit(this.blacklist));
+ this.getServer().getPluginManager().registerEvents(this, this);
+ this.log(EnumLogLevel.INFO, "Initialized Plugin");
+ if (this.blacklist.isMetricsEnabled()) {
+ this.metrics = new Metrics(this, OriginBlacklist.BSTATS_ID);
+ this.metrics.addCustomChart(new AdvancedPie("player_types", () -> {
+ final Map playerMap = new HashMap<>();
+
+ for (final Player player : Bukkit.getOnlinePlayers()) {
+ final boolean eagler = eaglerAPI.isEaglerPlayerByUUID(player.getUniqueId());
+ final String key = eagler ? "Eagler" : "Java";
+ playerMap.put(key, playerMap.getOrDefault(key, 0) + 1);
+ }
+
+ return playerMap;
+ }));
+ }
+ }
+
+ @EventHandler(priority = EventPriority.NORMAL)
+ public final void onEaglerLogin(final EaglercraftLoginEvent event) {
+ final OPlayer player = new OPlayer(event.getLoginConnection(), event.getProfileUsername(), event.getProfileUUID());
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public final void onEaglerMOTD(final EaglercraftMOTDEvent event) {
+ final OPlayer player = new OPlayer(event.getMOTDConnection(), null, null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public final void onJavaLogin(final AsyncPlayerPreLoginEvent event) {
+ final OPlayer player = new OPlayer(null, event.getName(), event.getUniqueId(),
+ event.getAddress() != null ? event.getAddress().toString() : null, null);
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public final void onJavaMOTD(final ServerListPingEvent event) {
+ final OPlayer player = new OPlayer(null, null, null,
+ event.getAddress() != null ? event.getAddress().toString() : null, null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @Override
+ public final String getPluginId() {
+ return this.getDescription().getName();
+ }
+
+ @Override
+ public final Semver getPluginVersion() {
+ return new Semver(this.getDescription().getVersion());
+ }
+
+ @Override
+ public final void log(final EnumLogLevel level, final String txt) {
+ if (level == EnumLogLevel.WARN) {
+ this.getLogger().warning(txt);
+ } else if (level == EnumLogLevel.ERROR) {
+ this.getLogger().severe(txt);
+ } else if (level == EnumLogLevel.DEBUG) {
+ if (this.blacklist != null && this.blacklist.isDebugEnabled()) {
+ this.getLogger().info(txt);
+ }
+ } else {
+ this.getLogger().info(txt);
+ }
+ }
+
+ @Override
+ public final void kickPlayer(final Component comp, final OriginBlacklistLoginEvent event) {
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ event.getEaglerEvent().setKickMessage(OriginBlacklist.getComponentString(comp));
+ } else {
+ final Object javaEvent = event.getJavaEvent();
+ final String msg = OriginBlacklist.getComponentString(comp);
+ if (javaEvent instanceof AsyncPlayerPreLoginEvent pre) {
+ pre.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, msg);
+ } else if (javaEvent instanceof PlayerJoinEvent join) {
+ join.getPlayer().kickPlayer(msg);
+ }
+ }
+ }
+
+ @Override
+ public final void setMOTD(final Component comp, final OriginBlacklistMOTDEvent event) {
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ this.blacklist.setEaglerMOTD(comp, event);
+ } else {
+ final ServerListPingEvent javaEvent = (ServerListPingEvent) event.getJavaEvent();
+ javaEvent.setMotd(OriginBlacklist.getComponentString(comp));
+ javaEvent.setMaxPlayers(0);
+ final CachedServerIcon icon = this.loadIcon();
+ if (icon != null) {
+ try {
+ javaEvent.setServerIcon(icon);
+ } catch (final Throwable t) {
+ }
+ }
+ }
+ }
+
+ private final CachedServerIcon loadIcon() {
+ if (this.iconCache != null)
+ return this.iconCache;
+ final String uri = this.blacklist.getConfig().getIconBase64URI();
+ if (uri == null || uri.isEmpty())
+ return null;
+ try {
+ String b64 = uri;
+ final int i = b64.indexOf("base64,");
+ if (i != -1)
+ b64 = b64.substring(i + "base64,".length());
+ final byte[] png = Base64.getDecoder().decode(b64);
+ final BufferedImage img = javax.imageio.ImageIO.read(new ByteArrayInputStream(png));
+ if (img != null) {
+ try {
+ this.iconCache = Bukkit.loadServerIcon(img);
+ } catch (final Throwable t) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+
+ return this.iconCache;
+ } catch (final Throwable t) {
+ return null;
+ }
+ }
+
+ @Override
+ public final String parsePlaceholders(final OPlayer player, final String txt) {
+ if (this.papiPlaceholdersEnabled) {
+ try {
+ final UUID uuid = player.getUUID();
+ final Player bp = uuid != null ? (Player) Bukkit.getPlayer(uuid) : null;
+ if (bp != null) {
+ return (String) ((Class>) this.papi)
+ .getMethod("setPlaceholders", org.bukkit.entity.Player.class, String.class).invoke(null, bp, txt);
+ }
+ } catch (final Throwable t) {
+ }
+ }
+ return txt;
+ }
+
+ @Override
+ public final void scheduleRepeat(final Runnable task, final int period, final TimeUnit unit) {
+ long ms = unit.toMillis((long) period);
+ long ticks = Math.max(1L, ms / 50L);
+ Bukkit.getScheduler().runTaskTimer(this, task, ticks, ticks);
+ }
+
+ @Override
+ public final void shutdown() {
+ this.metrics.shutdown();
+ Bukkit.getScheduler().cancelTasks(this);
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bukkit/command/BKTCommandContext.java b/src/main/java/xyz/webmc/originblacklist/bukkit/command/BKTCommandContext.java
new file mode 100644
index 0000000..e265898
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bukkit/command/BKTCommandContext.java
@@ -0,0 +1,36 @@
+package xyz.webmc.originblacklist.bukkit.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.command.CommandContext;
+
+import org.bukkit.command.CommandSender;
+
+public class BKTCommandContext implements CommandContext {
+ private final CommandSender sender;
+ private final String[] args;
+
+ public BKTCommandContext(final CommandSender sender, final String[] args) {
+ this.sender = sender;
+ this.args = args;
+ }
+
+ @Override
+ public String getPlayerName() {
+ return this.sender.getName();
+ }
+
+ @Override
+ public void reply(final String message) {
+ this.sender.sendMessage(OriginBlacklist.getLegacyFromMiniMessage(message));
+ }
+
+ @Override
+ public boolean hasPermission(final String permission) {
+ return this.sender.hasPermission(permission);
+ }
+
+ @Override
+ public String[] getArgs() {
+ return this.args;
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bukkit/command/OriginBlacklistCommandBukkit.java b/src/main/java/xyz/webmc/originblacklist/bukkit/command/OriginBlacklistCommandBukkit.java
new file mode 100644
index 0000000..7077c53
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bukkit/command/OriginBlacklistCommandBukkit.java
@@ -0,0 +1,26 @@
+package xyz.webmc.originblacklist.bukkit.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.command.OriginBlacklistCommand;
+
+import java.util.List;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+
+public class OriginBlacklistCommandBukkit extends OriginBlacklistCommand implements TabExecutor {
+ public OriginBlacklistCommandBukkit(OriginBlacklist plugin) {
+ super(plugin);
+ }
+
+ @Override
+ public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) {
+ return super.execute(new BKTCommandContext(sender, args));
+ }
+
+ @Override
+ public List onTabComplete(final CommandSender sender, final Command command, final String label, final String[] args) {
+ return super.suggest(new BKTCommandContext(sender, args));
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bungee/OriginBlacklistBungee.java b/src/main/java/xyz/webmc/originblacklist/bungee/OriginBlacklistBungee.java
new file mode 100644
index 0000000..852dca4
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bungee/OriginBlacklistBungee.java
@@ -0,0 +1,200 @@
+package xyz.webmc.originblacklist.bungee;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.enums.EnumLogLevel;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistLoginEvent;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistMOTDEvent;
+import xyz.webmc.originblacklist.base.util.IOriginBlacklistPlugin;
+import xyz.webmc.originblacklist.base.util.IncompatibleDependencyException;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+import xyz.webmc.originblacklist.bungee.command.OriginBlacklistCommandBungee;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.bstats.bungeecord.Metrics;
+import org.bstats.charts.AdvancedPie;
+import org.semver4j.Semver;
+
+import net.kyori.adventure.text.Component;
+import net.lax1dude.eaglercraft.backend.server.api.IEaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.bungee.EaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.bungee.event.EaglercraftLoginEvent;
+import net.lax1dude.eaglercraft.backend.server.api.bungee.event.EaglercraftMOTDEvent;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.ProxyPingEvent;
+import net.md_5.bungee.api.event.PostLoginEvent;
+import net.md_5.bungee.api.event.PreLoginEvent;
+import net.md_5.bungee.api.plugin.Listener;
+import net.md_5.bungee.api.plugin.Plugin;
+import net.md_5.bungee.event.EventHandler;
+import net.md_5.bungee.event.EventPriority;
+
+@SuppressWarnings({ "deprecation" })
+public final class OriginBlacklistBungee extends Plugin implements Listener, IOriginBlacklistPlugin {
+ private ProxyServer proxy;
+ private boolean papiPlaceholdersEnabled;
+ private Object papi;
+ private OriginBlacklist blacklist;
+ private IEaglerXServerAPI eaglerAPI;
+ private Metrics metrics;
+
+ @Override
+ public final void onEnable() {
+ this.proxy = ProxyServer.getInstance();
+ final Plugin eagx = this.getProxy().getPluginManager().getPlugin("EaglercraftXServer");
+ if (eagx == null) {
+ throw new IncompatibleDependencyException("EaglercraftXServer");
+ } else {
+ final Semver version = new Semver(eagx.getDescription().getVersion());
+ if (version.isLowerThan(OriginBlacklist.REQUIRED_API_VER)) {
+ throw new IncompatibleDependencyException("EaglerXServer", OriginBlacklist.REQUIRED_API_VER, version);
+ }
+ }
+ this.papiPlaceholdersEnabled = this.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") != null;
+ if (this.papiPlaceholdersEnabled) {
+ try {
+ this.papi = Class.forName("net.william278.papiproxybridge.api.PlaceholderAPI").getMethod("createInstance")
+ .invoke(null);
+ } catch (final Throwable t) {
+ this.papi = null;
+ this.papiPlaceholdersEnabled = false;
+ }
+ } else {
+ this.papi = null;
+ }
+ this.blacklist = new OriginBlacklist(this);
+ this.eaglerAPI = EaglerXServerAPI.instance();
+ this.getProxy().getPluginManager().registerCommand(this, new OriginBlacklistCommandBungee(this, this.blacklist, "originblacklist"));
+ this.getProxy().getPluginManager().registerListener(this, this);
+ this.log(EnumLogLevel.INFO, "Initialized Plugin");
+ if (this.blacklist.isMetricsEnabled()) {
+ this.metrics = new Metrics(this, OriginBlacklist.BSTATS_ID);
+ this.metrics.addCustomChart(new AdvancedPie("player_types", () -> {
+ final Map playerMap = new HashMap<>();
+
+ for (final ProxiedPlayer player : this.proxy.getPlayers()) {
+ final boolean eagler = eaglerAPI.isEaglerPlayerByUUID(player.getUniqueId());
+ final String key = eagler ? "Eagler" : "Java";
+ playerMap.put(key, playerMap.getOrDefault(key, 0) + 1);
+ }
+
+ return playerMap;
+ }));
+ }
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public final void onEaglerLogin(final EaglercraftLoginEvent event) {
+ final OPlayer player = new OPlayer(event.getLoginConnection(), event.getProfileUsername(), event.getProfileUUID());
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public final void onEaglerMOTD(final EaglercraftMOTDEvent event) {
+ final OPlayer player = new OPlayer(event.getMOTDConnection(), null, null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public final void onJavaLogin(final PostLoginEvent event) {
+ final ProxiedPlayer aPlayer = event.getPlayer();
+ final OPlayer bPlayer = new OPlayer(null, aPlayer.getName(), aPlayer.getUniqueId(),
+ aPlayer.getAddress().toString(), aPlayer.getClientBrand());
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(null, event, EnumConnectionType.JAVA, bPlayer));
+ }
+
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public final void onJavaHandshake(final PreLoginEvent event) {
+ final OPlayer player = new OPlayer(null, null, null, event.getConnection().getAddress().toString(), null);
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public final void onJavaMOTD(final ProxyPingEvent event) {
+ final OPlayer player = new OPlayer(null, null, null, event.getConnection().getAddress().toString(), null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @Override
+ public final String getPluginId() {
+ return this.getDescription().getName();
+ }
+
+ @Override
+ public final Semver getPluginVersion() {
+ return new Semver(this.getDescription().getVersion());
+ }
+
+ @Override
+ public final void log(final EnumLogLevel level, final String txt) {
+ if (level == EnumLogLevel.WARN) {
+ this.getLogger().warning(txt);
+ } else if (level == EnumLogLevel.ERROR) {
+ this.getLogger().severe(txt);
+ } else if (level == EnumLogLevel.DEBUG) {
+ if (this.blacklist != null && this.blacklist.isDebugEnabled()) {
+ this.getLogger().info(txt);
+ }
+ } else {
+ this.getLogger().info(txt);
+ }
+ }
+
+ @Override
+ public final void kickPlayer(final Component comp, final OriginBlacklistLoginEvent event) {
+ final String str = OriginBlacklist.getComponentString(comp);
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ event.getEaglerEvent().setKickMessage(str);
+ } else {
+ final Object javaEvent = event.getJavaEvent();
+ if (javaEvent instanceof PreLoginEvent preLoginEvent) {
+ preLoginEvent.getConnection().disconnect(str);
+ } else if (javaEvent instanceof PostLoginEvent postLoginEvent) {
+ postLoginEvent.getPlayer().disconnect(str);
+ }
+ }
+ }
+
+ @Override
+ public final void setMOTD(final Component comp, final OriginBlacklistMOTDEvent event) {
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ this.blacklist.setEaglerMOTD(comp, event);
+ } else {
+ final ProxyPingEvent javaEvent = (ProxyPingEvent) event.getJavaEvent();
+ final ServerPing ping = javaEvent.getResponse();
+ ping.setDescription(OriginBlacklist.getComponentString(comp));
+ ping.setFavicon(this.blacklist.getConfig().getIconBase64URI());
+ ping.getPlayers().setOnline(0);
+ ping.getPlayers().setMax(0);
+ javaEvent.setResponse(ping);
+ }
+ }
+
+ @Override
+ public final String parsePlaceholders(final OPlayer player, final String txt) {
+ if (this.papiPlaceholdersEnabled && this.papi != null) {
+ try {
+ return (String) this.papi.getClass().getMethod("formatPlaceholders", String.class, java.util.UUID.class)
+ .invoke(this.papi, txt, player.getUUID());
+ } catch (final Throwable t) {
+ }
+ }
+ return txt;
+ }
+
+ @Override
+ public final void scheduleRepeat(final Runnable task, final int period, final TimeUnit unit) {
+ this.proxy.getScheduler().schedule(this, task, period, period, unit);
+ }
+
+ @Override
+ public final void shutdown() {
+ this.metrics.shutdown();
+ this.proxy.getScheduler().cancel(this);
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bungee/command/BNGCommandContext.java b/src/main/java/xyz/webmc/originblacklist/bungee/command/BNGCommandContext.java
new file mode 100644
index 0000000..52422a3
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bungee/command/BNGCommandContext.java
@@ -0,0 +1,37 @@
+package xyz.webmc.originblacklist.bungee.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.command.CommandContext;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class BNGCommandContext implements CommandContext {
+ private final CommandSender sender;
+ private final String[] args;
+
+ public BNGCommandContext(final CommandSender sender, final String[] args) {
+ this.sender = sender;
+ this.args = args;
+ }
+
+ @Override
+ public String getPlayerName() {
+ return this.sender.getName();
+ }
+
+ @Override
+ public void reply(final String message) {
+ this.sender.sendMessage(TextComponent.fromLegacy(OriginBlacklist.getLegacyFromMiniMessage(message)));
+ }
+
+ @Override
+ public boolean hasPermission(final String permission) {
+ return this.sender.hasPermission(permission);
+ }
+
+ @Override
+ public String[] getArgs() {
+ return this.args;
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/bungee/command/OriginBlacklistCommandBungee.java b/src/main/java/xyz/webmc/originblacklist/bungee/command/OriginBlacklistCommandBungee.java
new file mode 100644
index 0000000..40ec09a
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/bungee/command/OriginBlacklistCommandBungee.java
@@ -0,0 +1,30 @@
+package xyz.webmc.originblacklist.bungee.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.command.OriginBlacklistCommand;
+import xyz.webmc.originblacklist.bungee.OriginBlacklistBungee;
+
+import java.util.List;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.api.plugin.TabExecutor;
+
+public class OriginBlacklistCommandBungee extends Command implements TabExecutor {
+ private final OriginBlacklistCommand cmd;
+
+ public OriginBlacklistCommandBungee(final OriginBlacklistBungee plugin, final OriginBlacklist blacklist, final String command) {
+ super(command);
+ this.cmd = new OriginBlacklistCommand(blacklist);
+ }
+
+ @Override
+ public void execute(final CommandSender sender, final String[] args) {
+ this.cmd.execute(new BNGCommandContext(sender, args));
+ }
+
+ @Override
+ public List onTabComplete(final CommandSender sender, final String[] args) {
+ return this.cmd.suggest(new BNGCommandContext(sender, args));
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/velocity/OriginBlacklistVelocity.java b/src/main/java/xyz/webmc/originblacklist/velocity/OriginBlacklistVelocity.java
new file mode 100644
index 0000000..3ee40dc
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/velocity/OriginBlacklistVelocity.java
@@ -0,0 +1,226 @@
+package xyz.webmc.originblacklist.velocity;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.enums.EnumConnectionType;
+import xyz.webmc.originblacklist.base.enums.EnumLogLevel;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistLoginEvent;
+import xyz.webmc.originblacklist.base.events.OriginBlacklistMOTDEvent;
+import xyz.webmc.originblacklist.base.util.IOriginBlacklistPlugin;
+import xyz.webmc.originblacklist.base.util.IncompatibleDependencyException;
+import xyz.webmc.originblacklist.base.util.OPlayer;
+import xyz.webmc.originblacklist.velocity.command.OriginBlacklistCommandVelocity;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.bstats.charts.AdvancedPie;
+import org.bstats.velocity.Metrics;
+import org.bstats.velocity.Metrics.Factory;
+import org.semver4j.Semver;
+import org.slf4j.Logger;
+
+import com.google.inject.Inject;
+import com.velocitypowered.api.event.PostOrder;
+import com.velocitypowered.api.event.Subscribe;
+import com.velocitypowered.api.event.connection.PreLoginEvent;
+import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
+import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
+import com.velocitypowered.api.event.proxy.ProxyPingEvent;
+import com.velocitypowered.api.plugin.PluginContainer;
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.ProxyServer;
+import com.velocitypowered.api.proxy.server.ServerPing;
+import com.velocitypowered.api.scheduler.ScheduledTask;
+import com.velocitypowered.api.util.Favicon;
+
+import net.kyori.adventure.text.Component;
+import net.lax1dude.eaglercraft.backend.server.api.IEaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.velocity.event.EaglercraftMOTDEvent;
+import net.lax1dude.eaglercraft.backend.server.api.velocity.EaglerXServerAPI;
+import net.lax1dude.eaglercraft.backend.server.api.velocity.event.EaglercraftLoginEvent;
+
+@SuppressWarnings({ "deprecation", "unchecked" })
+public final class OriginBlacklistVelocity implements IOriginBlacklistPlugin {
+ private final PluginContainer plugin;
+ private final Factory metricsFactory;
+ private final ProxyServer proxy;
+ private final Logger logger;
+
+ private boolean papiPlaceholdersEnabled;
+ private Object papi;
+ private OriginBlacklist blacklist;
+ private IEaglerXServerAPI eaglerAPI;
+ private Metrics metrics;
+
+ @Inject
+ public OriginBlacklistVelocity(final PluginContainer plugin, Factory metricsFactory, final ProxyServer proxy,
+ final Logger logger) {
+ this.plugin = plugin;
+ this.metricsFactory = metricsFactory;
+ this.proxy = proxy;
+ this.logger = logger;
+ }
+
+ @Subscribe
+ public void onProxyInitialization(ProxyInitializeEvent event) {
+ this.proxy.getPluginManager().getPlugin("eaglerxserver").ifPresentOrElse(plugin -> {
+ final Semver version = new Semver(plugin.getDescription().getVersion().orElse("1.0.0"));
+ if (version.isLowerThan(OriginBlacklist.REQUIRED_API_VER)) {
+ throw new IncompatibleDependencyException("EaglerXServer", OriginBlacklist.REQUIRED_API_VER, version);
+ }
+ }, () -> {
+ throw new IncompatibleDependencyException("EaglerXServer");
+ });
+ this.papiPlaceholdersEnabled = this.proxy.getPluginManager().getPlugin("papiproxybridge").isPresent();
+ if (this.papiPlaceholdersEnabled) {
+ try {
+ this.papi = Class.forName("net.william278.papiproxybridge.api.PlaceholderAPI").getMethod("createInstance")
+ .invoke(null);
+ } catch (final Throwable t) {
+ this.papi = null;
+ this.papiPlaceholdersEnabled = false;
+ }
+ } else {
+ this.papi = null;
+ }
+ this.blacklist = new OriginBlacklist(this);
+ this.eaglerAPI = EaglerXServerAPI.instance();
+ this.proxy.getCommandManager().register("originblacklist", new OriginBlacklistCommandVelocity(this.blacklist));
+ this.log(EnumLogLevel.INFO, "Initialized Plugin");
+ if (this.blacklist.isMetricsEnabled()) {
+ this.metrics = this.metricsFactory.make(this, OriginBlacklist.BSTATS_ID);
+ this.metrics.addCustomChart(new AdvancedPie("player_types", () -> {
+ final Map playerMap = new HashMap<>();
+
+ for (final Player player : this.proxy.getAllPlayers()) {
+ final boolean eagler = eaglerAPI.isEaglerPlayerByUUID(player.getUniqueId());
+ final String key = eagler ? "Eagler" : "Java";
+ playerMap.put(key, playerMap.getOrDefault(key, 0) + 1);
+ }
+
+ return playerMap;
+ }));
+ }
+ }
+
+ @Subscribe(order = PostOrder.FIRST)
+ public final void onEaglerLogin(final EaglercraftLoginEvent event) {
+ final OPlayer player = new OPlayer(event.getLoginConnection(), event.getProfileUsername(), event.getProfileUUID());
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @Subscribe(order = PostOrder.LAST)
+ public final void onEaglerMOTD(final EaglercraftMOTDEvent event) {
+ final OPlayer player = new OPlayer(event.getMOTDConnection(), null, null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(event, null, EnumConnectionType.EAGLER, player));
+ }
+
+ @Subscribe(order = PostOrder.FIRST)
+ public final void onJavaLogin(final PreLoginEvent event) {
+ final OPlayer player = new OPlayer(null, event.getUsername(), event.getUniqueId(),
+ event.getConnection().getRemoteAddress().toString(), null);
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @Subscribe(order = PostOrder.FIRST)
+ public final void onJavaHandshake(final PlayerClientBrandEvent event) {
+ final Player aPlayer = (Player) event.getPlayer();
+ final OPlayer bPlayer = new OPlayer(null, aPlayer.getUsername(), aPlayer.getUniqueId(),
+ aPlayer.getRemoteAddress().getAddress().toString(), event.getBrand());
+ this.blacklist.handleLogin(new OriginBlacklistLoginEvent(null, event, EnumConnectionType.JAVA, bPlayer));
+ }
+
+ @Subscribe(order = PostOrder.LAST)
+ public final void onJavaMOTD(final ProxyPingEvent event) {
+ final OPlayer player = new OPlayer(null, null, null, event.getConnection().getRemoteAddress().getHostString(),
+ null);
+ this.blacklist.handleMOTD(new OriginBlacklistMOTDEvent(null, event, EnumConnectionType.JAVA, player));
+ }
+
+ @Override
+ public final String getPluginId() {
+ return this.plugin.getDescription().getId();
+ }
+
+ @Override
+ public final Semver getPluginVersion() {
+ return new Semver(this.plugin.getDescription().getVersion().get());
+ }
+
+ @Override
+ public final void log(final EnumLogLevel level, final String txt) {
+ if (level == EnumLogLevel.WARN) {
+ this.logger.warn(txt);
+ } else if (level == EnumLogLevel.ERROR) {
+ this.logger.error(txt);
+ } else if (level == EnumLogLevel.DEBUG) {
+ if (this.blacklist.isDebugEnabled()) {
+ this.logger.debug(txt);
+ }
+ } else {
+ this.logger.info(txt);
+ }
+ }
+
+ @Override
+ public final void kickPlayer(final Component comp, final OriginBlacklistLoginEvent event) {
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ event.getEaglerEvent().setKickMessage(comp);
+ } else {
+ final Object javaEvent = event.getJavaEvent();
+ if (javaEvent instanceof PreLoginEvent loginEvent) {
+ loginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied(comp));
+ } else if (javaEvent instanceof PlayerClientBrandEvent brandEvent) {
+ brandEvent.getPlayer().disconnect(comp);
+ }
+ }
+ }
+
+ @Override
+ public final void setMOTD(final Component comp, final OriginBlacklistMOTDEvent event) {
+ if (event.getConnectionType() == EnumConnectionType.EAGLER) {
+ blacklist.setEaglerMOTD(comp, event);
+ } else {
+ final ProxyPingEvent javaEvent = (ProxyPingEvent) event.getJavaEvent();
+ ServerPing ping = ServerPing.builder()
+ .description(comp)
+ .version(new ServerPing.Version(0, ""))
+ .samplePlayers(List.of())
+ .onlinePlayers(0)
+ .maximumPlayers(0)
+ .favicon(new Favicon(this.blacklist.getConfig().getIconBase64URI()))
+ .build();
+ javaEvent.setPing(ping);
+ }
+ }
+
+ @Override
+ public final String parsePlaceholders(final OPlayer player, final String txt) {
+ if (this.papiPlaceholdersEnabled && this.papi != null) {
+ try {
+ return (String) this.papi.getClass().getMethod("formatPlaceholders", String.class, java.util.UUID.class)
+ .invoke(this.papi, txt, player.getUUID());
+ } catch (final Throwable t) {
+ }
+ }
+ return txt;
+ }
+
+ @Override
+ public final void scheduleRepeat(final Runnable task, final int period, final TimeUnit unit) {
+ this.proxy.getScheduler()
+ .buildTask(this, task)
+ .repeat(period, unit)
+ .schedule();
+ }
+
+ @Override
+ public final void shutdown() {
+ this.metrics.shutdown();
+ for (ScheduledTask task : this.proxy.getScheduler().tasksByPlugin(this.plugin)) {
+ task.cancel();
+ }
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/velocity/command/OriginBlacklistCommandVelocity.java b/src/main/java/xyz/webmc/originblacklist/velocity/command/OriginBlacklistCommandVelocity.java
new file mode 100644
index 0000000..409bb2c
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/velocity/command/OriginBlacklistCommandVelocity.java
@@ -0,0 +1,24 @@
+package xyz.webmc.originblacklist.velocity.command;
+
+import xyz.webmc.originblacklist.base.OriginBlacklist;
+import xyz.webmc.originblacklist.base.command.OriginBlacklistCommand;
+
+import java.util.List;
+
+import com.velocitypowered.api.command.SimpleCommand;
+
+public class OriginBlacklistCommandVelocity extends OriginBlacklistCommand implements SimpleCommand {
+ public OriginBlacklistCommandVelocity(OriginBlacklist plugin) {
+ super(plugin);
+ }
+
+ @Override
+ public void execute(final Invocation invocation) {
+ super.execute(new VCommandContext(invocation));
+ }
+
+ @Override
+ public List suggest(final Invocation invocation) {
+ return super.suggest(new VCommandContext(invocation));
+ }
+}
diff --git a/src/main/java/xyz/webmc/originblacklist/velocity/command/VCommandContext.java b/src/main/java/xyz/webmc/originblacklist/velocity/command/VCommandContext.java
new file mode 100644
index 0000000..1b130a2
--- /dev/null
+++ b/src/main/java/xyz/webmc/originblacklist/velocity/command/VCommandContext.java
@@ -0,0 +1,35 @@
+package xyz.webmc.originblacklist.velocity.command;
+
+import xyz.webmc.originblacklist.base.command.CommandContext;
+
+import com.velocitypowered.api.command.SimpleCommand.Invocation;
+
+import net.kyori.adventure.text.minimessage.MiniMessage;
+
+public class VCommandContext implements CommandContext {
+ private final Invocation invocation;
+
+ public VCommandContext(final Invocation invocation) {
+ this.invocation = invocation;
+ }
+
+ @Override
+ public String getPlayerName() {
+ return this.invocation.source().toString();
+ }
+
+ @Override
+ public void reply(final String message) {
+ this.invocation.source().sendMessage(MiniMessage.miniMessage().deserialize(message));
+ }
+
+ @Override
+ public boolean hasPermission(final String permission) {
+ return this.invocation.source().hasPermission(permission);
+ }
+
+ @Override
+ public String[] getArgs() {
+ return this.invocation.arguments();
+ }
+}
diff --git a/src/main/resources/server-blocked.png b/src/main/resources/blacklisted.png
similarity index 100%
rename from src/main/resources/server-blocked.png
rename to src/main/resources/blacklisted.png
diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml
index 7916bf5..bb401b5 100644
--- a/src/main/resources/bungee.yml
+++ b/src/main/resources/bungee.yml
@@ -1,7 +1,10 @@
-name: OriginBlacklist
-version: ${version}
-main: dev.colbster937.originblacklist.bungee.OriginBlacklistBungee
-description: ${description}
-author: Colbster937
-depends:
- - EaglercraftXServer
\ No newline at end of file
+name: ${plugin_name}
+version: ${plugin_vers}
+main: xyz.webmc.${plugin_iden}.bungee.${plugin_name}Bungee
+description: ${plugin_desc}
+website: ${plugin_site}
+author: [${plugin_athr}]
+contributors: [${plugin_ctbr}]
+depends: [${plugin_depb}]
+provides: [${plugin_prov}]
+softdepend: [${plugin_sdpb}]
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
deleted file mode 100644
index 24cded4..0000000
--- a/src/main/resources/config.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-messages:
- # Valid Placeholders:
- # - %blocked% - The player's origin/brand that was blocked
- # - %blocktype% - Shows what the player was blocked for
- # - %easyblocktype% - Shows what the player was blocked for in an eagler-kid readable form
- # - %notallowed1% - Longer "not allowed" message
- # - %notallowed2% - Shorter "not allowed" message
- # - %host% - The IP the player pinged
- # - %help% - The configured help message for the block type
-
- kick: |
- This %easyblocktype% is %notallowed1%!
- » %blocked% «
-
- %help%
-
- Think this is a mistake? Join our discord:
- discord.gg/changethisintheconfig
-
- # Please note that help is only supported in the kick message, not the MOTD
- help:
- generic: "Please switch to a different %easyblocktype%."
- player: "Please change your %easyblocktype%."
- ip: "Please contact staff for assistance."
-
- motd:
- enabled: true
- text: |
- This %easyblocktype% is %notallowed2%!
- » %blocked%
- icon: "blacklisted.png"
-
-# Origin + Brand blacklist supports wildcards
-# Everything should be lowercase
-blacklist:
- origins:
- - "*eagler-clients.vercel.app*"
- - "*eaglerhackedclients.vercel.app*"
- - "*eaglerhacks.github.io*"
- brands:
- - "*dragonx*"
- - "*piclient*"
- players:
- - "Admin"
- ips:
- - "192.0.2.0/24"
-
-discord:
- webhook: ""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-# :>
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index e583b2c..dfdc264 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,9 +1,12 @@
-name: OriginBlacklist
-version: ${version}
-main: dev.colbster937.originblacklist.bukkit.OriginBlacklistBukkit
-description: ${description}
-author: Colbster937
-depend:
- - EaglercraftXServer
+name: ${plugin_name}
+version: ${plugin_vers}
+main: xyz.webmc.${plugin_iden}.bukkit.${plugin_name}Bukkit
+description: ${plugin_desc}
+website: ${plugin_site}
+authors: [${plugin_athr}]
+contributors: [${plugin_ctbr}]
+depend: [${plugin_depa}]
+provides: [${plugin_prov}]
+softdepend: [${plugin_sdpa}]
commands:
originblacklist:
\ No newline at end of file
diff --git a/src/main/resources/velocity-plugin.json b/src/main/resources/velocity-plugin.json
index bf1052d..8dda797 100644
--- a/src/main/resources/velocity-plugin.json
+++ b/src/main/resources/velocity-plugin.json
@@ -1,14 +1,10 @@
{
- "id": "originblacklist",
- "name": "OriginBlacklist",
- "version": "${version}",
- "description": "${description}",
- "main": "dev.colbster937.originblacklist.velocity.OriginBlacklistVelocity",
- "authors": ["Colbster937"],
- "dependencies": [
- {
- "id": "eaglerxserver",
- "optional": false
- }
- ]
-}
+ "id": "${plugin_iden}",
+ "name": "${plugin_name}",
+ "version": "${plugin_vers}",
+ "description": "${plugin_desc}",
+ "website": "${plugin_site}",
+ "main": "xyz.webmc.${plugin_iden}.velocity.${plugin_name}Velocity",
+ "authors": [${plugin_athr}],
+ "dependencies": [${plugin_depc}]
+}
\ No newline at end of file