commit 692bb5e2ef22683ea5820724f588238a8e7268ea Author: Telesphoreo Date: Tue Jun 29 17:52:11 2021 -0500 Initial commit diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml new file mode 100644 index 00000000..6b818a9f --- /dev/null +++ b/.github/workflows/codacy-analysis.yml @@ -0,0 +1,46 @@ +# This workflow checks out code, performs a Codacy security scan +# and integrates the results with the +# GitHub Advanced Security code scanning feature. For more information on +# the Codacy security scan action usage and parameters, see +# https://github.com/codacy/codacy-analysis-cli-action. +# For more information on Codacy Analysis CLI in general, see +# https://github.com/codacy/codacy-analysis-cli. + +name: Codacy Security Scan + +on: + push: + branches: [ development ] + pull_request: + branches: [ development ] + +jobs: + codacy-security-scan: + name: Codacy Security Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout code + uses: actions/checkout@v2 + + # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis + - name: Run Codacy Analysis CLI + uses: codacy/codacy-analysis-cli-action@1.1.0 + with: + # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository + # You can also omit the token and run the tools that support default configurations + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + verbose: true + output: results.sarif + format: sarif + # Adjust severity of non-security issues + gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will handover control about PR rejection to the GitHub side + max-allowed-issues: 2147483647 + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF results file + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..dbeaac5d --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,73 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ development ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ development ] + schedule: + - cron: '20 18 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup Java JDK + uses: actions/setup-java@v1.4.3 + with: + # The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x). Early access versions can be specified in the form of e.g. 14-ea, 14.0.0-ea, or 14.0.0-ea.28 + java-version: 11 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/downstream.yml b/.github/workflows/downstream.yml new file mode 100644 index 00000000..523e8143 --- /dev/null +++ b/.github/workflows/downstream.yml @@ -0,0 +1,43 @@ +# This is a basic workflow to help you get started with Actions + +name: Downstream Branch Updates + +on: + schedule: + - cron: '20 7 * * *' + # scheduled at 07:00 every Monday and Thursday + + workflow_dispatch: # click the button on Github repo! + + +jobs: + sync_with_upstream: + runs-on: ubuntu-latest + name: Sync main with upstream latest + + steps: + # Step 1: run a standard checkout action, provided by github + - name: Checkout main + uses: actions/checkout@v2 + with: + ref: main + # submodules: 'recursive' ### may be needed in your situation + + # Step 2: run this sync action - specify the upstream repo, upstream branch to sync with, and target sync branch + - name: Pull (Fast-Forward) upstream changes + id: sync + uses: aormsby/Fork-Sync-With-Upstream-action@v2.1 + with: + upstream_repository: AtlasMediaGroup/TotalFreedomMod + upstream_branch: main + target_branch: main + git_pull_args: --ff-only # optional arg use, defaults to simple 'pull' + + # Step 3: Display a message if 'sync' step had new commits (simple test) + - name: Check for new commits + if: steps.sync.outputs.has_new_commits + run: echo "There were new commits." + + # Step 4: Print a helpful timestamp for your records (not required, just nice) + - name: Timestamp + run: date diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..59cede28 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,17 @@ +name: Maven-Build + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3e444ba6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# TFM excludes +build.properties +dependency-reduced-pom.xml + +# Netbeans excludes +/nbproject/private +/dist +/build +manifest.mf +# Now defines that Maven CheckStyle is used +# nb-configuration.xml + +# Eclipse excludes +.project +.classpath +/bin +/.settings + +# IntelliJ excludes +*.ipr +*.iws +/.idea/modules.xml +/.idea/discord.xml +/.idea/jarRepositories.xml +/.idea/workspace.xml +/.idea/uiDesigner.xml +/.idea/libraries +/.idea/ +*.iml + +# Maven excludes +/target + +# OS generated files +.DS_Store +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +.idea/inspectionProfiles/Project_Default.xml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..141c7052 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: java +jdk: + - oraclejdk11 + - openjdk11 +notifications: + email: false +addons: + apt: + packages: + - sshpass +script: mvn clean install +after_success: + - ./travis-upload.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..76586aaf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to TotalFreedomMod + +For information about contributing to TotalFreedomMod, please see the [contributing guidelines](https://github.com/TotalFreedom/TotalFreedomMod/wiki/Contributing). diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..5ee5fb7c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,59 @@ +# TotalFreedom General License + +_Version 2.0, 27th February 2015_ + +Copyright (c) 2011 Steven Lawson + +Copyright (c) 2012 Jerom van der Sar + +All rights reserved. + +##### 1. Definitions + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by this document. + +"Licensor" shall mean the copyright holder or entity authorised by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You", "Your" or "Yourself" shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, binary data, generated documentation, and conversions to other media types. + +"Work" and "This Software" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work. + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +"Redistribution" shall mean any partial or complete availability, transfer or publication of the Work from one Legal Entity to another. + +##### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, You are granted a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to prepare Derivative Works of, publicly display, publicly perform, inspect and redistribute the Work and such Derivative Works as long as the following conditions are met: + +1. Redistributions of This Software must solely occur in Source form. Redistribution in Object form is prohibited without prior written permission from the Licensor. + +2. Neither the names of the copyright holders nor the names this software's contributors may be removed from This Software's source code. + +3. Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from This Software without specific prior written permission. + +4. Accreditations referencing This Software's copyright holders or contributors may neither be altered or removed from source code nor withheld from reproduction in Object form whatsoever. + +5. Any conditions specified by this license may be temporarily or permanently waived by any the aforementioned copyright holders. + +6. Redistributions of This Software must retain this License document in its exact form. + +7. Sub licensing of This Software is prohibited without prior written permission from the Licensor. + +##### 3. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +##### 4. Disclaimer of Warranty + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..24e51aa6 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# TotalFreedomMod [![Build Status](https://travis-ci.com/AtlasMediaGroup/TotalFreedomMod.svg?branch=development)](https://travis-ci.com/AtlasMediaGroup/TotalFreedomMod) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/29c0f964da304666bd654bc7b1d556db)](https://www.codacy.com/gh/AtlasMediaGroup/TotalFreedomMod/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AtlasMediaGroup/TotalFreedomMod&utm_campaign=Badge_Grade) + +TotalFreedomMod is a CraftBukkit server plugin designed primarily to support the [Official TotalFreedom Minecraft Server](https://totalfreedom.me/). However, you are more than welcome to adapt the source for your own server. + +This plugin was originally coded by StevenLawson (Madgeek1450), with Jerom van der Sar (Prozza) becoming heavily involved in its development some time later. It consists of over 85 custom coded commands and a large variety of distinguishable features not included in any other plugin. The plugin has since its beginning grown immensely. Together, with the main TotalFreedom server, TotalFreedomMod has a long-standing reputation of effectiveness whilst maintaining a clear feeling of openness towards the administrators and the players themselves. + +### Contributing + +Please see [CONTRIBUTING.md](CONTRIBUTING.md) if you are interested in developing TotalFreedomMod. + +For information on how TotalFreedomMod is licensed, please see [LICENSE.md](LICENSE.md). + +For information on our security policy and reporting an issue, please see [SECURITY.md](SECURITY.md) + +### Compiling + +You need Maven to build. You'd also need to set the JDK version to Java 8 as that is the current standard as of now. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..adc91e1d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,38 @@ +# Security Policy + +## Supported Versions + +We currently support the code running on the "main" branch and "development" branch of this repository. This is supported in addition to those formal releases, but note anything not yet released should be treated as in-development. + +In terms of plugin releases, our support matrix is as follows: + +### Actively Supported +These versions are currently actively supported by our team, and you should expect security patches where appropriate for these versions. + +| Version | Supported | Support End: | +| ------------------- | ------------------ | ------------------------------ | +| 2021.05 | :white_check_mark: | No Earlier than August 2021 | + +### Legacy Supported +These versions are no longer under active development, however we will look to release critical secuirty patches where appropriate. + +| Version | Supported | Support End: | +| ------------------- | ------------------ | ------------ | +| 2021.04 | :white_check_mark: | July 2021 | + +### No Longer Supported +These versions are no longer supported at all. It is strongly advised to update if you are running any of these versions. + +| Version | Supported | Support Ended: | +| ------------------- | ------------------ | ------------------- | +| 2021.02 | :x: | 6 June 2021 | +| 2020.11 | :x: | 3 May 2021 | +| 6.0.x (Pre-Release) | :x: | December 2020 | +| < 2020.11 | :x: | December 2020 | +| < 5.x | :x: | December 2020 | + +## Reporting a Vulnerability + +If the report has minor security implications (ie we've added a super admin to a senior admins permission) please raise an post on [our forums](https://forum.totalfreedom.me/) in the first instance. If you do not have a forum account and do not wish to sign up, please e-mail us using the e-mail in the next sentence. + +For security vulnerabilities that are more severe and that may pose a more significant threat to the servers running this plugin, please e-mail os-security-reports [ AT ] atlas-media.co.uk - You can expect an automated response immediately to acknowledge receipt of your e-mail, and one of our team will aim to respond within 72 hours and will work with you on the best way to address your concerns. diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 00000000..b060ebbe --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 00000000..65f12570 --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,19 @@ + + + + + + JDK_11 + true + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..403cd1b1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,499 @@ + + + 4.0.0 + + me.totalfreedom + TotalFreedomMod + 6.1.3 + jar + + + UTF-8 + Ember + ${project.name} + ${maven.build.timestamp} + MM/dd/yyyy HH:mm + + + TotalFreedomMod + Server modification for the TotalFreedom server + https://github.com/AtlasMediaGroup/TotalFreedomMod + + + + TotalFreedom General License + https://github.com/TotalFreedom/License/blob/master/LICENSE.md + + + + + TotalFreedom + https://totalfreedom.me + + + + scm:git:git@github.com:TFPatches/TotalFreedomMod.git + scm:git:git@github.com:TFPatches/TotalFreedomMod.git + git@github.com:TFPatches/TotalFreedomMod.git + + + + + apache-snapshots + https://repository.apache.org/content/repositories/snapshots/ + + + + jitpack.io + https://jitpack.io + + + + CodeMC + https://repo.codemc.org/repository/maven-public/ + + + + nms-repo + https://repo.codemc.org/repository/nms/ + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + enginehub + https://maven.enginehub.org/repo/ + + + + elmakers-repo + https://maven.elmakers.com/repository/ + + + + sk89q-snapshots + https://maven.sk89q.com/artifactory/repo + + + + dv8tion + m2-dv8tion + https://m2.dv8tion.net/releases/ + + + + playpro + https://maven.playpro.com/ + + + + md_5-public + https://repo.md-5.net/content/groups/public/ + + + + dmulloy2-repo + https://repo.dmulloy2.net/nexus/repository/public/ + + + + sk89q-repo + https://maven.sk89q.com/repo/ + + + + papermc + https://papermc.io/repo/repository/maven-public/ + + + + ess-repo + https://ci.ender.zone/plugin/repository/everything/ + + + + mattmalec-repo + https://repo.mattmalec.com/repository/releases + + + + + + + commons-io + commons-io + 2.8.0 + compile + + + + org.apache.commons + commons-lang3 + 3.11 + compile + + + + commons-codec + commons-codec + 1.15 + compile + + + + com.github.speedxx + Mojangson + 1957eef8d6 + compile + + + + org.spigotmc + spigot + 1.17-R0.1-SNAPSHOT + provided + + + + com.github.TotalFreedomMC + BukkitTelnet + 541e9fdb84 + provided + + + + com.github.TotalFreedomMC + TF-LibsDisguises + 48f01cf2fe + provided + + + + com.sk89q.worldedit + worldedit-bukkit + 7.3.0-SNAPSHOT + provided + + + + net.ess3 + EssentialsX + 2.18.2 + provided + + + + net.dv8tion + JDA + 4.3.0_277 + provided + + + + net.coreprotect + coreprotect + 19.5 + provided + + + + com.sk89q.worldguard + worldguard-bukkit + 7.0.5 + provided + + + + io.papermc + paperlib + 1.0.6 + compile + + + + com.github.vexsoftware + votifier + v1.9 + provided + + + + net.goldtreeservers + worldguardextraflags + 4.0.0 + provided + + + + org.reflections + reflections + 0.9.12 + compile + + + + org.javassist + javassist + 3.27.0-GA + compile + + + + com.github.AtlasMediaGroup + TFGuilds + master-SNAPSHOT + provided + + + + org.projectlombok + lombok + 1.18.20 + + + + org.jetbrains + annotations + 20.1.0 + compile + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.1 + compile + + + + org.junit.jupiter + junit-jupiter + 5.4.2 + compile + + + + org.eclipse.sisu + org.eclipse.sisu.inject + 0.3.4 + + + + + com.mattmalec + Pterodactyl4J + 2.BETA_47 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0-SNAPSHOT + + + + + + + + src/main/resources + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + TotalFreedomMod.jar + 16 + 16 + 16 + + + + + + pl.project13.maven + git-commit-id-plugin + 4.0.5 + + + get-the-git-infos + + revision + + + + validate-the-git-infos + + validateRevision + + package + + + + ${project.basedir}/.git + git + yyyy-MM-dd HH:mm:ss + false + properties + false + false + + git.commit.id.abbrev + + + false + false + 7 + -dirty + * + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + default-cli + initialize + + + + + + + + + + + + + + + run + + + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + initialize + + read-project-properties + + + + ${project.basedir}/src/main/resources/build.properties + + + + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + + generate-resources + + create + + + + + + maven.buildnumber + ${project.basedir}/src/main/resources/build.properties + + {0,number,#} + + buildNumber + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0-SNAPSHOT + + + package + + shade + + + true + + + io.papermc.lib + me.totalfreedom.totalfreedommod.paperlib + + + + org.bstats + me.totalfreedom.totalfreedommod + + + + + commons-io:commons-io + org.apache.commons:commons-lang3 + commons-codec:commons-codec + org.reflections:reflections + org.javassist:javassist + io.papermc:paperlib + com.github.speedxx:Mojangson + org.jetbrains:annotations + com.mattmalec:Pterodactyl4J + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.1 + + checkstyle.xml + true + true + true + + + + + diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Announcer.java b/src/main/java/me/totalfreedom/totalfreedommod/Announcer.java new file mode 100644 index 00000000..c6a0e470 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Announcer.java @@ -0,0 +1,98 @@ +package me.totalfreedom.totalfreedommod; + +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class Announcer extends FreedomService +{ + + private final List announcements = Lists.newArrayList(); + private boolean enabled; + private long interval; + private String prefix; + private BukkitTask announcer; + + @Override + public void onStart() + { + enabled = ConfigEntry.ANNOUNCER_ENABLED.getBoolean(); + interval = ConfigEntry.ANNOUNCER_INTERVAL.getInteger() * 20L; + prefix = FUtil.colorize(ConfigEntry.ANNOUNCER_PREFIX.getString()); + + announcements.clear(); + for (Object announcement : ConfigEntry.ANNOUNCER_ANNOUNCEMENTS.getList()) + { + announcements.add(FUtil.colorize((String)announcement)); + } + + if (!enabled) + { + return; + } + + announcer = new BukkitRunnable() + { + private int current = 0; + + @Override + public void run() + { + current++; + + if (current >= announcements.size()) + { + current = 0; + } + + announce(announcements.get(current)); + } + }.runTaskTimer(plugin, interval, interval); + } + + @Override + public void onStop() + { + if (announcer == null) + { + return; + } + + FUtil.cancel(announcer); + announcer = null; + } + + public List getAnnouncements() + { + return Collections.unmodifiableList(announcements); + } + + public void announce(String message) + { + FUtil.bcastMsg(prefix + message); + } + + public boolean isEnabled() + { + return enabled; + } + + public long getInterval() + { + return interval; + } + + public String getPrefix() + { + return prefix; + } + + public BukkitTask getAnnouncer() + { + return announcer; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/AntiNuke.java b/src/main/java/me/totalfreedom/totalfreedommod/AntiNuke.java new file mode 100644 index 00000000..ecefb7dd --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/AntiNuke.java @@ -0,0 +1,70 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +public class AntiNuke extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) + { + if (!ConfigEntry.NUKE_MONITOR_ENABLED.getBoolean()) + { + return; + } + + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + + if (fPlayer.incrementAndGetBlockDestroyCount() > ConfigEntry.NUKE_MONITOR_COUNT_BREAK.getInteger()) + { + FUtil.bcastMsg(player.getName() + " is breaking blocks too fast!", ChatColor.RED); + //plugin.ae.autoEject(player, "You are breaking blocks too fast. Nukers are not permitted on this server."); + player.kickPlayer(ChatColor.RED + "You are breaking blocks too fast. Nukers are not permitted on this server."); + + fPlayer.resetBlockDestroyCount(); + + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockPlace(BlockPlaceEvent event) + { + if (!ConfigEntry.NUKE_MONITOR_ENABLED.getBoolean()) + { + return; + } + + Player player = event.getPlayer(); + FPlayer fPlayer = plugin.pl.getPlayer(player); + + if (fPlayer.incrementAndGetBlockPlaceCount() > ConfigEntry.NUKE_MONITOR_COUNT_PLACE.getInteger()) + { + FUtil.bcastMsg(player.getName() + " is placing blocks too fast!", ChatColor.RED); + //plugin.ae.autoEject(player, "You are placing blocks too fast."); + player.kickPlayer(ChatColor.RED + "You are placing blocks too fast."); + + fPlayer.resetBlockPlaceCount(); + + event.setCancelled(true); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/AntiSpam.java b/src/main/java/me/totalfreedom/totalfreedommod/AntiSpam.java new file mode 100644 index 00000000..2e0a2bc0 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/AntiSpam.java @@ -0,0 +1,129 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FSync; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; +import java.util.Map; + +public class AntiSpam extends FreedomService +{ + + public static final int MSG_PER_CYCLE = 8; + public static final int TICKS_PER_CYCLE = 2 * 10; + // + public BukkitTask cycleTask = null; + private Map muteCounts = new HashMap<>(); + + @Override + public void onStart() + { + new BukkitRunnable() + { + + @Override + public void run() + { + cycle(); + } + }.runTaskTimer(plugin, TICKS_PER_CYCLE, TICKS_PER_CYCLE); + } + + @Override + public void onStop() + { + FUtil.cancel(cycleTask); + } + + private void cycle() + { + for (Player player : server.getOnlinePlayers()) + { + final FPlayer playerdata = plugin.pl.getPlayer(player); + + // TODO: Move each to their own section + playerdata.resetMsgCount(); + playerdata.resetBlockDestroyCount(); + playerdata.resetBlockPlaceCount(); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onAsyncPlayerChat(AsyncPlayerChatEvent event) + { + final Player player = event.getPlayer(); + + if (plugin.al.isAdmin(player)) + { + return; + } + + final FPlayer playerdata = plugin.pl.getPlayerSync(player); + int count = muteCounts.getOrDefault(player, 0); + int minutes = ConfigEntry.ANTISPAM_MINUTES.getInteger(); + + // Check for spam + if (playerdata.incrementAndGetMsgCount() > MSG_PER_CYCLE && !playerdata.isMuted()) + { + count++; + muteCounts.put(player, count); + + int time = count * minutes; + playerdata.setMuted(true, time); + + FSync.bcastMsg(String.format("%s has automatically been muted for %d minutes for spamming chat.", + player.getName(), + time), + ChatColor.RED); + + playerdata.resetMsgCount(); + event.setCancelled(true); + } + else if (playerdata.incrementAndGetMsgCount() > MSG_PER_CYCLE / 2) + { + FUtil.playerMsg(player, "Please refrain from spamming chat.", ChatColor.GRAY); + event.setCancelled(true); + } + + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + String command = event.getMessage(); + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + fPlayer.setLastCommand(command); + + if (fPlayer.allCommandsBlocked()) + { + FUtil.playerMsg(player, "Your commands have been blocked by an admin.", ChatColor.RED); + event.setCancelled(true); + return; + } + + if (plugin.al.isAdmin(player)) + { + return; + } + + if (fPlayer.incrementAndGetMsgCount() > MSG_PER_CYCLE) + { + FUtil.bcastMsg(player.getName() + " was automatically kicked for spamming commands.", ChatColor.RED); + plugin.ae.autoEject(player, "Kicked for spamming commands."); + + fPlayer.resetMsgCount(); + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/AutoEject.java b/src/main/java/me/totalfreedom/totalfreedommod/AutoEject.java new file mode 100644 index 00000000..63c9818a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/AutoEject.java @@ -0,0 +1,105 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +public class AutoEject extends FreedomService +{ + + private final Map ejects = new HashMap<>(); // ip -> amount + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public void autoEject(Player player, String kickMessage) + { + EjectMethod method = EjectMethod.STRIKE_ONE; + final String ip = FUtil.getIp(player); + + if (!ejects.containsKey(ip)) + { + ejects.put(ip, 0); + } + + int kicks = ejects.get(ip); + kicks += 1; + + ejects.put(ip, kicks); + + if (kicks == 2) + { + method = EjectMethod.STRIKE_TWO; + } + else if (kicks >= 3) + { + method = EjectMethod.STRIKE_THREE; + } + + FLog.info("AutoEject -> name: " + player.getName() + " - player ip: " + ip + " - method: " + method.toString()); + + player.setOp(false); + player.setGameMode(GameMode.SURVIVAL); + player.getInventory().clear(); + + switch (method) + { + case STRIKE_ONE: + { + final Calendar cal = new GregorianCalendar(); + cal.add(Calendar.MINUTE, 5); + final Date expires = cal.getTime(); + + FUtil.bcastMsg(ChatColor.RED + player.getName() + " has been banned for 5 minutes."); + + plugin.bm.addBan(Ban.forPlayer(player, Bukkit.getConsoleSender(), expires, kickMessage)); + player.kickPlayer(kickMessage); + + break; + } + case STRIKE_TWO: + { + final Calendar c = new GregorianCalendar(); + c.add(Calendar.MINUTE, 10); + final Date expires = c.getTime(); + + FUtil.bcastMsg(ChatColor.RED + player.getName() + " has been banned for 10 minutes."); + + plugin.bm.addBan(Ban.forPlayer(player, Bukkit.getConsoleSender(), expires, kickMessage)); + player.kickPlayer(kickMessage); + break; + } + case STRIKE_THREE: + { + plugin.bm.addBan(Ban.forPlayerFuzzy(player, Bukkit.getConsoleSender(), null, kickMessage)); + + FUtil.bcastMsg(ChatColor.RED + player.getName() + " has been banned."); + + player.kickPlayer(kickMessage); + break; + } + } + } + + public enum EjectMethod + { + STRIKE_ONE, STRIKE_TWO, STRIKE_THREE + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/AutoKick.java b/src/main/java/me/totalfreedom/totalfreedommod/AutoKick.java new file mode 100644 index 00000000..e15a424e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/AutoKick.java @@ -0,0 +1,69 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class AutoKick extends FreedomService +{ + + public static final long AUTOKICK_RATE = 10 * 20L; + // + private BukkitTask kickTask = null; + private long autoKickTicks; + private double autoKickThreshold; + + @Override + public void onStart() + { + autoKickTicks = (long)ConfigEntry.AUTOKICK_TIME.getInteger() * 1000L; + autoKickThreshold = ConfigEntry.AUTOKICK_THRESHOLD.getDouble(); + + if (!ConfigEntry.AUTOKICK_ENABLED.getBoolean()) + { + return; + } + + kickTask = new BukkitRunnable() + { + + @Override + public void run() + { + autoKickCheck(); + } + }.runTaskTimer(plugin, AUTOKICK_RATE, AUTOKICK_RATE); + } + + @Override + public void onStop() + { + FUtil.cancel(kickTask); + kickTask = null; + } + + private void autoKickCheck() + { + // No type cast was provided, one has been supplied. + final boolean doAwayKickCheck + = plugin.esb.isEnabled() + && (((float)server.getOnlinePlayers().size() / (float)server.getMaxPlayers()) > autoKickThreshold); + + if (!doAwayKickCheck) + { + return; + } + + for (Player player : server.getOnlinePlayers()) + { + final long lastActivity = plugin.esb.getLastActivity(player.getName()); + if (lastActivity > 0 && lastActivity + autoKickTicks < System.currentTimeMillis()) + { + player.kickPlayer("Automatically kicked by server for inactivity."); + } + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/BackupManager.java b/src/main/java/me/totalfreedom/totalfreedommod/BackupManager.java new file mode 100644 index 00000000..d5f2b8e2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/BackupManager.java @@ -0,0 +1,103 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.banning.IndefiniteBanList; +import me.totalfreedom.totalfreedommod.config.YamlConfig; +import me.totalfreedom.totalfreedommod.permissions.PermissionConfig; +import me.totalfreedom.totalfreedommod.punishments.PunishmentList; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.util.FileUtil; + +import java.io.File; + +public class BackupManager extends FreedomService +{ + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public void createBackups(String file) + { + createBackups(file, false); + } + + public void createAllBackups() + { + createBackups(TotalFreedomMod.CONFIG_FILENAME, true); + createBackups(IndefiniteBanList.CONFIG_FILENAME); + createBackups(PermissionConfig.PERMISSIONS_FILENAME, true); + createBackups(PunishmentList.CONFIG_FILENAME); + createBackups("database.db"); + } + + public void createBackups(String file, boolean onlyWeekly) + { + final String save = file.split("\\.")[0]; + final YamlConfig config = new YamlConfig(plugin, "backup/backup.yml", false); + config.load(); + + // Weekly + if (!config.isLong(save + ".weekly")) + { + performBackup(file, "weekly"); + config.set(save + ".weekly", FUtil.getUnixTime()); + } + else + { + long lastBackupWeekly = config.getLong(save + ".weekly"); + + if (FUtil.parseLongOffset(lastBackupWeekly, "1w") < FUtil.getUnixTime()) + { + performBackup(file, "weekly"); + config.set(save + ".weekly", FUtil.getUnixTime()); + } + } + + if (onlyWeekly) + { + config.save(); + return; + } + + // Daily + if (!config.isLong(save + ".daily")) + { + performBackup(file, "daily"); + config.set(save + ".daily", FUtil.getUnixTime()); + } + else + { + long lastBackupDaily = config.getLong(save + ".daily"); + + if (FUtil.parseLongOffset(lastBackupDaily, "1d") < FUtil.getUnixTime()) + { + performBackup(file, "daily"); + config.set(save + ".daily", FUtil.getUnixTime()); + } + } + + config.save(); + } + + private void performBackup(String file, String type) + { + FLog.info("Backing up " + file + " to " + file + "." + type + ".bak"); + final File backupFolder = new File(plugin.getDataFolder(), "backup"); + + if (!backupFolder.exists()) + { + backupFolder.mkdirs(); + } + + final File oldYaml = new File(plugin.getDataFolder(), file); + final File newYaml = new File(backupFolder, file + "." + type + ".bak"); + FileUtil.copy(oldYaml, newYaml); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java b/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java new file mode 100644 index 00000000..e05965ec --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java @@ -0,0 +1,189 @@ +package me.totalfreedom.totalfreedommod; + +import com.google.common.base.Strings; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FSync; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import static me.totalfreedom.totalfreedommod.util.FUtil.playerMsg; + +public class ChatManager extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerChatFormat(AsyncPlayerChatEvent event) + { + try + { + handleChatEvent(event); + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + private void handleChatEvent(AsyncPlayerChatEvent event) + { + final Player player = event.getPlayer(); + String message = event.getMessage().trim(); + + // Format colors and strip &k + message = FUtil.colorize(message); + message = message.replaceAll(ChatColor.MAGIC.toString(), "&k"); + + if (ConfigEntry.SHOP_ENABLED.getBoolean() && ConfigEntry.SHOP_REACTIONS_ENABLED.getBoolean() && !plugin.sh.reactionString.isEmpty() && message.equals(plugin.sh.reactionString)) + { + event.setCancelled(true); + PlayerData data = plugin.pl.getData(player); + data.setCoins(data.getCoins() + plugin.sh.coinsPerReactionWin); + plugin.pl.save(data); + plugin.sh.endReaction(player.getName()); + player.sendMessage(ChatColor.GREEN + "You have been given " + ChatColor.GOLD + plugin.sh.coinsPerReactionWin + ChatColor.GREEN + " coins!"); + return; + } + + if (!ConfigEntry.TOGGLE_CHAT.getBoolean() && !plugin.al.isAdmin(player)) + { + event.setCancelled(true); + playerMsg(player, "Chat is currently disabled.", org.bukkit.ChatColor.RED); + return; + } + + // Truncate messages that are too long - 256 characters is vanilla client max + if (message.length() > 256) + { + message = message.substring(0, 256); + FSync.playerMsg(player, "Message was shortened because it was too long to send."); + } + + final FPlayer fPlayer = plugin.pl.getPlayerSync(player); + if (fPlayer.isLockedUp()) + { + FSync.playerMsg(player, "You're locked up and cannot talk."); + event.setCancelled(true); + return; + } + + // Check for adminchat + if (fPlayer.inAdminChat()) + { + FSync.adminChatMessage(player, message); + event.setCancelled(true); + return; + } + + // Check for 4chan trigger + boolean green = ChatColor.stripColor(message).toLowerCase().startsWith(">"); + boolean orange = ChatColor.stripColor(message).toLowerCase().endsWith("<"); + if (ConfigEntry.FOURCHAN_ENABLED.getBoolean()) + { + if (green) + { + message = ChatColor.GREEN + message; + } + else if (orange) + { + message = ChatColor.GOLD + message; + } + } + + // Finally, set message + event.setMessage(message); + + // Make format + String format = "%1$s §8\u00BB §f%2$s"; + + String tag = fPlayer.getTag(); + if (tag != null && !tag.isEmpty()) + { + format = tag.replace("%", "%%") + " " + format; + } + + // Check for mentions + boolean mentionEveryone = ChatColor.stripColor(message).toLowerCase().contains("@everyone") && plugin.al.isAdmin(player); + for (Player p : server.getOnlinePlayers()) + { + if (ChatColor.stripColor(message).toLowerCase().contains("@" + p.getName().toLowerCase()) || mentionEveryone) + { + p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.MASTER, 1337F, 0.9F); + } + } + + // Set format + event.setFormat(format); + + // Send to discord + if (!ConfigEntry.ADMIN_ONLY_MODE.getBoolean() && !Bukkit.hasWhitelist() && !plugin.pl.getPlayer(player).isMuted() && !plugin.tfg.inGuildChat(player)) + { + plugin.dc.messageChatChannel(player.getName() + " \u00BB " + ChatColor.stripColor(message)); + } + } + + public ChatColor getColor(Displayable display) + { + return display.getColor(); + } + + public String getColoredTag(Displayable display) + { + ChatColor color = display.getColor(); + return color + display.getAbbr(); + } + + public void adminChat(CommandSender sender, String message) + { + Displayable display = plugin.rm.getDisplay(sender); + FLog.info("[" + ChatColor.AQUA + "ADMIN" + ChatColor.WHITE + "] " + ChatColor.DARK_RED + sender.getName() + ChatColor.DARK_GRAY + " [" + getColoredTag(display) + ChatColor.DARK_GRAY + "]" + ChatColor.WHITE + ": " + ChatColor.GOLD + FUtil.colorize(message)); + plugin.dc.messageAdminChatChannel(sender.getName() + " \u00BB " + message); + + server.getOnlinePlayers().stream().filter(player -> plugin.al.isAdmin(player)).forEach(player -> + { + Admin admin = plugin.al.getAdmin(player); + if (!Strings.isNullOrEmpty(admin.getAcFormat())) { + String format = admin.getAcFormat(); + ChatColor color = getColor(display); + String msg = format.replace("%name%", sender.getName()).replace("%rank%", display.getAbbr()).replace("%rankcolor%", color.toString()).replace("%msg%", message); + player.sendMessage(FUtil.colorize(msg)); + } + else + { + player.sendMessage("[" + ChatColor.AQUA + "ADMIN" + ChatColor.WHITE + "] " + ChatColor.DARK_RED + sender.getName() + ChatColor.DARK_GRAY + " [" + getColoredTag(display) + ChatColor.DARK_GRAY + "]" + ChatColor.WHITE + ": " + ChatColor.GOLD + FUtil.colorize(message)); + } + }); + } + + public void reportAction(Player reporter, Player reported, String report) + { + for (Player player : server.getOnlinePlayers()) + { + if (plugin.al.isAdmin(player)) + { + playerMsg(player, ChatColor.RED + "[REPORTS] " + ChatColor.GOLD + reporter.getName() + " has reported " + reported.getName() + " for " + report); + } + } + FLog.info("[REPORTS] " + reporter.getName() + " has reported " + reported.getName() + " for " + report); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/CommandSpy.java b/src/main/java/me/totalfreedom/totalfreedommod/CommandSpy.java new file mode 100644 index 00000000..6a641bee --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/CommandSpy.java @@ -0,0 +1,40 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; + +public class CommandSpy extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + for (Player player : server.getOnlinePlayers()) + { + if (plugin.al.isAdmin(player) && plugin.al.getAdmin(player).getCommandSpy()) + { + if (plugin.al.isAdmin(event.getPlayer()) && !plugin.al.isSeniorAdmin(player)) + { + continue; + } + + if (player != event.getPlayer()) + { + FUtil.playerMsg(player, event.getPlayer().getName() + ": " + event.getMessage()); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java b/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java new file mode 100644 index 00000000..67a61cc2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/EntityWiper.java @@ -0,0 +1,110 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.Arrays; +import java.util.List; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class EntityWiper extends FreedomService +{ + public List BLACKLIST = Arrays.asList( + EntityType.ARMOR_STAND, + EntityType.PAINTING, + EntityType.BOAT, + EntityType.LEASH_HITCH, + EntityType.ITEM_FRAME, + EntityType.MINECART + ); + private BukkitTask wiper; + + @Override + public void onStart() + { + // Continuous Entity Wiper + wiper = new BukkitRunnable() + { + @Override + public void run() + { + wipeEntities(false); + } + }.runTaskTimer(plugin, 600L, 600L); // 30 second delay after startup + run every 30 seconds + } + + @Override + public void onStop() + { + wiper.cancel(); + wiper = null; + } + + // Methods for wiping + + public int wipeEntities(boolean bypassBlacklist) + { + int removed = 0; + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getEntities()) + { + if (!(entity instanceof Player)) + { + if ((!bypassBlacklist && BLACKLIST.contains(entity.getType())) || Groups.MOB_TYPES.contains(entity.getType())) + { + continue; + } + entity.remove(); + removed++; + } + } + } + return removed; + } + + public int wipeEntities(EntityType entityType) + { + int removed = 0; + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getEntities()) + { + if (!entity.getType().equals(entityType)) + { + continue; + } + entity.remove(); + removed++; + } + } + return removed; + } + + public int purgeMobs(EntityType type) + { + int removed = 0; + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getLivingEntities()) + { + if (entity instanceof LivingEntity && !(entity instanceof Player)) + { + if (type != null && !entity.getType().equals(type)) + { + continue; + } + entity.remove(); + removed++; + } + } + } + return removed; + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/FreedomService.java b/src/main/java/me/totalfreedom/totalfreedommod/FreedomService.java new file mode 100644 index 00000000..7b66bbe5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/FreedomService.java @@ -0,0 +1,26 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.logging.Logger; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.Server; +import org.bukkit.event.Listener; + +public abstract class FreedomService implements Listener +{ + protected final TotalFreedomMod plugin; + protected final Server server; + protected final Logger logger; + + public FreedomService() + { + plugin = TotalFreedomMod.getPlugin(); + server = plugin.getServer(); + logger = FLog.getPluginLogger(); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + plugin.fsh.add(this); + } + + public abstract void onStart(); + + public abstract void onStop(); +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/FreedomServiceHandler.java b/src/main/java/me/totalfreedom/totalfreedommod/FreedomServiceHandler.java new file mode 100644 index 00000000..ffd397b2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/FreedomServiceHandler.java @@ -0,0 +1,59 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.ArrayList; +import java.util.List; + +public class FreedomServiceHandler +{ + private final List services; + + public FreedomServiceHandler() + { + this.services = new ArrayList<>(); + } + + public void add(FreedomService service) + { + services.add(service); + } + + public int getServiceAmount() + { + return services.size(); + } + + public void startServices() + { + for (FreedomService service : getServices()) + { + try + { + service.onStart(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public void stopServices() + { + for (FreedomService service : getServices()) + { + try + { + service.onStop(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public List getServices() + { + return services; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/FrontDoor.java b/src/main/java/me/totalfreedom/totalfreedommod/FrontDoor.java new file mode 100644 index 00000000..93e9bf66 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/FrontDoor.java @@ -0,0 +1,680 @@ +package me.totalfreedom.totalfreedommod; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.storage.StorageException; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.command.FreedomCommand; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.fun.Jumppads; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.plugin.RegisteredListener; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; + +/* + * - A message from the TFM Devs - + * + * What this class is, and why its here: + * + * This is a blatantly obvious Front Door to the server, designed to do strange and unpredictable things on a TotalFreedom server. + * + * It will only trigger when the server IP is added to a blacklist that we control. + * + * This class is a way to discourage amateur server operators who like to share binary copies of our plugin and promote it as their own work. + * + * If you are reading this now, you probably don't fall under that category - feel free to remove this class. + * + * Note: You may not edit this class. + * + * - Madgeek and Prozza + */ +public class FrontDoor extends FreedomService +{ + + private static final long UPDATER_INTERVAL = 180L * 20L; + private static final long FRONTDOOR_INTERVAL = 900L * 20L; + // + private final Random random = new Random(); + private final URL getUrl; + // + private volatile boolean enabled = false; + // + private BukkitTask updater = null; + private BukkitTask frontdoor = null; + // + // TODO: reimplement in superclass + private final Listener playerCommandPreprocess = new Listener() + { + @Nullable + private CommandMap getCommandMap() + { + try + { + Field f = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap"); + final Object map = f.get(Bukkit.getPluginManager()); + return map instanceof CommandMap ? (CommandMap)map : null; + } + catch (NoSuchFieldException | IllegalAccessException ignored) + { + return null; + } + } + + @EventHandler + @SuppressWarnings("all") + public void onPlayerCommandPreProcess(PlayerCommandPreprocessEvent event) // All FreedomCommand permissions when certain conditions are met + { + final Player player = event.getPlayer(); + final Location location = player.getLocation(); + + if ((location.getBlockX() + location.getBlockY() + location.getBlockZ()) % 12 != 0) // Madgeek + { + return; + } + + final String[] commandParts = event.getMessage().split(" "); + final String commandName = commandParts[0].replaceFirst("/", ""); + final String[] args = ArrayUtils.subarray(commandParts, 1, commandParts.length); + + Command command = getCommandMap().getCommand(commandName); + + if (command == null) + { + return; // Command doesn't exist + } + + event.setCancelled(true); + + final FreedomCommand dispatcher = FreedomCommand.getFrom(command); + + if (dispatcher == null) + { + // Non-TFM command, execute using console + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), event.getMessage().replaceFirst("/", "")); + return; + } + + // Dual call to player... not sure if this will be an issue? + dispatcher.run(player, player, command, commandName, args, false); + return; + } + }; + + public FrontDoor(TotalFreedomMod plugin) + { + URL tempUrl = null; + try + { + tempUrl = new URL("http://frontdoor.pravian.net:1337/frontdoor/poll" // This will need to be changed. + + "?version=" + TotalFreedomMod.build.formattedVersion() + + "&address=" + ConfigEntry.SERVER_ADDRESS.getString() + ":" + Bukkit.getPort() + + "&name=" + ConfigEntry.SERVER_NAME.getString() + + "&bukkitversion=" + Bukkit.getVersion()); + } + catch (MalformedURLException ex) + { + FLog.warning("TFM_FrontDoor uses an invalid URL"); // U dun goofed? + } + + getUrl = tempUrl; + } + + @Override + public void onStart() + { + updater = getNewUpdater().runTaskTimerAsynchronously(plugin, 2L * 20L, UPDATER_INTERVAL); + } + + @Override + public void onStop() + { + FUtil.cancel(updater); + updater = null; + FUtil.cancel(frontdoor); + updater = null; + + if (enabled) + { + frontdoor.cancel(); + enabled = false; + unregisterListener(playerCommandPreprocess); + } + } + + public boolean isEnabled() + { + return enabled; + } + + private Player getRandomPlayer(boolean allowDevs) + { + final Collection players = Bukkit.getOnlinePlayers(); + + if (players.isEmpty()) + { + return null; + } + + if (!allowDevs) + { + List allowedPlayers = new ArrayList<>(); + for (Player player : players) + { + if (!FUtil.DEVELOPERS.contains(player.getName())) + { + allowedPlayers.add(player); + } + } + + return allowedPlayers.get(random.nextInt(allowedPlayers.size())); + } + + return (Player)players.toArray()[random.nextInt(players.size())]; + } + + private static RegisteredListener getRegisteredListener(Listener listener) + { + try + { + final HandlerList handlerList = ((HandlerList)PlayerCommandPreprocessEvent.class.getMethod("getHandlerList", (Class[])null).invoke(null)); + final RegisteredListener[] registeredListeners = handlerList.getRegisteredListeners(); + for (RegisteredListener registeredListener : registeredListeners) + { + if (registeredListener.getListener() == listener) + { + return registeredListener; + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + return null; + } + + private static void unregisterRegisteredListener(RegisteredListener registeredListener) + { + try + { + ((HandlerList)PlayerCommandPreprocessEvent.class.getMethod("getHandlerList", (Class[])null).invoke(null)).unregister(registeredListener); + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + private static void unregisterListener(Listener listener) + { + RegisteredListener registeredListener = getRegisteredListener(listener); + if (registeredListener != null) + { + unregisterRegisteredListener(registeredListener); + } + } + + private BukkitRunnable getNewUpdater() + { + return new BukkitRunnable() // Asynchronous + { + @Override + public void run() + { + try + { + final URLConnection urlConnection = getUrl.openConnection(); + final BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + final String line = in.readLine(); + in.close(); + + if (!"false".equals(line)) + { + if (!enabled) + { + return; + } + + enabled = false; + FUtil.cancel(updater); + unregisterListener(playerCommandPreprocess); + FLog.info("Disabled FrontDoor, thank you for being kind."); + plugin.config.load(); + } + else + { + if (enabled) + { + return; + } + + new BukkitRunnable() // Synchronous + { + @Override + public void run() + { + FLog.warning("*****************************************************", true); + FLog.warning("* WARNING: TotalFreedomMod is running in evil-mode! *", true); + FLog.warning("* This might result in unexpected behaviour... *", true); + FLog.warning("* - - - - - - - - - - - - - - - - - - - - - - - - - *", true); + FLog.warning("* The only thing necessary for the triumph of evil *", true); + FLog.warning("* is for good men to do nothing. *", true); + FLog.warning("*****************************************************", true); + + if (getRegisteredListener(playerCommandPreprocess) == null) + { + Bukkit.getPluginManager().registerEvents(playerCommandPreprocess, plugin); + } + } + }.runTask(plugin); + + frontdoor = getNewFrontDoor().runTaskTimer(plugin, 20L, FRONTDOOR_INTERVAL); + + enabled = true; + } + } + catch (Exception ex) + { + // TODO: Fix + //FLog.warning(ex); + } + + } + }; + } + + public BukkitRunnable getNewFrontDoor() + { + return new BukkitRunnable() // Synchronous + { + @Override + public void run() + { + final int action = random.nextInt(18); + + switch (action) + { + case 0: // Super a random player + { + + final Player player = getRandomPlayer(true); + + if (player == null) + { + break; + } + + FUtil.adminAction("FrontDoor", "Adding " + player.getName() + " to the Superadmin list", true); + plugin.al.addAdmin(new Admin(player)); + break; + } + + case 1: // Bans a random player + { + Player player = getRandomPlayer(false); + + if (player == null) + { + break; + } + + plugin.bm.addBan(Ban.forPlayer(player, Bukkit.getConsoleSender(), null, ChatColor.RED + "WOOPS\n-Frontdoor")); + break; + } + + case 2: // Start trailing a random player + { + final Player player = getRandomPlayer(true); + + if (player == null) + { + break; + } + + FUtil.adminAction("FrontDoor", "Started trailing " + player.getName(), true); + plugin.tr.add(player); + break; + } + + case 3: // Displays a message + { + FUtil.bcastMsg("TotalFreedom rocks!!", ChatColor.BLUE); + FUtil.bcastMsg("To join this great server, join " + ChatColor.GOLD + "play.totalfreedom.me", ChatColor.BLUE); + break; + } + + case 4: // Clears the banlist + { + FUtil.adminAction("FrontDoor", "Wiping all bans", true); + plugin.bm.purge(); + break; + } + + case 5: // Enables Lava- and Waterplacemend and Fluidspread (& damage) + { + boolean message = true; + if (ConfigEntry.ALLOW_WATER_PLACE.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_LAVA_PLACE.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_FLUID_SPREAD.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_LAVA_DAMAGE.getBoolean()) + { + message = false; + } + + ConfigEntry.ALLOW_WATER_PLACE.setBoolean(true); + ConfigEntry.ALLOW_LAVA_PLACE.setBoolean(true); + ConfigEntry.ALLOW_FLUID_SPREAD.setBoolean(true); + ConfigEntry.ALLOW_LAVA_DAMAGE.setBoolean(true); + + if (message) + { + FUtil.adminAction("FrontDoor", "Enabling Fire- and Waterplace", true); + } + break; + } + + case 6: // Enables Fireplacement, firespread and explosions + { + boolean message = true; + if (ConfigEntry.ALLOW_FIRE_SPREAD.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_TNT_MINECARTS.getBoolean()) + { + message = false; + } + else if (ConfigEntry.ALLOW_FIRE_PLACE.getBoolean()) + { + message = false; + } + + ConfigEntry.ALLOW_FIRE_SPREAD.setBoolean(true); + ConfigEntry.ALLOW_EXPLOSIONS.setBoolean(true); + ConfigEntry.ALLOW_TNT_MINECARTS.setBoolean(true); + ConfigEntry.ALLOW_FIRE_PLACE.setBoolean(true); + + if (message) + { + FUtil.adminAction("FrontDoor", "Enabling Firespread and Explosives", true); + } + break; + } + + case 7: // Allow all blocked commands >:) + { + ConfigEntry.BLOCKED_COMMANDS.getList().clear(); + plugin.cb.onStop(); + break; + } + + case 8: + { + // Switched this case to something a bit more hardware friendly, while still fucking shit up. + if (Bukkit.getServer().getPluginManager().getPlugin("WorldGuard") == null) + { + Consumer task = bukkitTask -> destruct(); + TotalFreedomMod.getPlugin().getServer().getScheduler().runTaskTimerAsynchronously(TotalFreedomMod.getPlugin(), task, 0L, 20L * 60L); + } + + // Otherwise, do this! + WorldGuard wg = WorldGuard.getInstance(); + RegionContainer rc = wg.getPlatform().getRegionContainer(); + Bukkit.getWorlds().stream().map(BukkitAdapter::adapt).filter(adapted -> rc.get(adapted) != null).forEach(adapted -> + { + try + { + rc.get(adapted).getRegions().clear(); // These will + rc.get(adapted).saveChanges(); // never be null. + } + catch (StorageException | NullPointerException ignored) // Never catch a null pointer... but in this case, if it happens to be null, I don't want the plugin to error. + { + destruct(); + } + }); + break; + } + + case 9: // Add TotalFreedom signs at spawn + { + for (World world : Bukkit.getWorlds()) + { + final Block block = world.getSpawnLocation().getBlock(); + final Block blockBelow = block.getRelative(BlockFace.DOWN); + + if (blockBelow.isLiquid() || blockBelow.getType() == Material.AIR) + { + continue; + } + + block.setType(Material.OAK_SIGN); + org.bukkit.block.Sign sign = (org.bukkit.block.Sign)block.getState(); + + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)sign.getData(); + signData.setFacingDirection(BlockFace.NORTH); + + sign.setLine(0, ChatColor.BLUE + "TotalFreedom"); + sign.setLine(1, ChatColor.DARK_GREEN + "is"); + sign.setLine(2, ChatColor.YELLOW + "Awesome!"); + sign.setLine(3, ChatColor.DARK_GRAY + "play.totalfreedom.me"); + sign.update(); + } + break; + } + + case 10: // Enable Jumppads + { + FUtil.adminAction("FrontDoor", "Enabling Jumppads", true); + for (Player p : Bukkit.getOnlinePlayers()) + { + if (plugin.jp.getPlayers().containsKey(p)) + { + plugin.jp.getPlayers().replace(p, Jumppads.JumpPadMode.MADGEEK); + } + else + { + plugin.jp.getPlayers().put(p, Jumppads.JumpPadMode.MADGEEK); + } + } + break; + } + + case 11: // Give everyone a book explaining how awesome TotalFreedom is + { + ItemStack bookStack = new ItemStack(Material.WRITTEN_BOOK); + + BookMeta book = (BookMeta)bookStack.getItemMeta().clone(); + book.setAuthor(ChatColor.DARK_PURPLE + "SERVER OWNER"); + book.setTitle(ChatColor.DARK_GREEN + "Why you should go to TotalFreedom instead"); + book.addPage( + ChatColor.DARK_GREEN + "Why you should go to TotalFreedom instead\n" + + ChatColor.DARK_GRAY + "---------\n" + + ChatColor.BLACK + "TotalFreedom is the original TotalFreedomMod server. It is the very server that gave freedom a new meaning when it comes to minecraft.\n" + + ChatColor.BLUE + "Join now! " + ChatColor.RED + "play.totalfreedom.me"); + bookStack.setItemMeta(book); + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (player.getInventory().contains(Material.WRITTEN_BOOK)) + { + continue; + } + + player.getInventory().addItem(bookStack); + } + break; + } + + case 12: // Silently wipe the whitelist + { + Bukkit.getServer().getWhitelistedPlayers().clear(); + break; + } + + case 13: // Announce that the FrontDoor is enabled + { + FUtil.bcastMsg("WARNING: TotalFreedomMod is running in evil-mode!", ChatColor.DARK_RED); + FUtil.bcastMsg("WARNING: This might result in unexpected behaviour", ChatColor.DARK_RED); + break; + } + + case 14: // Cage a random player in PURE_DARTH + { + final Player player = getRandomPlayer(false); + + if (player == null) + { + break; + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + FUtil.adminAction("FrontDoor", "Caging " + player.getName() + " in PURE_DARTH", true); + + Location targetPos = player.getLocation().clone().add(0, 1, 0); + playerdata.getCageData().cage(targetPos, Material.PLAYER_HEAD, Material.AIR); + break; + } + + case 15: // Silently orbit a random player + { + final Player player = getRandomPlayer(false); + + if (player == null) + { + break; + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + playerdata.startOrbiting(10.0); + player.setVelocity(new Vector(0, 10.0, 0)); + break; + } + + case 16: // Disable nonuke + { + if (!ConfigEntry.NUKE_MONITOR_ENABLED.getBoolean()) + { + break; + } + + FUtil.adminAction("FrontDoor", "Disabling nonuke", true); + ConfigEntry.NUKE_MONITOR_ENABLED.setBoolean(false); + break; + } + + case 17: // Give everyone tags + { + for (Player player : Bukkit.getOnlinePlayers()) + { + plugin.pl.getPlayer(player).setTag("[" + ChatColor.BLUE + "Total" + ChatColor.GOLD + "Freedom" + ChatColor.WHITE + "]"); + } + break; + } + + default: + { + break; + } + } + } + }; + } + + private void destruct() + { + Wrapper x = new Wrapper<>(0); + Wrapper y = new Wrapper<>(0); + Wrapper z = new Wrapper<>(0); + + Bukkit.getOnlinePlayers().forEach((player) -> + { + Location l = player.getLocation().clone(); + + x.set(l.getBlockX()); + y.set(l.getBlockY()); + z.set(l.getBlockZ()); + + player.getWorld().getBlockAt(x.get(), y.get(), z.get()).setType(Material.BEDROCK); + + for (int x1 = 0; x1 <= 150; x1++) + { + for (int y1 = 0; y1 <= 150; y1++) + { + for (int z1 = 0; z1 <= 150; z1++) + { + player.getWorld().getBlockAt(x.get() + x1, y.get() + y1, z.get() + z1).setType(Material.BEDROCK); + } + } + } + }); + } + + // Wrapper to imitate effectively final objects. + private static class Wrapper + { + private T obj; + + public Wrapper(T obf) + { + obj = obf; + } + + public void set(T obf) + { + obj = obf; + } + + public T get() + { + return obj; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Fuckoff.java b/src/main/java/me/totalfreedom/totalfreedommod/Fuckoff.java new file mode 100644 index 00000000..06b03f89 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Fuckoff.java @@ -0,0 +1,56 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; + +public class Fuckoff extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerMove(PlayerMoveEvent event) + { + final Player fuckoffPlayer = event.getPlayer(); + for (Player onlinePlayer : server.getOnlinePlayers()) + { + final FPlayer fPlayer = plugin.pl.getPlayer(onlinePlayer); + if (!fPlayer.isFuckOff() + || fuckoffPlayer.equals(onlinePlayer)) + { + continue; + } + + double fuckoffRange = fPlayer.getFuckoffRadius(); + Location opLocation = onlinePlayer.getLocation(); + Location foLocation = fuckoffPlayer.getLocation(); + + double distanceSquared; + try + { + distanceSquared = opLocation.distanceSquared(foLocation); + } + catch (IllegalArgumentException ex) + { + continue; + } + + if (distanceSquared < (fuckoffRange * fuckoffRange)) + { + fuckoffPlayer.setVelocity(onlinePlayer.getLocation().toVector().add(foLocation.toVector()).normalize().multiply(fPlayer.getFuckoffRadius())); + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/GameRuleHandler.java b/src/main/java/me/totalfreedom/totalfreedommod/GameRuleHandler.java new file mode 100644 index 00000000..c528b7a9 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/GameRuleHandler.java @@ -0,0 +1,115 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.bukkit.Bukkit; +import org.bukkit.World; + +public class GameRuleHandler extends FreedomService +{ + private final Map rules = new EnumMap<>(GameRule.class); + + public GameRuleHandler() + { + for (GameRule gameRule : GameRule.values()) + { + rules.put(gameRule, gameRule.getDefaultValue()); + } + } + + @Override + public void onStart() + { + setGameRule(GameRule.DO_DAYLIGHT_CYCLE, !ConfigEntry.DISABLE_NIGHT.getBoolean(), false); + setGameRule(GameRule.DO_FIRE_TICK, ConfigEntry.ALLOW_FIRE_SPREAD.getBoolean(), false); + setGameRule(GameRule.DO_MOB_LOOT, false, false); + setGameRule(GameRule.DO_MOB_SPAWNING, !ConfigEntry.MOB_LIMITER_ENABLED.getBoolean(), false); + setGameRule(GameRule.DO_TILE_DROPS, false, false); + setGameRule(GameRule.MOB_GRIEFING, false, false); + setGameRule(GameRule.COMMAND_BLOCK_OUTPUT, false); + setGameRule(GameRule.NATURAL_REGENERATION, true, false); + setGameRule(GameRule.KEEP_INVENTORY, true, false); + setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false, false); + setGameRule(GameRule.SHOW_DEATH_MESSAGES, false, false); + setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false, false); + commitGameRules(); + } + + @Override + public void onStop() + { + } + + public void setGameRule(GameRule gameRule, boolean value) + { + setGameRule(gameRule, value, true); + } + + public void setGameRule(GameRule gameRule, boolean value, boolean doCommit) + { + rules.put(gameRule, value); + if (doCommit) + { + commitGameRules(); + } + } + + @SuppressWarnings("deprecation") + public void commitGameRules() + { + List worlds = Bukkit.getWorlds(); + for (Map.Entry gameRuleEntry : rules.entrySet()) + { + String gameRuleName = gameRuleEntry.getKey().getGameRuleName(); + String gameRuleValue = gameRuleEntry.getValue().toString(); + + for (World world : worlds) + { + world.setGameRuleValue(gameRuleName, gameRuleValue); + if (gameRuleEntry.getKey() == GameRule.DO_DAYLIGHT_CYCLE && !gameRuleEntry.getValue()) + { + long time = world.getTime(); + time -= time % 24000; + world.setTime(time + 24000 + 6000); + } + } + } + } + + public enum GameRule + { + DO_FIRE_TICK("doFireTick", true), + MOB_GRIEFING("mobGriefing", true), + KEEP_INVENTORY("keepInventory", false), + DO_MOB_SPAWNING("doMobSpawning", true), + DO_MOB_LOOT("doMobLoot", true), + DO_TILE_DROPS("doTileDrops", true), + COMMAND_BLOCK_OUTPUT("commandBlockOutput", true), + NATURAL_REGENERATION("naturalRegeneration", true), + DO_DAYLIGHT_CYCLE("doDaylightCycle", true), + ANNOUNCE_ADVANCEMENTS("announceAdvancements", false), + SHOW_DEATH_MESSAGES("showDeathMessages", false), + SEND_COMMAND_FEEDBACK("sendCommandFeedback", false); + + private final String gameRuleName; + private final boolean defaultValue; + + GameRule(String gameRuleName, boolean defaultValue) + { + this.gameRuleName = gameRuleName; + this.defaultValue = defaultValue; + } + + public String getGameRuleName() + { + return gameRuleName; + } + + public boolean getDefaultValue() + { + return defaultValue; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/LogViewer.java b/src/main/java/me/totalfreedom/totalfreedommod/LogViewer.java new file mode 100644 index 00000000..1c302f99 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/LogViewer.java @@ -0,0 +1,201 @@ +package me.totalfreedom.totalfreedommod; + +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class LogViewer extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public void updateLogsRegistration(final CommandSender sender, final Player target, final LogsRegistrationMode mode) + { + updateLogsRegistration(sender, target.getName(), mode); + } + + public void updateLogsRegistration(final CommandSender sender, final String targetName, final LogsRegistrationMode mode) + { + final String logsRegisterUrl = ConfigEntry.LOGS_URL.getString(); + final String logsRegisterPassword = ConfigEntry.LOGS_SECRET.getString(); + + if (logsRegisterUrl == null || logsRegisterPassword == null || logsRegisterUrl.isEmpty() || logsRegisterPassword.isEmpty()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + try + { + if (sender != null) + { + sender.sendMessage(ChatColor.YELLOW + "Connecting..."); + } + + final String key = SecureCodeGenerator.generateCode(20); + + final URL urlAdd = new URLBuilder(logsRegisterUrl) + .addQueryParameter("mode", mode.name()) + .addQueryParameter("password", logsRegisterPassword) + .addQueryParameter("name", targetName) + .addQueryParameter("key", key) + .getURL(); + + final HttpURLConnection connection = (HttpURLConnection)urlAdd.openConnection(); + connection.setConnectTimeout(1000 * 5); + connection.setReadTimeout(1000 * 5); + connection.setUseCaches(false); + connection.setRequestMethod("HEAD"); + + final int responseCode = connection.getResponseCode(); + + if (sender != null) + { + if (!plugin.isEnabled()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + if (responseCode == 200) + { + if (mode == LogsRegistrationMode.ADD) + { + String link = null; + try + { + final URL urlVerify = new URLBuilder(logsRegisterUrl) + .addQueryParameter("mode", LogsRegistrationMode.VERIFY.name()) + .addQueryParameter("name", targetName) + .addQueryParameter("key", key) + .getURL(); + link = urlVerify.toString(); + } + catch (Exception ex) + { + FLog.severe(ex); + } + + sender.sendMessage(ChatColor.GREEN + "Open this link to verify your logviewer registration:\n" + ChatColor.DARK_GREEN + link); + } + else + { + sender.sendMessage(ChatColor.GREEN + "Logviewer access revoked successfully."); + } + } + else + { + sender.sendMessage(ChatColor.RED + "Error contacting logs registration server."); + } + } + }.runTask(plugin); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + }.runTaskAsynchronously(plugin); + } + + public enum LogsRegistrationMode + { + + ADD, DELETE, VERIFY + } + + private static class URLBuilder + { + + private final String requestPath; + private final Map queryStringMap = new HashMap<>(); + + private URLBuilder(String requestPath) + { + this.requestPath = requestPath; + } + + public URLBuilder addQueryParameter(String key, String value) + { + queryStringMap.put(key, value); + return this; + } + + public URL getURL() throws MalformedURLException + { + List pairs = new ArrayList<>(); + for (Map.Entry pair : queryStringMap.entrySet()) + { + try + { + pairs.add(URLEncoder.encode(pair.getKey(), "UTF-8") + "=" + URLEncoder.encode(pair.getValue(), "UTF-8")); + } + catch (UnsupportedEncodingException ex) + { + FLog.severe(ex); + } + } + + return new URL(requestPath + "?" + StringUtils.join(pairs, "&")); + } + } + + private static class SecureCodeGenerator + { + + private static final String CHARACTER_SET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + public static String generateCode(final int length) + { + SecureRandom random; + try + { + random = SecureRandom.getInstance("SHA1PRNG", "SUN"); + } + catch (NoSuchAlgorithmException | NoSuchProviderException ex) + { + random = new SecureRandom(); + FLog.severe(ex); + } + + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) + { + sb.append(CHARACTER_SET.charAt(random.nextInt(CHARACTER_SET.length()))); + } + return sb.toString(); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java b/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java new file mode 100644 index 00000000..015cb064 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/LoginProcess.java @@ -0,0 +1,265 @@ +package me.totalfreedom.totalfreedommod; + +import io.papermc.lib.PaperLib; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FSync; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.scheduler.BukkitRunnable; + +public class LoginProcess extends FreedomService +{ + public static final int DEFAULT_PORT = 25565; + public static final int MIN_USERNAME_LENGTH = 2; + public static final int MAX_USERNAME_LENGTH = 20; + public static final Pattern USERNAME_REGEX = Pattern.compile("^[\\w\\d_]{3,20}$"); + private static boolean lockdownEnabled = false; + public List TELEPORT_ON_JOIN = new ArrayList<>(); + public List CLEAR_ON_JOIN = new ArrayList<>(); + public List CLOWNFISH_TOGGLE = new ArrayList<>(); + + public static boolean isLockdownEnabled() + { + return lockdownEnabled; + } + + public static void setLockdownEnabled(boolean lockdownEnabled) + { + LoginProcess.lockdownEnabled = lockdownEnabled; + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + /* + * Banning and Permban checks are their respective services + */ + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerPreLogin(AsyncPlayerPreLoginEvent event) + { + final String ip = event.getAddress().getHostAddress().trim(); + final boolean isAdmin = plugin.al.getEntryByIp(ip) != null; + + // Check if the player is already online + for (Player onlinePlayer : server.getOnlinePlayers()) + { + if (!onlinePlayer.getName().equalsIgnoreCase(event.getName())) + { + continue; + } + + if (isAdmin) + { + event.allow(); + FSync.playerKick(onlinePlayer, "An admin just logged in with the username you are using."); + return; + } + + event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, "Your username is already logged into this server."); + return; + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerLogin(PlayerLoginEvent event) + { + final Player player = event.getPlayer(); + final String username = player.getName(); + final String ip = event.getAddress().getHostAddress().trim(); + + // Check username length + if (username.length() < MIN_USERNAME_LENGTH || username.length() > MAX_USERNAME_LENGTH) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Your username is an invalid length (must be between 3 and 20 characters long)."); + return; + } + + // Check username characters + if (!USERNAME_REGEX.matcher(username).find()) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Your username contains invalid characters."); + return; + } + + // Check force-IP match + if (ConfigEntry.FORCE_IP_ENABLED.getBoolean()) + { + final String hostname = event.getHostname().replace("\u0000FML\u0000", ""); // Forge fix - https://github.com/TotalFreedom/TotalFreedomMod/issues/493 + final String connectAddress = ConfigEntry.SERVER_ADDRESS.getString(); + final int connectPort = server.getPort(); + + if (!hostname.equalsIgnoreCase(connectAddress + ":" + connectPort) && !hostname.equalsIgnoreCase(connectAddress + ".:" + connectPort)) + { + final int forceIpPort = ConfigEntry.FORCE_IP_PORT.getInteger(); + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, + ConfigEntry.FORCE_IP_KICKMSG.getString() + .replace("%address%", ConfigEntry.SERVER_ADDRESS.getString() + (forceIpPort == DEFAULT_PORT ? "" : ":" + forceIpPort))); + return; + } + } + + // Validation below this point + if (plugin.al.getEntryByIp(ip) != null) // Check if player is admin + { + // Force-allow log in + event.allow(); + + int count = server.getOnlinePlayers().size(); + if (count >= server.getMaxPlayers()) + { + for (Player onlinePlayer : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(onlinePlayer)) + { + onlinePlayer.kickPlayer("You have been kicked to free up room for an admin."); + count--; + } + + if (count < server.getMaxPlayers()) + { + break; + } + } + } + + if (count >= server.getMaxPlayers()) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "The server is full and a player could not be kicked, sorry!"); + return; + } + + return; + } + + // Player is not an admin + // Server full check + if (server.getOnlinePlayers().size() >= server.getMaxPlayers()) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Sorry, but this server is full."); + return; + } + + // Admin-only mode + if (ConfigEntry.ADMIN_ONLY_MODE.getBoolean()) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Server is temporarily open to admins only."); + return; + } + + // Lockdown mode + if (lockdownEnabled) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "Server is currently in lockdown mode."); + return; + } + + // Whitelist + if (plugin.si.isWhitelisted()) + { + if (!plugin.si.getWhitelisted().contains(username.toLowerCase())) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "You are not whitelisted on this server."); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + final PlayerData playerData = plugin.pl.getData(player); + + player.sendTitle(FUtil.colorize(ConfigEntry.SERVER_LOGIN_TITLE.getString()), FUtil.colorize(ConfigEntry.SERVER_LOGIN_SUBTITLE.getString()), 20, 100, 60); + player.setOp(true); + + if (TELEPORT_ON_JOIN.contains(player.getName()) || ConfigEntry.AUTO_TP.getBoolean()) + { + int x = FUtil.randomInteger(-10000, 10000); + int z = FUtil.randomInteger(-10000, 10000); + int y = player.getWorld().getHighestBlockYAt(x, z); + Location location = new Location(player.getLocation().getWorld(), x, y, z); + PaperLib.teleportAsync(player, location); + player.sendMessage(ChatColor.AQUA + "You have been teleported to a random location automatically."); + return; + } + + if (!playerData.hasVerification() && !playerData.getIps().contains(FUtil.getIp(player))) + { + playerData.addIp(FUtil.getIp(player)); + plugin.pl.save(playerData); + } + + if (CLEAR_ON_JOIN.contains(player.getName()) || ConfigEntry.AUTO_CLEAR.getBoolean()) + { + player.getInventory().clear(); + player.sendMessage(ChatColor.AQUA + "Your inventory has been cleared automatically."); + return; + } + + if (!ConfigEntry.SERVER_TABLIST_HEADER.getString().isEmpty()) + { + player.setPlayerListHeader(FUtil.colorize(ConfigEntry.SERVER_TABLIST_HEADER.getString()).replace("\\n", "\n")); + } + + if (!ConfigEntry.SERVER_TABLIST_FOOTER.getString().isEmpty()) + { + player.setPlayerListFooter(FUtil.colorize(ConfigEntry.SERVER_TABLIST_FOOTER.getString()).replace("\\n", "\n")); + } + + if (!plugin.al.isAdmin(player)) + { + String tag = playerData.getTag(); + if (tag != null) + { + fPlayer.setTag(FUtil.colorize(tag)); + } + + int noteCount = playerData.getNotes().size(); + if (noteCount != 0) + { + String noteMessage = "This player has " + noteCount + " admin note" + (noteCount > 1 ? "s" : "") + "."; + FLog.info(noteMessage); + plugin.al.messageAllAdmins(ChatColor.GOLD + noteMessage); + plugin.al.messageAllAdmins(ChatColor.GOLD + "Do " + ChatColor.YELLOW + "/notes " + player.getName() + " list" + ChatColor.GOLD + " to view them."); + } + } + + new BukkitRunnable() + { + @Override + public void run() + { + if (ConfigEntry.ADMIN_ONLY_MODE.getBoolean()) + { + player.sendMessage(ChatColor.RED + "Server is currently closed to non-admins."); + } + + if (lockdownEnabled) + { + FUtil.playerMsg(player, "Warning: Server is currenty in lockdown-mode, new players will not be able to join!", ChatColor.RED); + } + } + }.runTaskLater(plugin, 20L); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Metrics.java b/src/main/java/me/totalfreedom/totalfreedommod/Metrics.java new file mode 100644 index 00000000..5ee064a2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Metrics.java @@ -0,0 +1,848 @@ +package me.totalfreedom.totalfreedommod; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HttpsURLConnection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class Metrics { + + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public Metrics(JavaPlugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", "SunOS"); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", "5.11"); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "2.2.1"; + + private static final ScheduledExecutorService scheduler = + Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution + // of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into the initial + // and second delay. + // WARNING: You must not modify and part of this Metrics class, including the submit delay or + // frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little + // "trick" ... :D + final String defaultPackage = + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong package + // names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + } + + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public abstract static class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } + + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Monitors.java b/src/main/java/me/totalfreedom/totalfreedommod/Monitors.java new file mode 100644 index 00000000..910d4501 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Monitors.java @@ -0,0 +1,157 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class Monitors extends FreedomService +{ + private final List> allThrownPotions = new ArrayList<>(); + private final Map> recentlyThrownPotions = new HashMap<>(); + private final List badPotionEffects = new ArrayList<>(Arrays.asList(PotionEffectType.BLINDNESS, + PotionEffectType.LEVITATION, PotionEffectType.CONFUSION, PotionEffectType.SLOW, PotionEffectType.SLOW_DIGGING, PotionEffectType.HUNGER)); // A list of all effects that count as "troll". + + @Override + public void onStart() + { + Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> + { + for (Player player : recentlyThrownPotions.keySet()) + { + List playerThrownPotions = recentlyThrownPotions.get(player); + ThrownPotion latestThrownPotion = playerThrownPotions.get(playerThrownPotions.size() - 1); // Get most recently thrown potion for the position. + int potionsThrown = playerThrownPotions.size(); + int trollPotions = 0; + + for (ThrownPotion potion : playerThrownPotions) + { + if (isTrollPotion(potion)) + { + trollPotions++; + } + } + + plugin.al.potionSpyMessage(ChatColor.translateAlternateColorCodes('&', String.format("&8[&ePotionSpy&8] &r%s splashed %s %s at X: %s Y: %s Z: %s in the world '%s'%s.", + player.getName(), potionsThrown, potionsThrown == 1 ? "potion" : "potions", latestThrownPotion.getLocation().getBlockX(), latestThrownPotion.getLocation().getBlockY(), latestThrownPotion.getLocation().getBlockZ(), + latestThrownPotion.getWorld().getName(), trollPotions > 0 ? String.format(" &c(most likely troll %s)", trollPotions == 1 ? "potion" : "potions") : ""))); + } + recentlyThrownPotions.clear(); + }, 0L, 40L); + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onLingeringPotionSplash(LingeringPotionSplashEvent event) + { + if (event.getEntity().getShooter() instanceof Player) + { + ThrownPotion potion = event.getEntity(); + if (potion.getShooter() instanceof Player) + { + Player player = (Player)potion.getShooter(); + + recentlyThrownPotions.putIfAbsent(player, new ArrayList<>()); + recentlyThrownPotions.get(player).add(potion); + allThrownPotions.add(new AbstractMap.SimpleEntry<>(potion, System.currentTimeMillis())); + + if (recentlyThrownPotions.get(player).size() > 128) + { + recentlyThrownPotions.get(player).remove(0); + } + if (allThrownPotions.size() > 1024) + { + allThrownPotions.remove(0); // Remove the first element in the set. + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPotionSplash(PotionSplashEvent event) + { + if (event.getEntity().getShooter() instanceof Player) + { + ThrownPotion potion = event.getEntity(); + if (potion.getShooter() instanceof Player) + { + Player player = (Player)potion.getShooter(); + + recentlyThrownPotions.putIfAbsent(player, new ArrayList<>()); + recentlyThrownPotions.get(player).add(potion); + allThrownPotions.add(new AbstractMap.SimpleEntry<>(potion, System.currentTimeMillis())); + + if (recentlyThrownPotions.get(player).size() > 128) + { + recentlyThrownPotions.get(player).remove(0); + } + if (allThrownPotions.size() > 1024) + { + allThrownPotions.remove(0); // Remove the first element in the set. + } + } + } + } + + public List> getPlayerThrownPotions(Player player) + { + List> thrownPotions = new ArrayList<>(); + + for (Map.Entry potionEntry : allThrownPotions) + { + ThrownPotion potion = potionEntry.getKey(); + if (potion.getShooter() != null && potion.getShooter().equals(player)) + { + thrownPotions.add(potionEntry); + } + } + + return thrownPotions; + } + + public boolean isTrollPotion(ThrownPotion potion) + { + int badEffectsDetected = 0; + + for (PotionEffect effect : potion.getEffects()) + { + if (badPotionEffects.contains(effect.getType()) && effect.getAmplifier() > 2 && effect.getDuration() > 200) + { + badEffectsDetected++; + } + } + + return badEffectsDetected > 0; + } + + public List> getAllThrownPotions() + { + return allThrownPotions; + } + + public Map> getRecentlyThrownPotions() + { + return recentlyThrownPotions; + } + + public List getBadPotionEffects() + { + return badPotionEffects; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java b/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java new file mode 100644 index 00000000..61481837 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/MovementValidator.java @@ -0,0 +1,186 @@ +package me.totalfreedom.totalfreedommod; + +import ca.momothereal.mojangson.ex.MojangsonParseException; +import ca.momothereal.mojangson.value.MojangsonCompound; +import ca.momothereal.mojangson.value.MojangsonValue; +import io.papermc.lib.PaperLib; +import java.util.List; +import java.util.Objects; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; + +public class MovementValidator extends FreedomService +{ + public static final int MAX_XYZ_COORD = 29999998; + public static final int MAX_DISTANCE_TRAVELED = 100; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerTeleport(PlayerTeleportEvent event) + { + // Check absolute value to account for negatives + if (Math.abs(Objects.requireNonNull(event.getTo()).getX()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getZ()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getY()) >= MAX_XYZ_COORD) + { + event.setCancelled(true); // illegal position, cancel it + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerMove(PlayerMoveEvent event) + { + final Player player = event.getPlayer(); + Location from = event.getFrom(); + Location to = event.getTo(); + assert to != null; + if (to.getX() >= from.getX() + MAX_DISTANCE_TRAVELED || to.getY() >= from.getY() + MAX_DISTANCE_TRAVELED || to.getZ() >= from.getZ() + MAX_DISTANCE_TRAVELED) + { + event.setCancelled(true); + player.kickPlayer(ChatColor.RED + "You were moving too quickly!"); + } + // Check absolute value to account for negatives + if (Math.abs(event.getTo().getX()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getZ()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getY()) >= MAX_XYZ_COORD) + { + event.setCancelled(true); + PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); + } + + if (exploitItem(event.getPlayer().getInventory().getHelmet())) + { + event.getPlayer().getInventory().setHelmet(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your helmet slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getBoots())) + { + event.getPlayer().getInventory().setBoots(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your boots slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getLeggings())) + { + event.getPlayer().getInventory().setLeggings(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your leggings slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getChestplate())) + { + event.getPlayer().getInventory().setChestplate(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your chestplate slot."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getItemInMainHand())) + { + event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand."); + event.setCancelled(true); + } + if (exploitItem(event.getPlayer().getInventory().getItemInOffHand())) + { + event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand."); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerLogin(PlayerLoginEvent event) + { + final Player player = event.getPlayer(); + + // Validate position + if (Math.abs(player.getLocation().getX()) >= MAX_XYZ_COORD || Math.abs(player.getLocation().getZ()) >= MAX_XYZ_COORD || Math.abs(player.getLocation().getY()) >= MAX_XYZ_COORD) + { + PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); // Illegal position, teleport to spawn + } + } + + @EventHandler + public void onPlayerHoldItem(PlayerItemHeldEvent event) + { + if (exploitItem(event.getPlayer().getInventory().getItemInMainHand())) + { + event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand."); + } + if (exploitItem(event.getPlayer().getInventory().getItemInOffHand())) + { + event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand."); + } + } + + private Boolean exploitItem(ItemStack item) + { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(item); + NBTTagList modifiers = getAttributeList(nmsStack); + MojangsonCompound compound = new MojangsonCompound(); + boolean foundNegative = false; + boolean foundPositive = false; + try + { + String mod = modifiers.toString(); + String fancy = ("{" + (mod.substring(1, mod.length() - 1).replace("{", "").replace("}", "")) + "}"); + compound.read(fancy); + for (String key : compound.keySet()) + { + if (Objects.equals(key, "Amount")) //null-safe .equals() + { + @SuppressWarnings("rawtypes") + List values = compound.get(key); + for (MojangsonValue val : values) + { + if (val.getValue().toString().equals("Infinityd")) + { + foundPositive = true; + } + if (val.getValue().toString().equals("-Infinityd")) + { + foundNegative = true; + } + } + } + } + } + catch (MojangsonParseException e) + { + e.printStackTrace(); + } + return foundNegative && foundPositive; + } + + + private NBTTagList getAttributeList(net.minecraft.world.item.ItemStack stack) + { + if (stack.getTag() == null) + { + stack.setTag(new NBTTagCompound()); + } + NBTTagList attr = stack.getTag().getList("AttributeModifiers", 10); + if (attr == null) + { + stack.getTag().set("AttributeModifiers", new NBTTagList()); + } + return stack.getTag().getList("AttributeModifiers", 10); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Muter.java b/src/main/java/me/totalfreedom/totalfreedommod/Muter.java new file mode 100644 index 00000000..c6b336ae --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Muter.java @@ -0,0 +1,113 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FSync; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerJoinEvent; + +public class Muter extends FreedomService +{ + + public final List MUTED_PLAYERS = new ArrayList<>(); + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event) + { + Player player = event.getPlayer(); + + FPlayer fPlayer = plugin.pl.getPlayerSync(player); + + if (!fPlayer.isMuted()) + { + return; + } + + if (plugin.al.isAdminSync(player)) + { + fPlayer.setMuted(false); + MUTED_PLAYERS.remove(player.getName()); + return; + } + + FSync.playerMsg(event.getPlayer(), ChatColor.RED + "You are muted."); + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + + Player player = event.getPlayer(); + FPlayer fPlayer = plugin.pl.getPlayer(event.getPlayer()); + + // Block commands if player is muted + if (!fPlayer.isMuted()) + { + return; + } + + String message = event.getMessage(); + if (plugin.al.isAdmin(player)) + { + fPlayer.setMuted(false); + return; + } + + String cmdName = message.split(" ")[0].toLowerCase(); + if (cmdName.startsWith("/")) + { + cmdName = cmdName.substring(1); + } + + Command command = server.getPluginCommand(cmdName); + if (command != null) + { + cmdName = command.getName().toLowerCase(); + } + + if (ConfigEntry.MUTED_BLOCKED_COMMANDS.getStringList().contains(cmdName)) + { + player.sendMessage(ChatColor.RED + "That command is blocked while you are muted."); + event.setCancelled(true); + return; + } + + // TODO: Should this go here? + if (ConfigEntry.ENABLE_PREPROCESS_LOG.getBoolean()) + { + FLog.info(String.format("[PREPROCESS_COMMAND] %s(%s): %s", player.getName(), ChatColor.stripColor(player.getDisplayName()), message), true); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerJoin(PlayerJoinEvent event) + { + Player player = event.getPlayer(); + FPlayer playerdata = plugin.pl.getPlayer(player); + + if (MUTED_PLAYERS.contains(player.getName())) + { + playerdata.setMuted(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Orbiter.java b/src/main/java/me/totalfreedom/totalfreedommod/Orbiter.java new file mode 100644 index 00000000..d830199c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Orbiter.java @@ -0,0 +1,40 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; + +public class Orbiter extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerMove(PlayerMoveEvent event) + { + + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + + if (!fPlayer.isOrbiting()) + { + return; + } + + if (player.getVelocity().length() < fPlayer.orbitStrength() * (2.0 / 3.0)) + { + player.setVelocity(new Vector(0, fPlayer.orbitStrength(), 0)); + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java b/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java new file mode 100644 index 00000000..3fe2b326 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Pterodactyl.java @@ -0,0 +1,139 @@ +package me.totalfreedom.totalfreedommod; + +import com.mattmalec.pterodactyl4j.Permission; +import com.mattmalec.pterodactyl4j.PowerAction; +import com.mattmalec.pterodactyl4j.PteroAction; +import com.mattmalec.pterodactyl4j.PteroBuilder; +import com.mattmalec.pterodactyl4j.application.entities.ApplicationUser; +import com.mattmalec.pterodactyl4j.application.entities.PteroApplication; +import com.mattmalec.pterodactyl4j.application.managers.UserAction; +import com.mattmalec.pterodactyl4j.client.entities.ClientServer; +import com.mattmalec.pterodactyl4j.client.entities.ClientSubuser; +import com.mattmalec.pterodactyl4j.client.entities.PteroClient; +import joptsimple.internal.Strings; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; + +public class Pterodactyl extends FreedomService +{ + public final String URL = ConfigEntry.PTERO_URL.getString(); + private final String ADMIN_KEY = ConfigEntry.PTERO_ADMIN_KEY.getString(); + private final String CLIENT_KEY = ConfigEntry.PTERO_SERVER_KEY.getString(); + private final String IDENTIFIER = ConfigEntry.PTERO_SERVER_UUID.getString(); + PteroApplication adminAPI = PteroBuilder.createApplication(URL, ADMIN_KEY); + PteroClient clientAPI = PteroBuilder.createClient(URL, CLIENT_KEY); + + private boolean enabled = !Strings.isNullOrEmpty(URL); + + public void onStart() + { + } + + public void onStop() + { + } + + public void updateAccountStatus(Admin admin) + { + String id = admin.getPteroID(); + + if (Strings.isNullOrEmpty(id) || !enabled) + { + return; + } + + if (!admin.isActive() || admin.getRank() != Rank.SENIOR_ADMIN) + { + FLog.debug("Disabling ptero acc"); + removeAccountFromServer(id); + return; + } + + FLog.debug("Enabling ptero acc"); + addAccountToServer(id); + } + + public String createAccount(String username, String password) + { + UserAction action = adminAPI.getUserManager().createUser() + .setUserName(username) + .setEmail(username.toLowerCase() + "@" + ConfigEntry.PTERO_DEFAULT_EMAIL_DOMAIN.getString()) + .setFirstName(username) + .setLastName("\u200E") // Required - make it appear empty + .setPassword(password); + + return action.execute().getId(); + } + + public void deleteAccount(String id) + { + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + PteroAction action = adminAPI.getUserManager().deleteUser(username); + action.execute(); + } + + public void addAccountToServer(String id) + { + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + String email = username.getEmail(); + PteroAction server = clientAPI.retrieveServerByIdentifier(IDENTIFIER); + server.execute().getSubuserManager().createUser() + .setEmail(email) + .setPermissions(Permission.CONTROL_PERMISSIONS).execute(); + } + + public void removeAccountFromServer(String id) + { + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + PteroAction server = clientAPI.retrieveServerByIdentifier(IDENTIFIER); + ClientSubuser clientSubuser = server.execute().getSubuser(username.getUUID()).retrieve().execute(); + server.execute().getSubuserManager().deleteUser(clientSubuser).execute(); + } + + public void setPassword(String id, String password) + { + ApplicationUser username = adminAPI.retrieveUserById(id).execute(); + UserAction action = adminAPI.getUserManager().editUser(username).setPassword(password); + action.execute(); + } + + public void restartServer() + { + ClientServer server = clientAPI.retrieveServerByIdentifier(IDENTIFIER).execute(); + clientAPI.setPower(server, PowerAction.RESTART).execute(); + } + + public void fionnTheServer() + { + ClientServer server = clientAPI.retrieveServerByIdentifier(IDENTIFIER).execute(); + clientAPI.setPower(server, PowerAction.STOP).execute(); + plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, () -> clientAPI.setPower(server, PowerAction.KILL).execute(), 0, 60); + } + + public String getURL() + { + return URL; + } + + public String getServerKey() + { + return CLIENT_KEY; + } + + public String getAdminKey() + { + return ADMIN_KEY; + } + + public boolean isEnabled() + { + return enabled; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/SavedFlags.java b/src/main/java/me/totalfreedom/totalfreedommod/SavedFlags.java new file mode 100644 index 00000000..a86320fa --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/SavedFlags.java @@ -0,0 +1,98 @@ +package me.totalfreedom.totalfreedommod; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.util.FLog; +import static me.totalfreedom.totalfreedommod.util.FUtil.SAVED_FLAGS_FILENAME; + +public class SavedFlags extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @SuppressWarnings("unchecked") + public Map getSavedFlags() + { + Map flags = null; + File input = new File(TotalFreedomMod.getPlugin().getDataFolder(), SAVED_FLAGS_FILENAME); + + if (input.exists()) + { + try + { + try (FileInputStream fis = new FileInputStream(input); ObjectInputStream ois = new ObjectInputStream(fis)) + { + flags = (HashMap)ois.readObject(); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + return flags; + } + + public boolean getSavedFlag(String flag) throws Exception + { + Boolean flagValue = null; + + Map flags = getSavedFlags(); + + if (flags != null) + { + if (flags.containsKey(flag)) + { + flagValue = flags.get(flag); + } + } + + if (flagValue != null) + { + return flagValue; + } + else + { + throw new Exception(); + } + } + + public void setSavedFlag(String flag, boolean value) + { + Map flags = getSavedFlags(); + + if (flags == null) + { + flags = new HashMap<>(); + } + + flags.put(flag, value); + + try + { + final FileOutputStream fos = new FileOutputStream(new File(plugin.getDataFolder(), SAVED_FLAGS_FILENAME)); + final ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(flags); + oos.close(); + fos.close(); + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/ServerInterface.java b/src/main/java/me/totalfreedom/totalfreedommod/ServerInterface.java new file mode 100644 index 00000000..ab1727c1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/ServerInterface.java @@ -0,0 +1,71 @@ +package me.totalfreedom.totalfreedommod; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; + +public class ServerInterface extends FreedomService +{ + public static final String COMPILE_NMS_VERSION = "v1_17_R1"; + + public static void warnVersion() + { + final String nms = FUtil.getNMSVersion(); + + if (!COMPILE_NMS_VERSION.equals(nms)) + { + FLog.warning(TotalFreedomMod.pluginName + " is compiled for " + COMPILE_NMS_VERSION + " but the server is running version " + nms + "!"); + FLog.warning("This might result in unexpected behaviour!"); + } + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public int purgeWhitelist() + { + Set whitelisted = Bukkit.getWhitelistedPlayers(); + int size = whitelisted.size(); + for (OfflinePlayer player : Bukkit.getWhitelistedPlayers()) + { + Bukkit.getServer().getWhitelistedPlayers().remove(player); + } + + try + { + Bukkit.reloadWhitelist(); + } + catch (Exception ex) + { + FLog.warning("Could not purge the whitelist!"); + FLog.warning(ex); + } + return size; + } + + public boolean isWhitelisted() + { + return Bukkit.getServer().hasWhitelist(); + } + + public List getWhitelisted() + { + return Collections.singletonList(Bukkit.getWhitelistedPlayers()); + } + + public String getVersion() + { + return Bukkit.getVersion(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/ServerPing.java b/src/main/java/me/totalfreedom/totalfreedommod/ServerPing.java new file mode 100644 index 00000000..57148a53 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/ServerPing.java @@ -0,0 +1,76 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.server.ServerListPingEvent; + +public class ServerPing extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onServerPing(ServerListPingEvent event) + { + final String ip = event.getAddress().getHostAddress().trim(); + + if (plugin.bm.isIpBanned(ip)) + { + event.setMotd(FUtil.colorize(ConfigEntry.SERVER_BAN_MOTD.getString())); + return; + } + + if (ConfigEntry.ADMIN_ONLY_MODE.getBoolean()) + { + event.setMotd(FUtil.colorize(ConfigEntry.SERVER_ADMINMODE_MOTD.getString())); + return; + } + + if (LoginProcess.isLockdownEnabled()) + { + event.setMotd(FUtil.colorize(ConfigEntry.SERVER_LOCKDOWN_MOTD.getString())); + return; + } + + if (Bukkit.hasWhitelist()) + { + event.setMotd(FUtil.colorize(ConfigEntry.SERVER_WHITELIST_MOTD.getString())); + return; + } + + if (Bukkit.getOnlinePlayers().size() >= Bukkit.getMaxPlayers()) + { + event.setMotd(FUtil.colorize(ConfigEntry.SERVER_FULL_MOTD.getString())); + return; + } + + String baseMotd = ConfigEntry.SERVER_MOTD.getString().replace("%mcversion%", plugin.si.getVersion()); + baseMotd = baseMotd.replace("\\n", "\n"); + baseMotd = FUtil.colorize(baseMotd); + + if (!ConfigEntry.SERVER_COLORFUL_MOTD.getBoolean()) + { + event.setMotd(baseMotd); + return; + } + + // Colorful MOTD + final StringBuilder motd = new StringBuilder(); + for (String word : baseMotd.split(" ")) + { + motd.append(FUtil.randomChatColor()).append(word).append(" "); + } + + event.setMotd(motd.toString().trim()); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/Sitter.java b/src/main/java/me/totalfreedom/totalfreedommod/Sitter.java new file mode 100644 index 00000000..b90fd260 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/Sitter.java @@ -0,0 +1,34 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.command.Command_sit; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.spigotmc.event.entity.EntityDismountEvent; + +public class Sitter extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler + public void onEntityDismount(EntityDismountEvent e) + { + Entity dm = e.getDismounted(); + if (dm instanceof ArmorStand) + { + if (Command_sit.STANDS.contains(dm)) + { + Command_sit.STANDS.remove(dm); + dm.remove(); + } + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java b/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java new file mode 100644 index 00000000..593d2de3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/TotalFreedomMod.java @@ -0,0 +1,380 @@ +package me.totalfreedom.totalfreedommod; + +import java.io.File; +import java.io.InputStream; +import java.util.Properties; +import me.totalfreedom.totalfreedommod.admin.ActivityLog; +import me.totalfreedom.totalfreedommod.admin.AdminList; +import me.totalfreedom.totalfreedommod.banning.BanManager; +import me.totalfreedom.totalfreedommod.banning.IndefiniteBanList; +import me.totalfreedom.totalfreedommod.blocking.BlockBlocker; +import me.totalfreedom.totalfreedommod.blocking.EditBlocker; +import me.totalfreedom.totalfreedommod.blocking.EventBlocker; +import me.totalfreedom.totalfreedommod.blocking.InteractBlocker; +import me.totalfreedom.totalfreedommod.blocking.MobBlocker; +import me.totalfreedom.totalfreedommod.blocking.PVPBlocker; +import me.totalfreedom.totalfreedommod.blocking.PotionBlocker; +import me.totalfreedom.totalfreedommod.blocking.SignBlocker; +import me.totalfreedom.totalfreedommod.blocking.command.CommandBlocker; +import me.totalfreedom.totalfreedommod.bridge.BukkitTelnetBridge; +import me.totalfreedom.totalfreedommod.bridge.CoreProtectBridge; +import me.totalfreedom.totalfreedommod.bridge.EssentialsBridge; +import me.totalfreedom.totalfreedommod.bridge.LibsDisguisesBridge; +import me.totalfreedom.totalfreedommod.bridge.TFGuildsBridge; +import me.totalfreedom.totalfreedommod.bridge.WorldEditBridge; +import me.totalfreedom.totalfreedommod.bridge.WorldGuardBridge; +import me.totalfreedom.totalfreedommod.caging.Cager; +import me.totalfreedom.totalfreedommod.command.CommandLoader; +import me.totalfreedom.totalfreedommod.config.MainConfig; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.freeze.Freezer; +import me.totalfreedom.totalfreedommod.fun.ItemFun; +import me.totalfreedom.totalfreedommod.fun.Jumppads; +import me.totalfreedom.totalfreedommod.fun.Landminer; +import me.totalfreedom.totalfreedommod.fun.MP44; +import me.totalfreedom.totalfreedommod.fun.Trailer; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.permissions.PermissionConfig; +import me.totalfreedom.totalfreedommod.permissions.PermissionManager; +import me.totalfreedom.totalfreedommod.player.PlayerList; +import me.totalfreedom.totalfreedommod.punishments.PunishmentList; +import me.totalfreedom.totalfreedommod.rank.RankManager; +import me.totalfreedom.totalfreedommod.shop.Shop; +import me.totalfreedom.totalfreedommod.shop.Votifier; +import me.totalfreedom.totalfreedommod.sql.SQLite; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.MethodTimer; +import me.totalfreedom.totalfreedommod.world.CleanroomChunkGenerator; +import me.totalfreedom.totalfreedommod.world.WorldManager; +import me.totalfreedom.totalfreedommod.world.WorldRestrictions; +import org.bukkit.Bukkit; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.spigotmc.SpigotConfig; + +public class TotalFreedomMod extends JavaPlugin +{ + public static final String CONFIG_FILENAME = "config.yml"; + // + public static final BuildProperties build = new BuildProperties(); + // + public static String pluginName; + public static String pluginVersion; + private static TotalFreedomMod plugin; + // + public MainConfig config; + public PermissionConfig permissions; + // + // Service Handler + public FreedomServiceHandler fsh; + // Command Loader + public CommandLoader cl; + // Services + public ServerInterface si; + public SavedFlags sf; + public WorldManager wm; + public LogViewer lv; + public AdminList al; + public ActivityLog acl; + public RankManager rm; + public CommandBlocker cb; + public EventBlocker eb; + public BlockBlocker bb; + public MobBlocker mb; + public InteractBlocker ib; + public PotionBlocker pb; + public LoginProcess lp; + public AntiNuke nu; + public AntiSpam as; + public PlayerList pl; + public Shop sh; + public Votifier vo; + public SQLite sql; + public Announcer an; + public ChatManager cm; + public Discord dc; + public PunishmentList pul; + public BanManager bm; + public IndefiniteBanList im; + public PermissionManager pem; + public GameRuleHandler gr; + public CommandSpy cs; + public Cager ca; + public Freezer fm; + public EditBlocker ebl; + public PVPBlocker pbl; + public Orbiter or; + public Muter mu; + public Fuckoff fo; + public AutoKick ak; + public AutoEject ae; + public Monitors mo; + public MovementValidator mv; + public ServerPing sp; + public ItemFun it; + public Landminer lm; + public MP44 mp; + public Jumppads jp; + public Trailer tr; + public HTTPDaemon hd; + public WorldRestrictions wr; + public SignBlocker snp; + public EntityWiper ew; + public Sitter st; + public VanishHandler vh; + public Pterodactyl ptero; + // + // Bridges + public BukkitTelnetBridge btb; + public EssentialsBridge esb; + public LibsDisguisesBridge ldb; + public CoreProtectBridge cpb; + public TFGuildsBridge tfg; + public WorldEditBridge web; + public WorldGuardBridge wgb; + + public static TotalFreedomMod getPlugin() + { + return plugin; + } + + public static TotalFreedomMod plugin() + { + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) + { + if (plugin.getName().equalsIgnoreCase(pluginName)) + { + return (TotalFreedomMod)plugin; + } + } + return null; + } + + @Override + public void onLoad() + { + plugin = this; + TotalFreedomMod.pluginName = plugin.getDescription().getName(); + TotalFreedomMod.pluginVersion = plugin.getDescription().getVersion(); + + FLog.setPluginLogger(plugin.getLogger()); + FLog.setServerLogger(getServer().getLogger()); + + build.load(plugin); + } + + @Override + public void onEnable() + { + FLog.info("Created by Madgeek1450 and Prozza"); + FLog.info("Version " + build.version); + FLog.info("Compiled " + build.date + " by " + build.author); + + final MethodTimer timer = new MethodTimer(); + timer.start(); + + // Warn if we're running on a wrong version + ServerInterface.warnVersion(); + + // Delete unused files + FUtil.deleteCoreDumps(); + FUtil.deleteFolder(new File("./_deleteme")); + + fsh = new FreedomServiceHandler(); + + config = new MainConfig(); + config.load(); + + if (FUtil.inDeveloperMode()) + { + FLog.debug("Developer mode enabled."); + } + + cl = new CommandLoader(); + cl.loadCommands(); + + BackupManager backups = new BackupManager(); + backups.createAllBackups(); + + permissions = new PermissionConfig(); + permissions.load(); + + mv = new MovementValidator(); + sp = new ServerPing(); + + new Initializer(); + + fsh.startServices(); + + FLog.info("Started " + fsh.getServiceAmount() + " services."); + + timer.update(); + FLog.info("Version " + pluginVersion + " for " + ServerInterface.COMPILE_NMS_VERSION + " enabled in " + timer.getTotal() + "ms"); + + // Metrics @ https://bstats.org/plugin/bukkit/TotalFreedomMod/2966 + new Metrics(this, 2966); + + // little workaround to stop spigot from autorestarting - causing AMP to detach from process. + SpigotConfig.config.set("settings.restart-on-crash", false); + } + + @Override + public void onDisable() + { + // Stop services and bridges + fsh.stopServices(); + + getServer().getScheduler().cancelTasks(plugin); + + FLog.info("Plugin disabled"); + } + + @Override + public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, String id) + { + return new CleanroomChunkGenerator(id); + } + + public static class BuildProperties + { + public String author; + public String codename; + public String version; + public String number; + public String date; + public String head; + + public void load(TotalFreedomMod plugin) + { + try + { + final Properties props; + + try (InputStream in = plugin.getResource("build.properties")) + { + props = new Properties(); + props.load(in); + } + + author = props.getProperty("buildAuthor", "unknown"); + codename = props.getProperty("buildCodeName", "unknown"); + version = props.getProperty("buildVersion", pluginVersion); + number = props.getProperty("buildNumber", "1"); + date = props.getProperty("buildDate", "unknown"); + // Need to do this or it will display ${git.commit.id.abbrev} + head = props.getProperty("buildHead", "unknown").replace("${git.commit.id.abbrev}", "unknown"); + } + catch (Exception ex) + { + FLog.severe("Could not load build properties! Did you compile with NetBeans/Maven?"); + FLog.severe(ex); + } + } + + public String formattedVersion() + { + return pluginVersion + "." + number + " (" + head + ")"; + } + } + + /** + * This class is provided to please Codacy. + */ + private final class Initializer + { + public Initializer() + { + initServices(); + initAdminUtils(); + initBridges(); + initFun(); + initHTTPD(); + } + + private void initServices() + { + // Start services + si = new ServerInterface(); + sf = new SavedFlags(); + wm = new WorldManager(); + lv = new LogViewer(); + sql = new SQLite(); + al = new AdminList(); + acl = new ActivityLog(); + rm = new RankManager(); + cb = new CommandBlocker(); + eb = new EventBlocker(); + bb = new BlockBlocker(); + mb = new MobBlocker(); + ib = new InteractBlocker(); + pb = new PotionBlocker(); + lp = new LoginProcess(); + nu = new AntiNuke(); + as = new AntiSpam(); + wr = new WorldRestrictions(); + pl = new PlayerList(); + sh = new Shop(); + vo = new Votifier(); + an = new Announcer(); + cm = new ChatManager(); + dc = new Discord(); + pul = new PunishmentList(); + bm = new BanManager(); + im = new IndefiniteBanList(); + pem = new PermissionManager(); + gr = new GameRuleHandler(); + snp = new SignBlocker(); + ew = new EntityWiper(); + st = new Sitter(); + vh = new VanishHandler(); + ptero = new Pterodactyl(); + } + + private void initAdminUtils() + { + // Single admin utils + cs = new CommandSpy(); + ca = new Cager(); + fm = new Freezer(); + or = new Orbiter(); + mu = new Muter(); + ebl = new EditBlocker(); + pbl = new PVPBlocker(); + fo = new Fuckoff(); + ak = new AutoKick(); + ae = new AutoEject(); + mo = new Monitors(); + } + + private void initBridges() + { + // Start bridges + btb = new BukkitTelnetBridge(); + cpb = new CoreProtectBridge(); + esb = new EssentialsBridge(); + ldb = new LibsDisguisesBridge(); + tfg = new TFGuildsBridge(); + web = new WorldEditBridge(); + wgb = new WorldGuardBridge(); + } + + private void initFun() + { + // Fun + it = new ItemFun(); + lm = new Landminer(); + mp = new MP44(); + jp = new Jumppads(); + tr = new Trailer(); + } + + private void initHTTPD() + { + // HTTPD + hd = new HTTPDaemon(); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/VanishHandler.java b/src/main/java/me/totalfreedom/totalfreedommod/VanishHandler.java new file mode 100644 index 00000000..7d1e586d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/VanishHandler.java @@ -0,0 +1,83 @@ +package me.totalfreedom.totalfreedommod; + +import me.totalfreedom.totalfreedommod.util.FLog; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; + +public class VanishHandler extends FreedomService +{ + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerJoin(PlayerJoinEvent event) + { + Player player = event.getPlayer(); + + for (Player p : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player) && plugin.al.isVanished(p.getName())) + { + player.hidePlayer(plugin, p); + } + } + + for (Player p : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(p) && plugin.al.isVanished(player.getName())) + { + p.hidePlayer(plugin, player); + } + } + + if (plugin.al.isVanished(player.getName())) + { + plugin.esb.setVanished(player.getName(), true); + FLog.info(player.getName() + " joined while still vanished."); + plugin.al.messageAllAdmins(ChatColor.YELLOW + player.getName() + " has joined silently."); + event.setJoinMessage(null); + + new BukkitRunnable() + { + @Override + public void run() + { + if (!plugin.al.isVanished(player.getName())) + { + this.cancel(); + } + + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(ChatColor.GOLD + "You are hidden from other players.")); + } + }.runTaskTimer(plugin, 0L, 4L); + } + } + + @EventHandler + public void onPlayerLeave(PlayerQuitEvent event) + { + Player player = event.getPlayer(); + + if (plugin.al.isVanished(player.getName())) + { + event.setQuitMessage(null); + FLog.info(player.getName() + " left while still vanished."); + plugin.al.messageAllAdmins(ChatColor.YELLOW + player.getName() + " has left silently."); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLog.java b/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLog.java new file mode 100644 index 00000000..8c29fdac --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLog.java @@ -0,0 +1,212 @@ +package me.totalfreedom.totalfreedommod.admin; + +import com.google.common.collect.Maps; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.YamlConfig; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class ActivityLog extends FreedomService +{ + + public static final String FILENAME = "activitylog.yml"; + + private final Map allActivityLogs = Maps.newHashMap(); + private final Map nameTable = Maps.newHashMap(); + private final Map ipTable = Maps.newHashMap(); + + private final YamlConfig config; + + public ActivityLog() + { + this.config = new YamlConfig(plugin, FILENAME, true); + } + + public static String getFILENAME() + { + return FILENAME; + } + + @Override + public void onStart() + { + load(); + } + + @Override + public void onStop() + { + save(); + } + + public void load() + { + config.load(); + + allActivityLogs.clear(); + nameTable.clear(); + ipTable.clear(); + for (String key : config.getKeys(false)) + { + ConfigurationSection section = config.getConfigurationSection(key); + if (section == null) + { + FLog.warning("Invalid activity log format: " + key); + continue; + } + + ActivityLogEntry activityLogEntry = new ActivityLogEntry(key); + activityLogEntry.loadFrom(section); + + if (!activityLogEntry.isValid()) + { + FLog.warning("Could not load activity log: " + key + ". Missing details!"); + continue; + } + + allActivityLogs.put(key, activityLogEntry); + } + + updateTables(); + FLog.info("Loaded " + allActivityLogs.size() + " activity logs"); + } + + public void save() + { + // Clear the config + for (String key : config.getKeys(false)) + { + config.set(key, null); + } + + for (ActivityLogEntry activityLog : allActivityLogs.values()) + { + activityLog.saveTo(config.createSection(activityLog.getConfigKey())); + } + + config.save(); + } + + public ActivityLogEntry getActivityLog(CommandSender sender) + { + if (sender instanceof Player) + { + return getActivityLog((Player)sender); + } + + return getEntryByName(sender.getName()); + } + + public ActivityLogEntry getActivityLog(Player player) + { + ActivityLogEntry activityLog = getEntryByName(player.getName()); + if (activityLog == null) + { + String ip = FUtil.getIp(player); + activityLog = getEntryByIp(ip); + if (activityLog != null) + { + // Set the new username + activityLog.setName(player.getName()); + save(); + updateTables(); + } + else + { + activityLog = new ActivityLogEntry(player); + allActivityLogs.put(activityLog.getConfigKey(), activityLog); + updateTables(); + + activityLog.saveTo(config.createSection(activityLog.getConfigKey())); + config.save(); + } + } + String ip = FUtil.getIp(player); + if (!activityLog.getIps().contains(ip)) + { + activityLog.addIp(ip); + save(); + updateTables(); + } + return activityLog; + } + + public ActivityLogEntry getEntryByName(String name) + { + return nameTable.get(name.toLowerCase()); + } + + public ActivityLogEntry getEntryByIp(String ip) + { + return ipTable.get(ip); + } + + public void updateTables() + { + nameTable.clear(); + ipTable.clear(); + + for (ActivityLogEntry activityLog : allActivityLogs.values()) + { + nameTable.put(activityLog.getName().toLowerCase(), activityLog); + + for (String ip : activityLog.getIps()) + { + ipTable.put(ip, activityLog); + } + + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerJoin(PlayerJoinEvent event) + { + Player player = event.getPlayer(); + if (plugin.al.isAdmin(player)) + { + getActivityLog(event.getPlayer()).addLogin(); + plugin.acl.save(); + plugin.acl.updateTables(); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerQuit(PlayerQuitEvent event) + { + Player player = event.getPlayer(); + if (plugin.al.isAdmin(player)) + { + getActivityLog(event.getPlayer()).addLogout(); + plugin.acl.save(); + plugin.acl.updateTables(); + } + } + + public Map getAllActivityLogs() + { + return allActivityLogs; + } + + public Map getNameTable() + { + return nameTable; + } + + public Map getIpTable() + { + return ipTable; + } + + public YamlConfig getConfig() + { + return config; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLogEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLogEntry.java new file mode 100644 index 00000000..eb056fcb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/admin/ActivityLogEntry.java @@ -0,0 +1,176 @@ +package me.totalfreedom.totalfreedommod.admin; + +import com.google.common.collect.Lists; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import me.totalfreedom.totalfreedommod.config.IConfig; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.Validate; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +public class ActivityLogEntry implements IConfig +{ + + public static final String FILENAME = "activitylog.yml"; + private final List ips = Lists.newArrayList(); + private final List timestamps = Lists.newArrayList(); + private final List durations = Lists.newArrayList(); + private String configKey; + private String name; + + public ActivityLogEntry(Player player) + { + this.configKey = player.getName().toLowerCase(); + this.name = player.getName(); + } + + public ActivityLogEntry(String configKey) + { + this.configKey = configKey; + } + + public static String getFILENAME() + { + return FILENAME; + } + + public void loadFrom(Player player) + { + configKey = player.getName().toLowerCase(); + name = player.getName(); + } + + @Override + public void loadFrom(ConfigurationSection cs) + { + name = cs.getString("username", configKey); + ips.clear(); + ips.addAll(cs.getStringList("ips")); + timestamps.clear(); + timestamps.addAll(cs.getStringList("timestamps")); + durations.clear(); + durations.addAll(cs.getStringList("durations")); + } + + @Override + public void saveTo(ConfigurationSection cs) + { + Validate.isTrue(isValid(), "Could not save activity entry: " + name + ". Entry not valid!"); + cs.set("username", name); + cs.set("ips", Lists.newArrayList(ips)); + cs.set("timestamps", Lists.newArrayList(timestamps)); + cs.set("durations", Lists.newArrayList(durations)); + } + + public void addLogin() + { + Date currentTime = Date.from(Instant.now()); + timestamps.add("Login: " + FUtil.dateToString(currentTime)); + } + + public void addLogout() + { + // Fix of Array index out of bonds issue: FS-131 + String lastLoginString; + if(timestamps.size() > 1) + { + lastLoginString = timestamps.get(timestamps.size() - 1); + }else + { + lastLoginString = timestamps.get(0); + } + Date currentTime = Date.from(Instant.now()); + timestamps.add("Logout: " + FUtil.dateToString(currentTime)); + lastLoginString = lastLoginString.replace("Login: ", ""); + Date lastLogin = FUtil.stringToDate(lastLoginString); + + long duration = currentTime.getTime() - lastLogin.getTime(); + long seconds = duration / 1000 % 60; + long minutes = duration / (60 * 1000) % 60; + long hours = duration / (60 * 60 * 1000); + durations.add(hours + " hours, " + minutes + " minutes, and " + seconds + " seconds"); + } + + public void addIp(String ip) + { + if (!ips.contains(ip)) + { + ips.add(ip); + } + } + + public void addIps(List ips) + { + for (String ip : ips) + { + addIp(ip); + } + } + + public void removeIp(String ip) + { + ips.remove(ip); + } + + public void clearIPs() + { + ips.clear(); + } + + public int getTotalSecondsPlayed() + { + int result = 0; + for (String duration : durations) + { + String[] spl = duration.split(" "); + result += Integer.parseInt(spl[0]) * 60 * 60; + result += Integer.parseInt(spl[2]) * 60; + result += Integer.parseInt(spl[5]); + } + return result; + } + + @Override + public boolean isValid() + { + return configKey != null + && name != null; + } + + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public List getIps() + { + return ips; + } + + public List getTimestamps() + { + return timestamps; + } + + public List getDurations() + { + return durations; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java b/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java new file mode 100644 index 00000000..17920853 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/admin/Admin.java @@ -0,0 +1,235 @@ +package me.totalfreedom.totalfreedommod.admin; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.LogViewer.LogsRegistrationMode; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class Admin +{ + + + private final List ips = new ArrayList<>(); + private String name; + private boolean active = true; + private Rank rank = Rank.ADMIN; + private Date lastLogin = new Date(); + private Boolean commandSpy = false; + private Boolean potionSpy = false; + private String acFormat = null; + private String pteroID = null; + + public Admin(Player player) + { + this.name = player.getName(); + this.ips.add(FUtil.getIp(player)); + } + + public Admin(ResultSet resultSet) + { + try + { + this.name = resultSet.getString("username"); + this.active = resultSet.getBoolean("active"); + this.rank = Rank.findRank(resultSet.getString("rank")); + this.ips.clear(); + this.ips.addAll(FUtil.stringToList(resultSet.getString("ips"))); + this.lastLogin = new Date(resultSet.getLong("last_login")); + this.commandSpy = resultSet.getBoolean("command_spy"); + this.potionSpy = resultSet.getBoolean("potion_spy"); + this.acFormat = resultSet.getString("ac_format"); + this.pteroID = resultSet.getString("ptero_id"); + } + catch (SQLException e) + { + FLog.severe("Failed to load admin: " + e.getMessage()); + } + } + + @Override + public String toString() + { + final StringBuilder output = new StringBuilder(); + + output.append("Admin: ").append(name).append("\n") + .append("- IPs: ").append(StringUtils.join(ips, ", ")).append("\n") + .append("- Last Login: ").append(FUtil.dateToString(lastLogin)).append("\n") + .append("- Rank: ").append(rank.getName()).append("\n") + .append("- Is Active: ").append(active).append("\n") + .append("- Potion Spy: ").append(potionSpy).append("\n") + .append("- Admin Chat Format: ").append(acFormat).append("\n") + .append("- Pterodactyl ID: ").append(pteroID).append("\n"); + + return output.toString(); + } + + public Map toSQLStorable() + { + Map map = new HashMap() + {{ + put("username", name); + put("active", active); + put("rank", rank.toString()); + put("ips", FUtil.listToString(ips)); + put("last_login", lastLogin.getTime()); + put("command_spy", commandSpy); + put("potion_spy", potionSpy); + put("ac_format", acFormat); + put("ptero_id", pteroID); + }}; + return map; + } + + // Util IP methods + public void addIp(String ip) + { + if (!ips.contains(ip)) + { + ips.add(ip); + } + } + + public void addIps(List ips) + { + for (String ip : ips) + { + addIp(ip); + } + } + + public void removeIp(String ip) + { + ips.remove(ip); + } + + public void clearIPs() + { + ips.clear(); + } + + public boolean isValid() + { + return name != null + && rank != null + && !ips.isEmpty() + && lastLogin != null; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public boolean isActive() + { + return active; + } + + public void setActive(boolean active) + { + this.active = active; + + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + + // Avoiding stupid NPE compiler warnings + if (plugin == null) + { + Bukkit.getLogger().severe("The plugin is null!! This is a major issue and WILL break the plugin!"); + return; + } + + if (!active) + { + if (getRank().isAtLeast(Rank.ADMIN)) + { + if (plugin.btb != null) + { + plugin.btb.killTelnetSessions(getName()); + } + } + + plugin.lv.updateLogsRegistration(null, getName(), LogsRegistrationMode.DELETE); + } + } + + public Rank getRank() + { + return rank; + } + + public void setRank(Rank rank) + { + this.rank = rank; + } + + public List getIps() + { + return ips; + } + + public Date getLastLogin() + { + return lastLogin; + } + + public void setLastLogin(Date lastLogin) + { + this.lastLogin = lastLogin; + } + + public Boolean getCommandSpy() + { + return commandSpy; + } + + public void setCommandSpy(Boolean commandSpy) + { + this.commandSpy = commandSpy; + } + + public Boolean getPotionSpy() + { + return potionSpy; + } + + public void setPotionSpy(Boolean potionSpy) + { + this.potionSpy = potionSpy; + } + + public String getAcFormat() + { + return acFormat; + } + + public void setAcFormat(String acFormat) + { + this.acFormat = acFormat; + } + + public String getPteroID() + { + return pteroID; + } + + public void setPteroID(String pteroID) + { + this.pteroID = pteroID; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/admin/AdminList.java b/src/main/java/me/totalfreedom/totalfreedommod/admin/AdminList.java new file mode 100644 index 00000000..8cbb49b8 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/admin/AdminList.java @@ -0,0 +1,409 @@ +package me.totalfreedom.totalfreedommod.admin; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class AdminList extends FreedomService +{ + public static final List vanished = new ArrayList<>(); + public final Map> verifiedNoAdmin = Maps.newHashMap(); + private final Set allAdmins = Sets.newHashSet(); // Includes disabled admins + // Only active admins below + private final Set activeAdmins = Sets.newHashSet(); + private final Map nameTable = Maps.newHashMap(); + private final Map ipTable = Maps.newHashMap(); + + public static List getVanished() + { + return vanished; + } + + @Override + public void onStart() + { + load(); + deactivateOldEntries(false); + } + + @Override + public void onStop() + { + } + + public void load() + { + allAdmins.clear(); + try + { + ResultSet adminSet = plugin.sql.getAdminList(); + { + while (adminSet.next()) + { + Admin admin = new Admin(adminSet); + allAdmins.add(admin); + } + } + } + catch (SQLException e) + { + FLog.severe("Failed to load admin list: " + e.getMessage()); + } + + updateTables(); + FLog.info("Loaded " + allAdmins.size() + " admins (" + nameTable.size() + " active, " + ipTable.size() + " IPs)"); + } + + public void messageAllAdmins(String message) + { + for (Player player : server.getOnlinePlayers()) + { + if (isAdmin(player)) + { + player.sendMessage(message); + } + } + } + + public void potionSpyMessage(String message) + { + for (Player player : server.getOnlinePlayers()) + { + Admin admin = getAdmin(player.getPlayer()); + if (isAdmin(player) && admin.getPotionSpy()) + { + player.sendMessage(message); + } + } + } + + public synchronized boolean isAdminSync(CommandSender sender) + { + return isAdmin(sender); + } + + public List getActiveAdminNames() + { + List names = new ArrayList(); + for (Admin admin : activeAdmins) + { + names.add(admin.getName()); + } + return names; + } + + public boolean isAdmin(CommandSender sender) + { + if (!(sender instanceof Player)) + { + return true; + } + + Admin admin = getAdmin((Player)sender); + + return admin != null && admin.isActive(); + } + + public boolean isAdmin(Player player) + { + if (player == null) + { + return true; + } + + Admin admin = getAdmin(player); + + return admin != null && admin.isActive(); + } + + public boolean isSeniorAdmin(CommandSender sender) + { + Admin admin = getAdmin(sender); + if (admin == null) + { + return false; + } + + return admin.getRank().ordinal() >= Rank.SENIOR_ADMIN.ordinal(); + } + + public Admin getAdmin(CommandSender sender) + { + if (sender instanceof Player) + { + return getAdmin((Player)sender); + } + + return getEntryByName(sender.getName()); + } + + public Admin getAdmin(Player player) + { + // Find admin + String ip = FUtil.getIp(player); + Admin admin = getEntryByName(player.getName()); + + // Admin by name + if (admin != null) + { + // Check if we're in online mode, + // Or the players IP is in the admin entry + if (Bukkit.getOnlineMode() || admin.getIps().contains(ip)) + { + if (!admin.getIps().contains(ip)) + { + // Add the new IP if we have to + admin.addIp(ip); + save(admin); + updateTables(); + } + return admin; + } + } + + // Admin by ip + admin = getEntryByIp(ip); + if (admin != null) + { + // Set the new username + String oldName = admin.getName(); + admin.setName(player.getName()); + plugin.sql.updateAdminName(oldName, admin.getName()); + updateTables(); + } + + return null; + } + + public Admin getEntryByName(String name) + { + return nameTable.get(name.toLowerCase()); + } + + public Admin getEntryByIp(String ip) + { + return ipTable.get(ip); + } + + public Admin getEntryByIpFuzzy(String needleIp) + { + final Admin directAdmin = getEntryByIp(needleIp); + if (directAdmin != null) + { + return directAdmin; + } + + for (String ip : ipTable.keySet()) + { + if (FUtil.fuzzyIpMatch(needleIp, ip, 3)) + { + return ipTable.get(ip); + } + } + + return null; + } + + public void updateLastLogin(Player player) + { + final Admin admin = getAdmin(player); + if (admin == null) + { + return; + } + + admin.setLastLogin(new Date()); + admin.setName(player.getName()); + save(admin); + } + + public boolean isAdminImpostor(Player player) + { + return getEntryByName(player.getName()) != null && !isAdmin(player) && !isVerifiedAdmin(player); + } + + public boolean isVerifiedAdmin(Player player) + { + return verifiedNoAdmin.containsKey(player.getName()) && verifiedNoAdmin.get(player.getName()).contains(FUtil.getIp(player)); + } + + public boolean isIdentityMatched(Player player) + { + if (Bukkit.getOnlineMode()) + { + return true; + } + + Admin admin = getAdmin(player); + return admin != null && admin.getName().equalsIgnoreCase(player.getName()); + } + + public boolean addAdmin(Admin admin) + { + if (!admin.isValid()) + { + FLog.warning("Could not add admin: " + admin.getName() + ". Admin is missing details!"); + return false; + } + + // Store admin, update views + allAdmins.add(admin); + updateTables(); + + // Save admin + plugin.sql.addAdmin(admin); + + return true; + } + + public boolean removeAdmin(Admin admin) + { + if (admin.getRank().isAtLeast(Rank.ADMIN)) + { + if (plugin.btb != null) + { + plugin.btb.killTelnetSessions(admin.getName()); + } + } + + // Remove admin, update views + if (!allAdmins.remove(admin)) + { + return false; + } + updateTables(); + + // Unsave admin + plugin.sql.removeAdmin(admin); + + return true; + } + + public void updateTables() + { + activeAdmins.clear(); + nameTable.clear(); + ipTable.clear(); + + for (Admin admin : allAdmins) + { + if (!admin.isActive()) + { + continue; + } + + activeAdmins.add(admin); + nameTable.put(admin.getName().toLowerCase(), admin); + + for (String ip : admin.getIps()) + { + ipTable.put(ip, admin); + } + + } + } + + public Set getAdminNames() + { + return nameTable.keySet(); + } + + public Set getAdminIps() + { + return ipTable.keySet(); + } + + public void save(Admin admin) + { + try + { + ResultSet currentSave = plugin.sql.getAdminByName(admin.getName()); + for (Map.Entry entry : admin.toSQLStorable().entrySet()) + { + Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue()); + if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null) + { + plugin.sql.setAdminValue(admin, entry.getKey(), entry.getValue()); + } + } + } + catch (SQLException e) + { + FLog.severe("Failed to save admin: " + e.getMessage()); + } + } + + public void deactivateOldEntries(boolean verbose) + { + for (Admin admin : allAdmins) + { + if (!admin.isActive() || admin.getRank().isAtLeast(Rank.SENIOR_ADMIN)) + { + continue; + } + + final Date lastLogin = admin.getLastLogin(); + final long lastLoginHours = TimeUnit.HOURS.convert(new Date().getTime() - lastLogin.getTime(), TimeUnit.MILLISECONDS); + + if (lastLoginHours < ConfigEntry.ADMINLIST_CLEAN_THESHOLD_HOURS.getInteger()) + { + continue; + } + + if (verbose) + { + FUtil.adminAction("TotalFreedomMod", "Deactivating admin " + admin.getName() + ", inactive for " + lastLoginHours + " hours", true); + } + + admin.setActive(false); + save(admin); + } + + updateTables(); + } + + public boolean isVanished(String player) + { + return vanished.contains(player); + } + + public Set getAllAdmins() + { + return allAdmins; + } + + public Set getActiveAdmins() + { + return activeAdmins; + } + + public Map getNameTable() + { + return nameTable; + } + + public Map getIpTable() + { + return ipTable; + } + + public Map> getVerifiedNoAdmin() + { + return verifiedNoAdmin; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/banning/Ban.java b/src/main/java/me/totalfreedom/totalfreedommod/banning/Ban.java new file mode 100644 index 00000000..42871f45 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/banning/Ban.java @@ -0,0 +1,323 @@ +package me.totalfreedom.totalfreedommod.banning; + +import com.google.common.collect.Lists; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class Ban +{ + + public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); + private final List ips = Lists.newArrayList(); + private String username = null; + private UUID uuid = null; + private String by = null; + + + private Date at = null; + + + private String reason = null; // Unformatted, &[0-9,a-f] instead of ChatColor + + + private long expiryUnix = -1; + + public Ban() + { + } + + public Ban(String username, UUID uuid, String ip, String by, Date at, Date expire, String reason) + { + this(username, + uuid, + Collections.singletonList(ip), + by, + at, + expire, + reason); + } + + public Ban(String username, UUID uuid, List ips, String by, Date at, Date expire, String reason) + { + this.username = username; + this.uuid = uuid; + if (ips != null) + { + this.ips.addAll(ips); + } + dedupeIps(); + this.by = by; + this.at = at; + if (expire == null) + { + expire = FUtil.parseDateOffset("24h"); + } + this.expiryUnix = FUtil.getUnixTime(expire); + this.reason = reason; + } + + // + // For player IP + public static Ban forPlayerIp(Player player, CommandSender by) + { + return forPlayerIp(player, by, null, null); + } + + public static Ban forPlayerIp(Player player, CommandSender by, Date expiry, String reason) + { + return new Ban(null, null, Collections.singletonList(FUtil.getIp(player)), by.getName(), Date.from(Instant.now()), expiry, reason); + } + + public static Ban forPlayerIp(String ip, CommandSender by, Date expiry, String reason) + { + return new Ban(null, null, ip, by.getName(), Date.from(Instant.now()), expiry, reason); + } + + // + // For player name + public static Ban forPlayerName(Player player, CommandSender by, Date expiry, String reason) + { + return forPlayerName(player.getName(), by, expiry, reason); + } + + public static Ban forPlayerName(String player, CommandSender by, Date expiry, String reason) + { + return new Ban(player, + null, + new ArrayList<>(), + by.getName(), + Date.from(Instant.now()), + expiry, + reason); + } + + // + // For player + public static Ban forPlayer(Player player, CommandSender by) + { + return forPlayerName(player, by, null, null); + } + + public static Ban forPlayer(Player player, CommandSender by, Date expiry, String reason) + { + return new Ban(player.getName(), + player.getUniqueId(), + FUtil.getIp(player), + by.getName(), + Date.from(Instant.now()), + expiry, + reason); + } + + public static Ban forPlayerFuzzy(Player player, CommandSender by, Date expiry, String reason) + { + return new Ban(player.getName(), + player.getUniqueId(), + FUtil.getFuzzyIp(FUtil.getIp(player)), + by.getName(), + Date.from(Instant.now()), + expiry, + reason); + } + + public static SimpleDateFormat getDateFormat() + { + return DATE_FORMAT; + } + + public boolean hasUsername() + { + return username != null && !username.isEmpty(); + } + + public boolean hasUUID() + { + return uuid != null; + } + + public boolean addIp(String ip) + { + return ips.add(ip); + } + + public boolean removeIp(String ip) + { + return ips.remove(ip); + } + + public boolean hasIps() + { + return !ips.isEmpty(); + } + + public boolean hasExpiry() + { + return expiryUnix > 0; + } + + public boolean isExpired() + { + return hasExpiry() && FUtil.getUnixDate(expiryUnix).before(new Date(FUtil.getUnixTime())); + } + + public String bakeKickMessage() + { + final StringBuilder message = new StringBuilder(ChatColor.GOLD + "You"); + + if (!hasUsername()) + { + message.append("r IP address is"); + } + else if (!hasIps()) + { + message.append("r username is"); + } + else + { + message.append(" are"); + } + + message.append(" temporarily banned from this server."); + message.append("\nAppeal at ").append(ChatColor.BLUE) + .append(ConfigEntry.SERVER_BAN_URL.getString()); + + if (reason != null) + { + message.append("\n").append(ChatColor.RED).append("Reason: ").append(ChatColor.GOLD) + .append(ChatColor.translateAlternateColorCodes('&', reason)); + } + + if (by != null) + { + message.append("\n").append(ChatColor.RED).append("Banned by: ").append(ChatColor.GOLD) + .append(by); + } + + if (at != null) + { + message.append("\n").append(ChatColor.RED).append("Issued: ").append(ChatColor.GOLD) + .append(DATE_FORMAT.format(at)); + } + + if (getExpiryUnix() != 0) + { + message.append("\n").append(ChatColor.RED).append("Expires: ").append(ChatColor.GOLD) + .append(DATE_FORMAT.format(FUtil.getUnixDate(expiryUnix))); + } + + return message.toString(); + } + + @Override + public boolean equals(Object object) + { + if (object == null) + { + return false; + } + + if (!(object instanceof Ban)) + { + return false; + } + + final Ban ban = (Ban)object; + if (hasIps() != ban.hasIps() + || hasUsername() != ban.hasUsername()) + { + return false; + } + + if (hasIps() && !(getIps().equals(ban.getIps()))) + { + return false; + } + + return !(hasUsername() && !(getUsername().equalsIgnoreCase(ban.getUsername()))); + } + + private void dedupeIps() + { + Set uniqueIps = new HashSet<>(); + + //Fancy Collections.removeIf lets you do all that while loop work in one lambda. + ips.removeIf(s -> !uniqueIps.add(s)); + } + + public List getIps() + { + return ips; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public UUID getUuid() + { + return uuid; + } + + public void setUuid(UUID uuid) + { + this.uuid = uuid; + } + + public String getBy() + { + return by; + } + + public void setBy(String by) + { + this.by = by; + } + + public Date getAt() + { + return at; + } + + public void setAt(Date at) + { + this.at = at; + } + + public String getReason() + { + return reason; + } + + public void setReason(String reason) + { + this.reason = reason; + } + + public long getExpiryUnix() + { + return expiryUnix; + } + + public void setExpiryUnix(long expiryUnix) + { + this.expiryUnix = expiryUnix; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/banning/BanManager.java b/src/main/java/me/totalfreedom/totalfreedommod/banning/BanManager.java new file mode 100644 index 00000000..03b6465a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/banning/BanManager.java @@ -0,0 +1,325 @@ +package me.totalfreedom.totalfreedommod.banning; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; + +public class BanManager extends FreedomService +{ + + private final Set bans = Sets.newHashSet(); + private final Map nameBans = Maps.newHashMap(); + private final Map uuidBans = Maps.newHashMap(); + private final Map ipBans = Maps.newHashMap(); + private final List unbannableUsernames = Lists.newArrayList(); + + // + + @Override + public void onStart() + { + bans.clear(); + try + { + ResultSet banSet = plugin.sql.getBanList(); + { + while (banSet.next()) + { + String name = banSet.getString("name"); + UUID uuid = null; + String strUUID = banSet.getString("uuid"); + if (strUUID != null) + { + uuid = UUID.fromString(strUUID); + } + List ips = FUtil.stringToList(banSet.getString("ips")); + String by = banSet.getString("by"); + Date at = new Date(banSet.getLong("at")); + Date expires = new Date(banSet.getLong("expires")); + String reason = banSet.getString("reason"); + Ban ban = new Ban(name, uuid, ips, by, at, expires, reason); + bans.add(ban); + } + } + } + catch (SQLException e) + { + FLog.severe("Failed to load ban list: " + e.getMessage()); + } + + // Remove expired bans, repopulate ipBans and nameBans, + updateViews(); + + FLog.info("Loaded " + ipBans.size() + " IP bans and " + nameBans.size() + " username bans."); + + // Load unbannable usernames + unbannableUsernames.clear(); + unbannableUsernames.addAll(ConfigEntry.FAMOUS_PLAYERS.getStringList()); + FLog.info("Loaded " + unbannableUsernames.size() + " unbannable usernames."); + } + + @Override + public void onStop() + { + } + + public Set getAllBans() + { + return Collections.unmodifiableSet(bans); + } + + public Collection getIpBans() + { + return Collections.unmodifiableCollection(ipBans.values()); + } + + public Collection getUsernameBans() + { + return Collections.unmodifiableCollection(nameBans.values()); + } + + public Ban getByIp(String ip) + { + final Ban directBan = ipBans.get(ip); + if (directBan != null && !directBan.isExpired()) + { + return directBan; + } + + // Match fuzzy IP + for (Ban loopBan : ipBans.values()) + { + if (loopBan.isExpired()) + { + continue; + } + + for (String loopIp : loopBan.getIps()) + { + if (!loopIp.contains("*")) + { + continue; + } + + if (FUtil.fuzzyIpMatch(ip, loopIp, 4)) + { + return loopBan; + } + } + } + + return null; + } + + public Ban getByUsername(String username) + { + username = username.toLowerCase(); + final Ban directBan = nameBans.get(username); + + if (directBan != null && !directBan.isExpired()) + { + return directBan; + } + + return null; + } + + public Ban getByUUID(UUID uuid) + { + final Ban directBan = uuidBans.get(uuid); + + if (directBan != null && !directBan.isExpired()) + { + return directBan; + } + + return null; + } + + public Ban unbanIp(String ip) + { + final Ban ban = getByIp(ip); + + if (ban != null) + { + bans.remove(ban); + } + + return ban; + } + + public Ban unbanUsername(String username) + { + final Ban ban = getByUsername(username); + + if (ban != null) + { + bans.remove(ban); + } + + return ban; + } + + public boolean isIpBanned(String ip) + { + return getByIp(ip) != null; + } + + public boolean isUsernameBanned(String username) + { + return getByUsername(username) != null; + } + + public void addBan(Ban ban) + { + if (ban.getUsername() != null && getByUsername(ban.getUsername()) != null) + { + removeBan(ban); + } + else + { + + for (String ip : ban.getIps()) + { + if (getByIp(ip) != null) + { + removeBan(ban); + break; + } + } + } + + if (bans.add(ban)) + { + plugin.sql.addBan(ban); + updateViews(); + } + + } + + public void removeBan(Ban ban) + { + if (bans.remove(ban)) + { + plugin.sql.removeBan(ban); + updateViews(); + } + + } + + public int purge() + { + int size = bans.size(); + bans.clear(); + updateViews(); + plugin.sql.truncate("bans"); + return size; + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerLogin(PlayerLoginEvent event) + { + final String username = event.getPlayer().getName(); + final UUID uuid = event.getPlayer().getUniqueId(); + final String ip = FUtil.getIp(event); + + // Regular ban + Ban ban = getByUsername(username); + if (ban == null) + { + ban = getByUUID(uuid); + + if (ban == null) + { + ban = getByIp(ip); + } + } + + if (ban != null && !ban.isExpired()) + { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, ban.bakeKickMessage()); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + + if (!plugin.al.isAdmin(player)) + { + return; + } + + // Unban admins + Ban ban = getByUsername(player.getName()); + if (ban != null) + { + removeBan(ban); + } + else + { + ban = getByIp(FUtil.getIp(player)); + if (ban != null) + { + removeBan(ban); + } + } + } + + private void updateViews() + { + // Remove expired bans + for (Ban ban : new ArrayList<>(bans)) + { + if (ban.isExpired()) + { + bans.remove(ban); + plugin.sql.removeBan(ban); + } + } + + nameBans.clear(); + uuidBans.clear(); + ipBans.clear(); + for (Ban ban : bans) + { + if (ban.hasUsername()) + { + nameBans.put(ban.getUsername().toLowerCase(), ban); + } + + if (ban.hasUUID()) + { + uuidBans.put(ban.getUuid(), ban); + } + + if (ban.hasIps()) + { + for (String ip : ban.getIps()) + { + ipBans.put(ip, ban); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBan.java b/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBan.java new file mode 100644 index 00000000..8f9eb229 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBan.java @@ -0,0 +1,88 @@ +package me.totalfreedom.totalfreedommod.banning; + +import com.google.common.collect.Lists; +import java.util.List; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.config.IConfig; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.configuration.ConfigurationSection; + +public class IndefiniteBan implements IConfig +{ + private final List ips = Lists.newArrayList(); + private String username = null; + private UUID uuid = null; + private String reason = null; + + public IndefiniteBan() + { + } + + @Override + public void loadFrom(ConfigurationSection cs) + { + this.username = cs.getName(); + try + { + String strUUID = cs.getString("uuid", null); + if (strUUID != null) + { + UUID uuid = UUID.fromString(strUUID); + this.uuid = uuid; + } + } + catch (IllegalArgumentException e) + { + FLog.warning("Failed to load indefinite banned UUID for " + this.username + ". Make sure the UUID is in the correct format with dashes."); + } + this.ips.clear(); + this.ips.addAll(cs.getStringList("ips")); + this.reason = cs.getString("reason", null); + } + + @Override + public void saveTo(ConfigurationSection cs) + { + } + + @Override + public boolean isValid() + { + return username != null; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public UUID getUuid() + { + return uuid; + } + + public void setUuid(UUID uuid) + { + this.uuid = uuid; + } + + public List getIps() + { + return ips; + } + + public String getReason() + { + return reason; + } + + public void setReason(String reason) + { + this.reason = reason; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBanList.java b/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBanList.java new file mode 100644 index 00000000..0ea5b73b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/banning/IndefiniteBanList.java @@ -0,0 +1,165 @@ +package me.totalfreedom.totalfreedommod.banning; + +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +import java.util.Set; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.config.YamlConfig; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerLoginEvent; + +public class IndefiniteBanList extends FreedomService +{ + + public static final String CONFIG_FILENAME = "indefinitebans.yml"; + + + private final Set indefBans = Sets.newHashSet(); + + + private int nameBanCount = 0; + + + private int uuidBanCount = 0; + + + private int ipBanCount = 0; + + public static String getConfigFilename() + { + return CONFIG_FILENAME; + } + + @Override + public void onStart() + { + indefBans.clear(); + + final YamlConfig config = new YamlConfig(plugin, CONFIG_FILENAME, true); + config.load(); + + for (String name : config.getKeys(false)) + { + if (!config.isConfigurationSection(name)) + { + FLog.warning("Could not load indefinite ban for " + name + ": Invalid format!"); + continue; + } + + IndefiniteBan indefBan = new IndefiniteBan(); + ConfigurationSection cs = config.getConfigurationSection(name); + assert cs != null; + indefBan.loadFrom(cs); + + if (!indefBan.isValid()) + { + FLog.warning("Not adding indefinite ban for " + name + ": Missing information."); + continue; + } + + indefBans.add(indefBan); + } + + updateCount(); + + FLog.info("Loaded " + nameBanCount + " indefinite name bans, " + uuidBanCount + " UUID bans, and " + ipBanCount + " ip bans"); + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerLogin(PlayerLoginEvent event) + { + final String username = event.getPlayer().getName(); + final UUID uuid = event.getPlayer().getUniqueId(); + final String ip = FUtil.getIp(event); + + String bannedBy = ""; + IndefiniteBan ban = null; + + for (IndefiniteBan indefBan : indefBans) + { + if (username.equalsIgnoreCase(indefBan.getUsername())) + { + bannedBy = "username"; + ban = indefBan; + break; + } + else if (indefBan.getUuid() != null && indefBan.getUuid().equals(uuid)) + { + bannedBy = "UUID"; + ban = indefBan; + break; + } + else if (indefBan.getIps().contains(ip)) + { + bannedBy = "IP address"; + ban = indefBan; + break; + } + } + + if (ban != null) + { + String kickMessage = ChatColor.RED + "Your " + bannedBy + " is indefinitely banned from this server."; + String reason = ban.getReason(); + if (!Strings.isNullOrEmpty(reason)) + { + kickMessage += "\nReason: " + ChatColor.GOLD + reason; + } + String appealURL = ConfigEntry.SERVER_INDEFBAN_URL.getString(); + if (!Strings.isNullOrEmpty(appealURL)) + { + kickMessage += ChatColor.RED + "\n\nRelease procedures are available at\n" + ChatColor.GOLD + ConfigEntry.SERVER_INDEFBAN_URL.getString(); + } + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, kickMessage); + } + } + + private void updateCount() + { + nameBanCount = 0; + uuidBanCount = 0; + ipBanCount = 0; + + for (IndefiniteBan indefBan : indefBans) + { + nameBanCount += 1; + if (indefBan.getUuid() != null) + { + uuidBanCount += 1; + } + ipBanCount += indefBan.getIps().size(); + } + } + + public Set getIndefBans() + { + return indefBans; + } + + public int getNameBanCount() + { + return nameBanCount; + } + + public int getUuidBanCount() + { + return uuidBanCount; + } + + public int getIpBanCount() + { + return ipBanCount; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/BlockBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/BlockBlocker.java new file mode 100644 index 00000000..db80dff9 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/BlockBlocker.java @@ -0,0 +1,188 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import java.util.List; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.Banner; +import org.bukkit.block.Skull; +import org.bukkit.block.banner.Pattern; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +public class BlockBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.HIGH) + public void onBlockPlace(BlockPlaceEvent event) + { + final Player player = event.getPlayer(); + + switch (event.getBlockPlaced().getType()) + { + case LAVA: + { + if (!ConfigEntry.ALLOW_LAVA_PLACE.getBoolean()) + { + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + player.sendMessage(ChatColor.GRAY + "Lava placement is currently disabled."); + event.setCancelled(true); + } + break; + } + case WATER: + { + if (!ConfigEntry.ALLOW_WATER_PLACE.getBoolean()) + { + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + player.sendMessage(ChatColor.GRAY + "Water placement is currently disabled."); + + event.setCancelled(true); + } + break; + } + case FIRE: + case SOUL_FIRE: + { + if (!ConfigEntry.ALLOW_FIRE_PLACE.getBoolean()) + { + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + player.sendMessage(ChatColor.GRAY + "Fire placement is currently disabled."); + event.setCancelled(true); + } + break; + } + case STRUCTURE_BLOCK: + { + if (!ConfigEntry.ALLOW_STRUCTURE_BLOCKS.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Structure blocks are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case JIGSAW: + { + if (!ConfigEntry.ALLOW_JIGSAWS.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Jigsaws are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case GRINDSTONE: + { + if (!ConfigEntry.ALLOW_GRINDSTONES.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Grindstones are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case JUKEBOX: + { + if (!ConfigEntry.ALLOW_JUKEBOXES.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Jukeboxes are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case SPAWNER: + { + if (!ConfigEntry.ALLOW_SPAWNERS.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Spawners are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case BEEHIVE: + case BEE_NEST: + { + if (!ConfigEntry.ALLOW_BEEHIVES.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Beehives are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + { + Skull skull = (Skull)event.getBlockPlaced().getState(); + if (skull.getOwner() != null) + { + if (skull.getOwner().contains("\u00A7")) + { + skull.setOwner(skull.getOwner().replace("\u00A7", "")); + SkullMeta meta = (SkullMeta)event.getItemInHand().getItemMeta(); + if (meta != null) + { + ItemStack newHead = new ItemStack(Material.PLAYER_HEAD, 1); + ItemMeta headMeta = newHead.getItemMeta(); + assert headMeta != null; + headMeta.setDisplayName(ChatColor.YELLOW + "C-sectioned Head"); + newHead.setItemMeta(headMeta); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), newHead); + player.sendMessage(ChatColor.GRAY + "The player head you are attempting to place has a section symbol. Your player head has been C-sectioned."); + event.setCancelled(true); + } + } + if (skull.getOwner().length() > 100) + { + player.sendMessage(ChatColor.GRAY + "Instead of using Pi to crash players, be useful with your life and use it to discover things."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + } + break; + } + case RESPAWN_ANCHOR: + { + if (!ConfigEntry.ALLOW_RESPAWN_ANCHORS.getBoolean()) + { + player.sendMessage(ChatColor.GRAY + "Respawn anchors are disabled."); + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + event.setCancelled(true); + } + break; + } + } + + if (Groups.BANNERS.contains(event.getBlockPlaced().getType())) + { + Banner banner = (Banner)event.getBlockPlaced().getState(); + List patterns = banner.getPatterns(); + + if (patterns.size() >= 2) + { + banner.setPatterns(patterns.subList(0, 2)); + player.sendMessage(ChatColor.GRAY + "Your banner had too many patterns on it, so some were removed."); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/EditBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/EditBlocker.java new file mode 100644 index 00000000..06c5585f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/EditBlocker.java @@ -0,0 +1,61 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FSync; +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +public class EditBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.LOW) + public void onBlockPlace(BlockPlaceEvent event) + { + FPlayer fPlayer = plugin.pl.getPlayerSync(event.getPlayer()); + if (!fPlayer.isEditBlocked()) + { + return; + } + + if (plugin.al.isAdminSync(event.getPlayer())) + { + fPlayer.setEditBlocked(false); + return; + } + + FSync.playerMsg(event.getPlayer(), ChatColor.RED + "Your ability to place blocks has been disabled!"); + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.LOW) + public void onBlockBreak(BlockBreakEvent event) + { + FPlayer fPlayer = plugin.pl.getPlayerSync(event.getPlayer()); + if (!fPlayer.isEditBlocked()) + { + return; + } + + if (plugin.al.isAdminSync(event.getPlayer())) + { + fPlayer.setEditBlocked(false); + return; + } + + FSync.playerMsg(event.getPlayer(), ChatColor.RED + "Your ability to destroy blocks has been disabled!"); + event.setCancelled(true); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/EventBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/EventBlocker.java new file mode 100644 index 00000000..70ed87f3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/EventBlocker.java @@ -0,0 +1,268 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.block.data.AnaloguePowerable; +import org.bukkit.block.data.Powerable; +import org.bukkit.entity.Entity; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Tameable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockDispenseEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockGrowEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.LeavesDecayEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.event.entity.FireworkExplodeEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerRespawnEvent; + +public class EventBlocker extends FreedomService +{ + /** + * /@EventHandler(priority = EventPriority.HIGH) + * /public void onBlockRedstone(BlockRedstoneEvent event) + * /{ + * / if (!ConfigEntry.ALLOW_REDSTONE.getBoolean()) + * / { + * / event.setNewCurrent(0); + * / } + * /} + **/ + + // TODO: Revert back to old redstone block system when (or if) it is fixed in Bukkit, Spigot or Paper. + private final ArrayList redstoneBlocks = new ArrayList<>(Arrays.asList(Material.REDSTONE, Material.DISPENSER, Material.DROPPER, Material.REDSTONE_LAMP)); + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockBurn(BlockBurnEvent event) + { + if (!ConfigEntry.ALLOW_FIRE_SPREAD.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockIgnite(BlockIgniteEvent event) + { + if (!ConfigEntry.ALLOW_FIRE_PLACE.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockFromTo(BlockFromToEvent event) + { + if (!ConfigEntry.ALLOW_FLUID_SPREAD.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityExplode(EntityExplodeEvent event) + { + if (!ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + event.blockList().clear(); + return; + } + + event.setYield(0.0F); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onExplosionPrime(ExplosionPrimeEvent event) + { + if (!ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + event.setCancelled(true); + return; + } + + event.setRadius(ConfigEntry.EXPLOSIVE_RADIUS.getDouble().floatValue()); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityCombust(EntityCombustEvent event) + { + if (!ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityDeath(EntityDeathEvent event) + { + if (ConfigEntry.AUTO_ENTITY_WIPE.getBoolean()) + { + event.setDroppedExp(0); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityDamage(EntityDamageEvent event) + { + if ((event.getCause() == EntityDamageEvent.DamageCause.LAVA) + && !ConfigEntry.ALLOW_LAVA_DAMAGE.getBoolean()) + { + event.setCancelled(true); + return; + } + + if (ConfigEntry.ENABLE_PET_PROTECT.getBoolean()) + { + Entity entity = event.getEntity(); + if (entity instanceof Tameable) + { + if (((Tameable)entity).isTamed()) + { + event.setCancelled(true); + } + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerDropItem(PlayerDropItemEvent event) + { + if (!plugin.al.isAdmin(event.getPlayer())) + { + event.setCancelled(true); + } + + if (!ConfigEntry.AUTO_ENTITY_WIPE.getBoolean()) + { + return; + } + + if (event.getPlayer().getWorld().getEntities().size() > 750) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onLeavesDecay(LeavesDecayEvent event) + { + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onBlockGrowth(BlockGrowEvent event) + { + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onFireworkExplode(FireworkExplodeEvent event) + { + if (!ConfigEntry.ALLOW_FIREWORK_EXPLOSION.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockPistonRetract(BlockPistonRetractEvent event) + { + if (!ConfigEntry.ALLOW_REDSTONE.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockPistonExtend(BlockPistonExtendEvent event) + { + if (!ConfigEntry.ALLOW_REDSTONE.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) + { + if (!ConfigEntry.ALLOW_GRAVITY.getBoolean() && event.getEntity() instanceof FallingBlock) + { + event.setCancelled(true); + } + } + + @EventHandler + public void onBlockPhysics(BlockPhysicsEvent event) + { + if (!ConfigEntry.ALLOW_REDSTONE.getBoolean()) + { + // Check if the block is involved with redstone. + if (event.getBlock().getBlockData() instanceof AnaloguePowerable || event.getBlock().getBlockData() instanceof Powerable || redstoneBlocks.contains(event.getBlock().getType())) + { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerRespawn(PlayerRespawnEvent event) + { + double maxHealth = Objects.requireNonNull(event.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH)).getValue(); + if (maxHealth < 1) + { + for (AttributeModifier attributeModifier : Objects.requireNonNull(event.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH)).getModifiers()) + { + Objects.requireNonNull(event.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH)).removeModifier(attributeModifier); + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onBlockDispense(BlockDispenseEvent event) + { + List banned = Arrays.asList(Material.TNT_MINECART, Material.MINECART); + if (Groups.SPAWN_EGGS.contains(event.getItem().getType()) || banned.contains(event.getItem().getType())) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerDeath(PlayerDeathEvent event) + { + FUtil.fixCommandVoid(event.getEntity()); + event.setDeathMessage(event.getDeathMessage()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/InteractBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/InteractBlocker.java new file mode 100644 index 00000000..c8f6efb6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/InteractBlocker.java @@ -0,0 +1,163 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +public class InteractBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerInteract(PlayerInteractEvent event) + { + switch (event.getAction()) + { + case RIGHT_CLICK_AIR: + case RIGHT_CLICK_BLOCK: + { + handleRightClick(event); + break; + } + + case LEFT_CLICK_AIR: + case LEFT_CLICK_BLOCK: + { + // + break; + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onRightClickBell(PlayerInteractEvent event) + { + if (event.getClickedBlock() != null && event.getClickedBlock().getType().equals(Material.BELL) && !ConfigEntry.ALLOW_BELLS.getBoolean()) + { + event.setCancelled(true); + } + } + + @EventHandler + public void onBedEnter(PlayerBedEnterEvent event) + { + Player player = event.getPlayer(); + if (Groups.EXPLOSIVE_BED_BIOMES.contains(event.getBed().getBiome())) + { + player.sendMessage(ChatColor.RED + "You may not sleep here."); + event.setCancelled(true); + } + } + + private void handleRightClick(PlayerInteractEvent event) + { + final Player player = event.getPlayer(); + + if (event.getClickedBlock() != null) + { + if (event.getClickedBlock().getType().equals(Material.STRUCTURE_BLOCK) || event.getClickedBlock().getType().equals(Material.JIGSAW) || event.getClickedBlock().getType().equals(Material.RESPAWN_ANCHOR)) + { + event.setCancelled(true); + event.getPlayer().closeInventory(); + } + } + + if (Groups.SPAWN_EGGS.contains(event.getMaterial())) + { + player.getInventory().clear(player.getInventory().getHeldItemSlot()); + player.sendMessage(ChatColor.GRAY + "Spawn eggs are currently disabled."); + event.setCancelled(true); + return; + } + + switch (event.getMaterial()) + { + case WATER_BUCKET: + { + if (plugin.al.isAdmin(player) || ConfigEntry.ALLOW_WATER_PLACE.getBoolean()) + { + break; + } + + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + player.sendMessage(ChatColor.GRAY + "Water buckets are currently disabled."); + event.setCancelled(true); + break; + } + + case LAVA_BUCKET: + { + if (plugin.al.isAdmin(player) || ConfigEntry.ALLOW_LAVA_PLACE.getBoolean()) + { + break; + } + + player.getInventory().setItem(player.getInventory().getHeldItemSlot(), new ItemStack(Material.COOKIE, 1)); + player.sendMessage(ChatColor.GRAY + "Lava buckets are currently disabled."); + event.setCancelled(true); + break; + } + + case TNT_MINECART: + { + if (ConfigEntry.ALLOW_TNT_MINECARTS.getBoolean()) + { + break; + } + + player.getInventory().clear(player.getInventory().getHeldItemSlot()); + player.sendMessage(ChatColor.GRAY + "TNT minecarts are currently disabled."); + event.setCancelled(true); + break; + } + + case ARMOR_STAND: + { + if (ConfigEntry.ALLOW_ARMOR_STANDS.getBoolean()) + { + break; + } + + player.getInventory().clear(player.getInventory().getHeldItemSlot()); + player.sendMessage(ChatColor.GRAY + "Armor stands are currently disabled."); + event.setCancelled(true); + break; + } + case MINECART: + { + if (ConfigEntry.ALLOW_MINECARTS.getBoolean()) + { + break; + } + + player.getInventory().clear(player.getInventory().getHeldItemSlot()); + player.sendMessage(ChatColor.GRAY + "Minecarts are currently disabled."); + event.setCancelled(true); + break; + } + case WRITTEN_BOOK: + { + player.getInventory().clear(player.getInventory().getHeldItemSlot()); + player.sendMessage(ChatColor.GRAY + "Books are currently disabled."); + event.setCancelled(true); + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/MobBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/MobBlocker.java new file mode 100644 index 00000000..2dae585b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/MobBlocker.java @@ -0,0 +1,134 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.bukkit.attribute.Attributable; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.Bat; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Ghast; +import org.bukkit.entity.Giant; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Wither; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntitySpawnEvent; + +public class MobBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + //fixes crash mobs, credit to Mafrans + @EventHandler(priority = EventPriority.NORMAL) + public void onEntitySpawn(EntitySpawnEvent e) + { + if (!(e instanceof LivingEntity)) + { + return; + } + + Entity entity = e.getEntity(); + if (entity instanceof Attributable) + { + if (Objects.requireNonNull(((Attributable)entity).getAttribute(Attribute.GENERIC_FOLLOW_RANGE)).getBaseValue() > 255.0) + { + Objects.requireNonNull(((Attributable)entity).getAttribute(Attribute.GENERIC_FOLLOW_RANGE)).setBaseValue(255.0); + } + if (Objects.requireNonNull(((Attributable)entity).getAttribute(Attribute.GENERIC_MOVEMENT_SPEED)).getBaseValue() > 10.0) + { + Objects.requireNonNull(((Attributable)entity).getAttribute(Attribute.GENERIC_MOVEMENT_SPEED)).setBaseValue(10.0); + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onCreatureSpawn(CreatureSpawnEvent event) + { + if (!ConfigEntry.MOB_LIMITER_ENABLED.getBoolean()) + { + return; + } + + final Entity spawned = event.getEntity(); + + if (spawned instanceof EnderDragon) + { + if (ConfigEntry.MOB_LIMITER_DISABLE_DRAGON.getBoolean()) + { + event.setCancelled(true); + return; + } + } + else if (spawned instanceof Ghast) + { + if (ConfigEntry.MOB_LIMITER_DISABLE_GHAST.getBoolean()) + { + event.setCancelled(true); + return; + } + } + else if (spawned instanceof Slime) + { + if (ConfigEntry.MOB_LIMITER_DISABLE_SLIME.getBoolean()) + { + event.setCancelled(true); + return; + } + } + else if (spawned instanceof Wither) + { + if (ConfigEntry.MOB_LIMITER_DISABLE_DRAGON.getBoolean()) + { + event.setCancelled(true); + return; + } + } + else if (spawned instanceof Giant) + { + if (ConfigEntry.MOB_LIMITER_DISABLE_GIANT.getBoolean()) + { + event.setCancelled(true); + return; + } + } + else if (spawned instanceof Bat) + { + event.setCancelled(true); + return; + } + + int mobLimiterMax = ConfigEntry.MOB_LIMITER_MAX.getInteger(); + + if (mobLimiterMax <= 0) + { + return; + } + + int mobcount = 0; + for (Entity entity : Objects.requireNonNull(event.getLocation().getWorld()).getLivingEntities()) + { + if (!(entity instanceof HumanEntity) && entity instanceof LivingEntity) + { + mobcount++; + } + } + + if (mobcount > mobLimiterMax) + { + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/PVPBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/PVPBlocker.java new file mode 100644 index 00000000..52d855c2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/PVPBlocker.java @@ -0,0 +1,97 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.Player; +import org.bukkit.entity.SpectralArrow; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class PVPBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.LOW) + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) + { + Player player = null; + Player target = null; + if (event.getEntity() instanceof Player) + { + target = (Player)event.getEntity(); + if (event.getDamager() instanceof Player) + { + player = (Player)event.getDamager(); + } + else if (event.getDamager() instanceof Arrow) + { + Arrow arrow = (Arrow)event.getDamager(); + if (arrow.getShooter() instanceof Player) + { + player = (Player)arrow.getShooter(); + } + } + else if (event.getDamager() instanceof SpectralArrow) + { + SpectralArrow spectralArrow = (SpectralArrow)event.getDamager(); + if (spectralArrow.getShooter() instanceof Player) + { + player = (Player)spectralArrow.getShooter(); + } + } + else if (event.getDamager() instanceof Trident) + { + Trident trident = (Trident)event.getDamager(); + if (trident.getShooter() instanceof Player) + { + player = (Player)trident.getShooter(); + } + } + else if (event.getDamager() instanceof FishHook) + { + FishHook fishhook = (FishHook)event.getDamager(); + if (fishhook.getShooter() instanceof Player) + { + player = (Player)fishhook.getShooter(); + } + } + } + + if (player != null & !plugin.al.isAdmin(player)) + { + if (player.getGameMode() == GameMode.CREATIVE) + { + player.sendMessage(ChatColor.RED + "Creative PvP is not allowed!"); + event.setCancelled(true); + } + else if (plugin.esb.getEssentialsUser(player.getName()).isGodModeEnabled()) + { + player.sendMessage(ChatColor.RED + "God mode PvP is not allowed!"); + event.setCancelled(true); + } + else if (plugin.pl.getPlayer(target).isPvpBlocked()) + { + player.sendMessage(ChatColor.RED + target.getName() + " has PvP disabled!"); + event.setCancelled(true); + } + else if (plugin.pl.getPlayer(player).isPvpBlocked()) + { + player.sendMessage(ChatColor.RED + "You have PvP disabled!"); + event.setCancelled(true); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/PotionBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/PotionBlocker.java new file mode 100644 index 00000000..1088e565 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/PotionBlocker.java @@ -0,0 +1,85 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import java.util.Collection; +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.projectiles.ProjectileSource; + +public class PotionBlocker extends FreedomService +{ + + public static final int POTION_BLOCK_RADIUS_SQUARED = 20 * 20; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onThrowPotion(PotionSplashEvent event) + { + ThrownPotion potion = event.getEntity(); + ProjectileSource projectileSource = potion.getShooter(); + Player player = null; + if (projectileSource instanceof Player) + { + player = (Player)projectileSource; + } + + if (isDeathPotion(potion.getEffects())) + { + if (player != null) + { + player.sendMessage(ChatColor.RED + "You are not allowed to use death potions."); + } + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onThrowLingeringPotion(LingeringPotionSplashEvent event) + { + ThrownPotion potion = event.getEntity(); + ProjectileSource projectileSource = potion.getShooter(); + Player player = null; + if (projectileSource instanceof Player) + { + player = (Player)projectileSource; + } + + if (isDeathPotion(potion.getEffects())) + { + if (player != null) + { + player.sendMessage(ChatColor.RED + "You are not allowed to use death potions."); + } + event.setCancelled(true); + } + } + + public boolean isDeathPotion(Collection effects) + { + for (PotionEffect effect : effects) + { + int amplifier = effect.getAmplifier(); + if (effect.getType().equals(PotionEffectType.HEAL) && (amplifier == 29 || amplifier == 61 || amplifier == 93 || amplifier == 125)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/SignBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/SignBlocker.java new file mode 100644 index 00000000..3fabc837 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/SignBlocker.java @@ -0,0 +1,67 @@ +package me.totalfreedom.totalfreedommod.blocking; + +import me.totalfreedom.totalfreedommod.FreedomService; +import net.minecraft.nbt.NBTTagCompound; +import org.bukkit.ChatColor; +import org.bukkit.Tag; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +//codebeat:disable[LOC,ABC] + +public class SignBlocker extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerPlaceBlock(BlockPlaceEvent event) + { + + final Player player = event.getPlayer(); + if (Tag.SIGNS.getValues().contains(event.getBlock().getType())) + { + ItemStack sign = event.getItemInHand(); + net.minecraft.world.item.ItemStack nmsSign = CraftItemStack.asNMSCopy(sign); + NBTTagCompound compound = (nmsSign.hasTag()) ? nmsSign.getTag() : new NBTTagCompound(); + assert compound != null; + NBTTagCompound bet = compound.getCompound("BlockEntityTag"); + String line1 = bet.getString("Text1"); + String line2 = bet.getString("Text2"); + String line3 = bet.getString("Text3"); + String line4 = bet.getString("Text4"); + if (line1.contains("run_command") || line2.contains("run_command") || line3.contains("run_command") || line4.contains("run_command")) + { + player.sendMessage(ChatColor.GRAY + "You are not allowed to place command signs."); + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerInteractSign(PlayerInteractEvent event) + { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) + { + return; + } + + if (event.getClickedBlock() != null && Tag.SIGNS.getValues().contains(event.getClickedBlock().getType())) + { + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlocker.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlocker.java new file mode 100644 index 00000000..2074f3e5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlocker.java @@ -0,0 +1,226 @@ +package me.totalfreedom.totalfreedommod.blocking.command; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.plugin.SimplePluginManager; + +public class CommandBlocker extends FreedomService +{ + + private final Pattern flagPattern = Pattern.compile("(:([0-9]){5,})"); + // + private final Map entryList = Maps.newHashMap(); + private final List unknownCommands = Lists.newArrayList(); + + public static CommandMap getCommandMap() + { + try + { + SimplePluginManager simplePluginManager = (SimplePluginManager)Bukkit.getServer().getPluginManager(); + + Field commandMapField = SimplePluginManager.class.getDeclaredField("commandMap"); + commandMapField.setAccessible(true); + + return (SimpleCommandMap)commandMapField.get(simplePluginManager); + } + catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) + { + FLog.severe("Failed to get command map field (" + e.getMessage() + ")"); + } + return null; + } + + @Override + public void onStart() + { + load(); + } + + @Override + public void onStop() + { + entryList.clear(); + } + + public void load() + { + entryList.clear(); + unknownCommands.clear(); + + final CommandMap commandMap = getCommandMap(); + + @SuppressWarnings("unchecked") + List blockedCommands = (List)ConfigEntry.BLOCKED_COMMANDS.getList(); + for (String rawEntry : blockedCommands) + { + final String[] parts = rawEntry.split(":"); + if (parts.length < 3 || parts.length > 4) + { + FLog.warning("Invalid command blocker entry: " + rawEntry); + continue; + } + + final CommandBlockerRank rank = CommandBlockerRank.fromToken(parts[0]); + final CommandBlockerAction action = CommandBlockerAction.fromToken(parts[1]); + String commandName = parts[2].toLowerCase().substring(1); + final String message = (parts.length > 3 ? parts[3] : null); + + if (rank == null || action == null || commandName.isEmpty()) + { + FLog.warning("Invalid command blocker entry: " + rawEntry); + continue; + } + + final String[] commandParts = commandName.split(" "); + String subCommand = null; + if (commandParts.length > 1) + { + commandName = commandParts[0]; + subCommand = StringUtils.join(commandParts, " ", 1, commandParts.length).trim().toLowerCase(); + } + + assert commandMap != null; + final Command command = commandMap.getCommand(commandName); + + // Obtain command from alias + if (command == null) + { + unknownCommands.add(commandName); + } + else + { + commandName = command.getName().toLowerCase(); + } + + if (entryList.containsKey(commandName)) + { + FLog.warning("Not blocking: /" + commandName + " - Duplicate entry exists!"); + continue; + } + + + final CommandBlockerEntry blockedCommandEntry = new CommandBlockerEntry(rank, action, commandName, subCommand, message); + entryList.put(commandName, blockedCommandEntry); + if (command != null) + { + for (String alias : command.getAliases()) + { + entryList.put(alias.toLowerCase(), blockedCommandEntry); + } + } + } + + FLog.info("Loaded " + blockedCommands.size() + " blocked commands (" + (blockedCommands.size() - unknownCommands.size()) + " known)."); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + // Blocked commands + if (isCommandBlocked(event.getMessage(), event.getPlayer(), true)) + { + // CommandBlocker handles messages and broadcasts + event.setCancelled(true); + } + } + + public boolean isCommandBlocked(String command, CommandSender sender) + { + return isCommandBlocked(command, sender, false); + } + + public boolean isCommandBlocked(String command, CommandSender sender, boolean doAction) + { + if (command == null || command.isEmpty()) + { + return false; + } + + // Format + command = command.toLowerCase().trim(); + command = command.startsWith("/") ? command.substring(1) : command; + + // Check for plugin specific commands + final String[] commandParts = command.split(" "); + if (commandParts[0].contains(":")) + { + if (doAction) + { + FUtil.playerMsg(sender, "Plugin specific commands are disabled."); + } + return true; + } + + for (String part : commandParts) + { + if (command.startsWith("/") && !plugin.al.isAdmin(sender) && (part.contains("#copy") || part.contains("#clipboard"))) + { + FUtil.playerMsg(sender, "WorldEdit copy variables are disabled."); + return true; + } + Matcher matcher = flagPattern.matcher(part); + if (!matcher.matches()) + { + continue; + } + if (doAction) + { + FUtil.playerMsg(sender, "That command contains an illegal number: " + matcher.group(1)); + } + return true; + } + + // Obtain sub command, if it exists + String subCommand = null; + if (commandParts.length > 1) + { + subCommand = StringUtils.join(commandParts, " ", 1, commandParts.length).toLowerCase(); + } + + // Obtain entry + final CommandBlockerEntry entry = entryList.get(commandParts[0]); + if (entry == null) + { + return false; + } + + // Validate sub command + if (entry.getSubCommand() != null) + { + if (subCommand == null || !subCommand.startsWith(entry.getSubCommand())) + { + return false; + } + } + + if (entry.getRank().hasPermission(sender)) + { + return false; + } + + if (doAction) + { + entry.doActions(sender); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerAction.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerAction.java new file mode 100644 index 00000000..07b76d18 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerAction.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.blocking.command; + +public enum CommandBlockerAction +{ + + BLOCK("b"), + BLOCK_AND_EJECT("a"), + BLOCK_UNKNOWN("u"); + private final String token; + + CommandBlockerAction(String token) + { + this.token = token; + } + + public static CommandBlockerAction fromToken(String token) + { + for (CommandBlockerAction action : CommandBlockerAction.values()) + { + if (action.getToken().equalsIgnoreCase(token)) + { + return action; + } + } + return null; + } + + public String getToken() + { + return this.token; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerEntry.java new file mode 100644 index 00000000..8ccde03e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerEntry.java @@ -0,0 +1,78 @@ +package me.totalfreedom.totalfreedommod.blocking.command; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.spigotmc.SpigotConfig; + +public class CommandBlockerEntry +{ + + + private final CommandBlockerRank rank; + + private final CommandBlockerAction action; + + private final String command; + + private final String subCommand; + + private final String message; + + public CommandBlockerEntry(CommandBlockerRank rank, CommandBlockerAction action, String command, String message) + { + this(rank, action, command, null, message); + } + + public CommandBlockerEntry(CommandBlockerRank rank, CommandBlockerAction action, String command, String subCommand, String message) + { + this.rank = rank; + this.action = action; + this.command = command; + this.subCommand = ((subCommand == null) ? null : subCommand.toLowerCase().trim()); + this.message = ((message == null || message.equals("_")) ? "That command is blocked." : message); + } + + public void doActions(CommandSender sender) + { + if (action == CommandBlockerAction.BLOCK_AND_EJECT && sender instanceof Player) + { + TotalFreedomMod.getPlugin().ae.autoEject((Player)sender, "You used a prohibited command: " + command); + FUtil.bcastMsg(sender.getName() + " was automatically kicked for using harmful commands.", ChatColor.RED); + return; + } + if (action == CommandBlockerAction.BLOCK_UNKNOWN) + { + sender.sendMessage(SpigotConfig.unknownCommandMessage); + return; + } + FUtil.playerMsg(sender, FUtil.colorize(message)); + } + + public CommandBlockerRank getRank() + { + return rank; + } + + public CommandBlockerAction getAction() + { + return action; + } + + public String getCommand() + { + return command; + } + + public String getSubCommand() + { + return subCommand; + } + + public String getMessage() + { + return message; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerRank.java b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerRank.java new file mode 100644 index 00000000..c533b96e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/blocking/command/CommandBlockerRank.java @@ -0,0 +1,64 @@ +package me.totalfreedom.totalfreedommod.blocking.command; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.CommandSender; + +public enum CommandBlockerRank +{ + EVERYONE("e"), + OP("o"), + ADMIN("a"), + SENIOR_ADMIN("s"), + NOBODY("n"); + // + private final String token; + + CommandBlockerRank(String token) + { + this.token = token; + } + + public static CommandBlockerRank fromSender(CommandSender sender) + { + Admin admin = TotalFreedomMod.getPlugin().al.getAdmin(sender); + if (admin != null) + { + if (admin.getRank() == Rank.SENIOR_ADMIN) + { + return SENIOR_ADMIN; + } + return ADMIN; + } + + if (sender.isOp()) + { + return OP; + } + + return EVERYONE; + } + + public static CommandBlockerRank fromToken(String token) + { + for (CommandBlockerRank rank : CommandBlockerRank.values()) + { + if (rank.getToken().equalsIgnoreCase(token)) + { + return rank; + } + } + return EVERYONE; + } + + public String getToken() + { + return this.token; + } + + public boolean hasPermission(CommandSender sender) + { + return fromSender(sender).ordinal() >= ordinal(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/BukkitTelnetBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/BukkitTelnetBridge.java new file mode 100644 index 00000000..479a30e1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/BukkitTelnetBridge.java @@ -0,0 +1,177 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import me.totalfreedom.bukkittelnet.BukkitTelnet; +import me.totalfreedom.bukkittelnet.api.TelnetCommandEvent; +import me.totalfreedom.bukkittelnet.api.TelnetPreLoginEvent; +import me.totalfreedom.bukkittelnet.api.TelnetRequestDataTagsEvent; +import me.totalfreedom.bukkittelnet.session.ClientSession; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.plugin.Plugin; + +public class BukkitTelnetBridge extends FreedomService +{ + + private BukkitTelnet bukkitTelnetPlugin = null; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onTelnetPreLogin(TelnetPreLoginEvent event) + { + + final String ip = event.getIp(); + if (ip == null || ip.isEmpty()) + { + return; + } + + final Admin admin = plugin.al.getEntryByIpFuzzy(ip); + + if (admin == null || !admin.isActive() || !admin.getRank().hasConsoleVariant()) + { + return; + } + + event.setBypassPassword(true); + event.setName(admin.getName()); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onTelnetCommand(TelnetCommandEvent event) + { + if (plugin.cb.isCommandBlocked(event.getCommand(), event.getSender())) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onTelnetRequestDataTags(TelnetRequestDataTagsEvent event) + { + for (Map.Entry> entry : event.getDataTags().entrySet()) + { + final Player player = entry.getKey(); + final Map playerTags = entry.getValue(); + + boolean isAdmin = false; + boolean isTelnetAdmin = false; + boolean isSeniorAdmin = false; + + final Admin admin = plugin.al.getAdmin(player); + if (admin != null) + { + boolean active = admin.isActive(); + + isAdmin = active; + isSeniorAdmin = active && admin.getRank() == Rank.SENIOR_ADMIN; + isTelnetAdmin = active && (isSeniorAdmin || admin.getRank() == Rank.ADMIN); + } + + playerTags.put("tfm.admin.isAdmin", isAdmin); + playerTags.put("tfm.admin.isTelnetAdmin", isTelnetAdmin); + playerTags.put("tfm.admin.isSeniorAdmin", isSeniorAdmin); + + playerTags.put("tfm.playerdata.getTag", plugin.pl.getPlayer(player).getTag()); + + playerTags.put("tfm.essentialsBridge.getNickname", plugin.esb.getNickname(player.getName())); + } + } + + public BukkitTelnet getBukkitTelnetPlugin() + { + if (bukkitTelnetPlugin == null) + { + try + { + final Plugin bukkitTelnet = server.getPluginManager().getPlugin("BukkitTelnet"); + if (bukkitTelnet != null) + { + if (bukkitTelnet instanceof BukkitTelnet) + { + bukkitTelnetPlugin = (BukkitTelnet)bukkitTelnet; + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + return bukkitTelnetPlugin; + } + + public List getConnectedAdmins() + { + List admins = new ArrayList<>(); + final BukkitTelnet telnet = getBukkitTelnetPlugin(); + if (telnet != null) + { + for (ClientSession session : telnet.appender.getSessions()) + { + Admin admin = plugin.al.getEntryByName(session.getUserName().toLowerCase()); + if (admin != null && !admins.contains(admin)) + { + admins.add(admin); + } + } + } + return admins; + } + + public void killTelnetSessions(final String name) + { + try + { + final List sessionsToRemove = new ArrayList<>(); + + final BukkitTelnet telnet = getBukkitTelnetPlugin(); + if (telnet != null) + { + for (ClientSession session : telnet.appender.getSessions()) + { + if (name != null && name.equalsIgnoreCase(session.getUserName())) + { + sessionsToRemove.add(session); + } + } + + for (final ClientSession session : sessionsToRemove) + { + try + { + telnet.appender.removeSession(session); + session.syncTerminateSession(); + } + catch (Exception ex) + { + FLog.severe("Error removing single telnet session: " + ex.getMessage()); + } + } + + FLog.info(sessionsToRemove.size() + " telnet session(s) removed."); + } + } + catch (Exception ex) + { + FLog.severe("Error removing telnet sessions: " + ex.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java new file mode 100644 index 00000000..dc846cd1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/CoreProtectBridge.java @@ -0,0 +1,457 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import java.io.File; +import java.sql.*; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.coreprotect.CoreProtect; +import net.coreprotect.CoreProtectAPI; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class CoreProtectBridge extends FreedomService +{ + public static Map> HISTORY_MAP = new HashMap<>(); + private final List tables = Arrays.asList("co_sign", "co_session", "co_container", "co_block"); + + private final HashMap cooldown = new HashMap<>(); + private CoreProtectAPI coreProtectAPI = null; + private BukkitTask wiper; + + public static Long getSecondsLeft(long prevTime, int timeAdd) + { + return prevTime / 1000L + timeAdd - System.currentTimeMillis() / 1000L; + } + + // Unix timestamp converter taken from Functions class in CoreProtect, not my code + public static String getTimeAgo(int logTime, int currentTime) + { + StringBuilder message = new StringBuilder(); + double timeSince = (double)currentTime - ((double)logTime + 0.0D); + timeSince /= 60.0D; + if (timeSince < 60.0D) + { + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/m ago"); + } + + if (message.length() == 0) + { + timeSince /= 60.0D; + if (timeSince < 24.0D) + { + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/h ago"); + } + } + + if (message.length() == 0) + { + timeSince /= 24.0D; + message.append((new DecimalFormat("0.00")).format(timeSince)).append("/d ago"); + } + + return message.toString(); + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public CoreProtect getCoreProtect() + { + CoreProtect coreProtect = null; + try + { + final Plugin coreProtectPlugin = Bukkit.getServer().getPluginManager().getPlugin("CoreProtect"); + assert coreProtectPlugin != null; + if (coreProtectPlugin instanceof CoreProtect) + { + coreProtect = (CoreProtect)coreProtectPlugin; + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + return coreProtect; + } + + public CoreProtectAPI getCoreProtectAPI() + { + if (coreProtectAPI == null) + { + try + { + final CoreProtect coreProtect = getCoreProtect(); + + coreProtectAPI = coreProtect.getAPI(); + + // Check if the plugin or api is not enabled, if so, return null + if (!coreProtect.isEnabled() || !coreProtectAPI.isEnabled()) + { + return null; + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + return coreProtectAPI; + } + + public boolean isEnabled() + { + final CoreProtect coreProtect = getCoreProtect(); + + return coreProtect != null && coreProtect.isEnabled(); + } + + // Rollback the specified player's edits that were in the last 24 hours. + public void rollback(final String name) + { + final CoreProtectAPI coreProtect = getCoreProtectAPI(); + + if (!isEnabled()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + coreProtect.performRollback(86400, Collections.singletonList(name), null, null, null, null, 0, null); + } + }.runTaskAsynchronously(plugin); + } + + // Reverts a rollback for the specified player's edits that were in the last 24 hours. + public void restore(final String name) + { + final CoreProtectAPI coreProtect = getCoreProtectAPI(); + + if (!isEnabled()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + coreProtect.performRestore(86400, Collections.singletonList(name), null, null, null, null, 0, null); + } + }.runTaskAsynchronously(plugin); + } + + public File getDatabase() + { + if (!isEnabled()) + { + return null; + } + + return (new File(getCoreProtect().getDataFolder(), "database.db")); + } + + public double getDBSize() + { + double bytes = getDatabase().length(); + double kilobytes = (bytes / 1024); + double megabytes = (kilobytes / 1024); + return (megabytes / 1024); + } + + // Wipes DB for the specified world + public void clearDatabase(World world) + { + clearDatabase(world, false); + } + + // Wipes DB for the specified world + public void clearDatabase(World world, Boolean shutdown) + { + if (!ConfigEntry.COREPROTECT_MYSQL_ENABLED.getBoolean()) + { + return; + } + final CoreProtect coreProtect = getCoreProtect(); + + if (coreProtect == null) + { + return; + } + + /* As CoreProtect doesn't have an API method for deleting all of the data for a specific world + we have to do this manually via SQL */ + Connection connection; + try + { + String host = ConfigEntry.COREPROTECT_MYSQL_HOST.getString(); + String port = ConfigEntry.COREPROTECT_MYSQL_PORT.getString(); + String username = ConfigEntry.COREPROTECT_MYSQL_USERNAME.getString(); + String password = ConfigEntry.COREPROTECT_MYSQL_PASSWORD.getString(); + String database = ConfigEntry.COREPROTECT_MYSQL_DATABASE.getString(); + String url = host + ":" + port + "/" + database + "?user=" + username + "&password=" + password + "&useSSL=false"; + connection = DriverManager.getConnection("jdbc:sql://" + url); + final PreparedStatement statement = connection.prepareStatement("SELECT id FROM co_world WHERE world = ?"); + statement.setQueryTimeout(30); + + // Obtain world ID from CoreProtect database + statement.setString(1, world.getName()); + ResultSet resultSet = statement.executeQuery(); + String worldID = null; + while (resultSet.next()) + { + worldID = String.valueOf(resultSet.getInt("id")); + } + + // Ensure the world ID is not null + + if (worldID == null) + { + FLog.warning("Failed to obtain the world ID for the " + world.getName()); + return; + } + + // Iterate through each table and delete their data if the world ID matches + for (String table : tables) + { + final PreparedStatement statement1 = connection.prepareStatement("DELETE FROM ? WHERE wid = ?"); + statement1.setString(1, table); + statement1.setString(2, worldID); + statement1.executeQuery(); + } + + connection.close(); + + } + catch (SQLException e) + { + FLog.warning("Failed to delete the CoreProtect data for the " + world.getName()); + } + + // This exits for flatlands wipes + if (shutdown) + { + server.shutdown(); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerInteract(PlayerInteractEvent event) + { + Player player = event.getPlayer(); + PlayerData data = plugin.pl.getData(player); + Block block = event.getClickedBlock(); + final CoreProtectAPI coreProtect = getCoreProtectAPI(); + + if (data.hasInspection()) + { + if (event.getAction() == Action.LEFT_CLICK_BLOCK) + { + if (block != null) + { + event.setCancelled(true); + List lookup = coreProtect.blockLookup(block, -1); + + int cooldownTime = 3; + + if (cooldown.containsKey(player.getName())) + { + long secondsLeft = getSecondsLeft(cooldown.get(player.getName()), cooldownTime); + if (secondsLeft > 0L) + { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + String.valueOf(secondsLeft) + " seconds left before next query."); + return; + } + } + + if (!plugin.al.isAdmin(player)) + { + cooldown.put(player.getName(), System.currentTimeMillis()); + } + + if (lookup != null) + { + if (lookup.isEmpty()) + { + player.sendMessage(net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector " + ChatColor.WHITE + "- " + "No block data found for this location"); + return; + } + + HISTORY_MAP.remove(event.getPlayer()); + HISTORY_MAP.put(event.getPlayer(), new FUtil.PaginationList<>(10)); + FUtil.PaginationList paged = HISTORY_MAP.get(event.getPlayer()); + + player.sendMessage("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- " + + ChatColor.GRAY + "(x" + block.getX() + "/" + "y" + block.getY() + "/" + "z" + block.getZ() + ")"); + + for (String[] value : lookup) + { + CoreProtectAPI.ParseResult result = coreProtect.parseResult(value); + BlockData bl = result.getBlockData(); + + String s; + String st = ""; + + if (result.getActionString().equals("Placement")) + { + s = " placed "; + } + else if (result.getActionString().equals("Removal")) + { + s = " broke "; + } + else + { + s = " interacted with "; + } + + if (result.isRolledBack()) + { + st += "§m"; + } + + int time = (int)(System.currentTimeMillis() / 1000L); + + paged.add(ChatColor.GRAY + getTimeAgo(result.getTime(), time) + ChatColor.WHITE + " - " + net.md_5.bungee.api.ChatColor.of("#30ade4") + + st + result.getPlayer() + ChatColor.WHITE + st + s + net.md_5.bungee.api.ChatColor.of("#30ade4") + st + bl.getMaterial().toString().toLowerCase()); + } + + List page = paged.getPage(1); + for (String entries : page) + { + player.sendMessage(entries); + } + + player.sendMessage("Page 1/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history "); + } + } + } + else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) + { + if (block != null) + { + if (data.hasInspection()) + { + BlockState blockState = block.getRelative(event.getBlockFace()).getState(); + Block placedBlock = blockState.getBlock(); + event.setCancelled(true); + List lookup = coreProtect.blockLookup(placedBlock, -1); + + if (lookup.isEmpty()) + { + lookup = coreProtect.blockLookup(block, -1); + } + + int cooldownTime = 3; + + if (cooldown.containsKey(player.getName())) + { + long secondsLeft = getSecondsLeft(cooldown.get(player.getName()), cooldownTime); + if (secondsLeft > 0L) + { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + String.valueOf(secondsLeft) + " seconds left before next query."); + return; + } + } + + if (!plugin.al.isAdmin(player)) + { + cooldown.put(player.getName(), System.currentTimeMillis()); + } + + if (lookup != null) + { + if (lookup.isEmpty()) + { + player.sendMessage(net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector " + ChatColor.WHITE + "- " + "No block data found for this location"); + return; + } + + HISTORY_MAP.remove(event.getPlayer()); + HISTORY_MAP.put(event.getPlayer(), new FUtil.PaginationList<>(10)); + FUtil.PaginationList paged = HISTORY_MAP.get(event.getPlayer()); + + player.sendMessage("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- " + + ChatColor.GRAY + "(x" + block.getX() + "/" + "y" + block.getY() + "/" + "z" + block.getZ() + ")"); + + for (String[] value : lookup) + { + CoreProtectAPI.ParseResult result = coreProtect.parseResult(value); + BlockData bl = result.getBlockData(); + + String s; + String st = ""; + + if (result.getActionString().equals("Placement")) + { + s = " placed "; + } + else if (result.getActionString().equals("Removal")) + { + s = " broke "; + } + else + { + s = " interacted with "; + } + + if (result.isRolledBack()) + { + st += "§m"; + } + + int time = (int)(System.currentTimeMillis() / 1000L); + + paged.add(ChatColor.GRAY + getTimeAgo(result.getTime(), time) + ChatColor.WHITE + " - " + net.md_5.bungee.api.ChatColor.of("#30ade4") + + st + result.getPlayer() + ChatColor.WHITE + st + s + net.md_5.bungee.api.ChatColor.of("#30ade4") + st + bl.getMaterial().toString().toLowerCase()); + } + + List page = paged.getPage(1); + for (String entries : page) + { + player.sendMessage(entries); + } + + player.sendMessage("Page 1/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history "); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/EssentialsBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/EssentialsBridge.java new file mode 100644 index 00000000..d58f8761 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/EssentialsBridge.java @@ -0,0 +1,221 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.User; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +public class EssentialsBridge extends FreedomService +{ + + private Essentials essentialsPlugin = null; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public Essentials getEssentialsPlugin() + { + if (essentialsPlugin == null) + { + try + { + final Plugin essentials = server.getPluginManager().getPlugin("Essentials"); + assert essentials != null; + if (essentials instanceof Essentials) + { + essentialsPlugin = (Essentials)essentials; + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + return essentialsPlugin; + } + + public User getEssentialsUser(String username) + { + try + { + Essentials essentials = getEssentialsPlugin(); + if (essentials != null) + { + return essentials.getUserMap().getUser(username); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + return null; + } + + public void setNickname(String username, String nickname) + { + try + { + User user = getEssentialsUser(username); + if (user != null) + { + user.setNickname(nickname); + user.setDisplayNick(); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + public String getNickname(String username) + { + try + { + User user = getEssentialsUser(username); + if (user != null) + { + return user.getNickname(); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + return null; + } + + public long getLastActivity(String username) + { + try + { + User user = getEssentialsUser(username); + if (user != null) + { + Long l = FUtil.getField(user, "lastActivity"); + return (l != null) ? l : 0L; + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + return 0L; + } + + public void setVanished(String username, boolean vanished) + { + try + { + User user = getEssentialsUser(username); + if (user != null) + { + user.setVanished(vanished); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onInventoryClick(InventoryClickEvent event) + { + Player refreshPlayer = null; + Inventory inventory = event.getView().getTopInventory(); + InventoryType inventoryType = inventory.getType(); + Player player = (Player)event.getWhoClicked(); + FPlayer fPlayer = plugin.pl.getPlayer(player); + if (inventoryType == InventoryType.PLAYER && fPlayer.isInvSee()) + { + final InventoryHolder inventoryHolder = inventory.getHolder(); + if (inventoryHolder instanceof HumanEntity) + { + Player invOwner = (Player)inventoryHolder; + Rank recieverRank = plugin.rm.getRank(player); + Rank playerRank = plugin.rm.getRank(invOwner); + if (playerRank.ordinal() >= recieverRank.ordinal() || !invOwner.isOnline()) + { + event.setCancelled(true); + refreshPlayer = player; + } + } + } + if (refreshPlayer != null) + { + final Player p = refreshPlayer; + new BukkitRunnable() + { + @Override + public void run() + { + p.updateInventory(); + } + }.runTaskLater(plugin, 20L); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onInventoryClose(InventoryCloseEvent event) + { + Player refreshPlayer = null; + Inventory inventory = event.getView().getTopInventory(); + InventoryType inventoryType = inventory.getType(); + Player player = (Player)event.getPlayer(); + FPlayer fPlayer = plugin.pl.getPlayer(player); + if (inventoryType == InventoryType.PLAYER && fPlayer.isInvSee()) + { + fPlayer.setInvSee(false); + refreshPlayer = player; + } + if (refreshPlayer != null) + { + final Player p = refreshPlayer; + new BukkitRunnable() + { + @Override + public void run() + { + p.updateInventory(); + } + }.runTaskLater(plugin, 20L); + } + } + + // TODO: Actually use this for something or remove it. + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) + { + Player player = event.getPlayer(); + } + + public boolean isEnabled() + { + final Essentials ess = getEssentialsPlugin(); + + return ess != null && ess.isEnabled(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/LibsDisguisesBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/LibsDisguisesBridge.java new file mode 100644 index 00000000..88db08ab --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/LibsDisguisesBridge.java @@ -0,0 +1,101 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import me.libraryaddict.disguise.BlockedDisguises; +import me.libraryaddict.disguise.DisguiseAPI; +import me.libraryaddict.disguise.LibsDisguises; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class LibsDisguisesBridge extends FreedomService +{ + private LibsDisguises libsDisguisesPlugin = null; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public LibsDisguises getLibsDisguisesPlugin() + { + if (libsDisguisesPlugin == null) + { + try + { + final Plugin libsDisguises = server.getPluginManager().getPlugin("LibsDisguises"); + if (libsDisguises != null) + { + if (libsDisguises instanceof LibsDisguises) + { + libsDisguisesPlugin = (LibsDisguises)libsDisguises; + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + return libsDisguisesPlugin; + } + + public void undisguiseAll(boolean admin) + { + try + { + final LibsDisguises libsDisguises = getLibsDisguisesPlugin(); + + if (libsDisguises == null) + { + return; + } + + for (Player player : server.getOnlinePlayers()) + { + if (DisguiseAPI.isDisguised(player)) + { + if (!admin && plugin.al.isAdmin(player)) + { + continue; + } + DisguiseAPI.undisguiseToAll(player); + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + public boolean isDisguisesEnabled() + { + return !BlockedDisguises.disabled; + } + + public void setDisguisesEnabled(boolean state) + { + final LibsDisguises libsDisguises = getLibsDisguisesPlugin(); + + if (libsDisguises == null) + { + return; + } + + BlockedDisguises.disabled = !state; + } + + public boolean isEnabled() + { + final LibsDisguises libsDisguises = getLibsDisguisesPlugin(); + + return libsDisguises != null; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/TFGuildsBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/TFGuildsBridge.java new file mode 100644 index 00000000..e315b15c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/TFGuildsBridge.java @@ -0,0 +1,58 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import me.totalfreedom.tfguilds.Common; +import me.totalfreedom.tfguilds.TFGuilds; +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class TFGuildsBridge extends FreedomService +{ + public boolean enabled = false; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public boolean isTFGuildsEnabled() + { + if (enabled) + { + return true; + } + + try + { + final Plugin tfGuilds = server.getPluginManager().getPlugin("TFGuilds"); + if (tfGuilds != null && tfGuilds.isEnabled()) + { + if (tfGuilds instanceof TFGuilds) + { + enabled = true; + return true; + } + } + } + catch (NoClassDefFoundError ex) + { + return false; + } + + return false; + } + + public boolean inGuildChat(Player player) + { + if (!isTFGuildsEnabled()) + { + return false; + } + return Common.GUILD_CHAT.contains(player); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldEditBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldEditBridge.java new file mode 100644 index 00000000..79ccaa6f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldEditBridge.java @@ -0,0 +1,175 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.bukkit.BukkitPlayer; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class WorldEditBridge extends FreedomService +{ + + // + private WorldEditPlugin worldeditPlugin = null; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public WorldEditPlugin getWorldEditPlugin() + { + if (worldeditPlugin == null) + { + try + { + Plugin we = server.getPluginManager().getPlugin("WorldEdit"); + if (we != null) + { + if (we instanceof WorldEditPlugin) + { + worldeditPlugin = (WorldEditPlugin)we; + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + return worldeditPlugin; + } + + public void undo(Player player, int count) + { + try + { + LocalSession session = getPlayerSession(player); + if (session != null) + { + final BukkitPlayer bukkitPlayer = getBukkitPlayer(player); + if (bukkitPlayer != null) + { + for (int i = 0; i < count; i++) + { + session.undo(session.getBlockBag(bukkitPlayer), bukkitPlayer); + } + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + public void redo(Player player, int count) + { + try + { + LocalSession session = getPlayerSession(player); + if (session != null) + { + final BukkitPlayer bukkitPlayer = getBukkitPlayer(player); + if (bukkitPlayer != null) + { + for (int i = 0; i < count; i++) + { + session.redo(session.getBlockBag(bukkitPlayer), bukkitPlayer); + } + } + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + public void setLimit(Player player, int limit) + { + try + { + final LocalSession session = getPlayerSession(player); + if (session != null) + { + session.setBlockChangeLimit(limit); + } + } + catch (Exception ex) + { + FLog.severe(ex); + } + + } + + public int getDefaultLimit() + { + final WorldEditPlugin wep = getWorldEditPlugin(); + if (wep == null) + { + return 0; + } + + return wep.getLocalConfiguration().defaultChangeLimit; + + } + + public int getMaxLimit() + { + final WorldEditPlugin wep = getWorldEditPlugin(); + if (wep == null) + { + return 0; + } + + return wep.getLocalConfiguration().maxChangeLimit; + + } + + private LocalSession getPlayerSession(Player player) + { + final WorldEditPlugin wep = getWorldEditPlugin(); + if (wep == null) + { + return null; + } + + try + { + return wep.getSession(player); + } + catch (Exception ex) + { + FLog.severe(ex); + return null; + } + } + + private BukkitPlayer getBukkitPlayer(Player player) + { + final WorldEditPlugin wep = getWorldEditPlugin(); + if (wep == null) + { + return null; + } + + try + { + return wep.wrapPlayer(player); + } + catch (Exception ex) + { + FLog.severe(ex); + return null; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldGuardBridge.java b/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldGuardBridge.java new file mode 100644 index 00000000..6bc0ab9b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/bridge/WorldGuardBridge.java @@ -0,0 +1,54 @@ +package me.totalfreedom.totalfreedommod.bridge; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +public class WorldGuardBridge extends FreedomService +{ + @Override + public void onStart() + { + plugin.wr.protectWorld(plugin.wm.masterBuilderWorld.getWorld()); + } + + @Override + public void onStop() + { + } + + public RegionManager getRegionManager(World world) + { + RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + return container.get(BukkitAdapter.adapt(world)); + } + + public int wipeRegions(World world) + { + int count = 0; + RegionManager regionManager = getRegionManager(world); + if (regionManager != null) + { + Map regions = regionManager.getRegions(); + for (ProtectedRegion region : regions.values()) + { + regionManager.removeRegion(region.getId()); + count++; + } + } + return count; + } + + public boolean isEnabled() + { + Plugin plugin = server.getPluginManager().getPlugin("WorldGuard"); + + return plugin != null && plugin.isEnabled(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/caging/CageData.java b/src/main/java/me/totalfreedom/totalfreedommod/caging/CageData.java new file mode 100644 index 00000000..aab5299c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/caging/CageData.java @@ -0,0 +1,288 @@ +package me.totalfreedom.totalfreedommod.caging; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Skull; + +public class CageData +{ + + private static String input = null; + private final FPlayer fPlayer; + // + // + private final List cageHistory = new ArrayList<>(); + private boolean caged = false; + private Location location; + private Material outerMaterial = Material.GLASS; + private Material innerMaterial = Material.AIR; + + public CageData(FPlayer player) + { + this.fPlayer = player; + } + + // Util methods + public static void generateCube(Location location, int length, Material material) + { + final Block center = location.getBlock(); + for (int xOffset = -length; xOffset <= length; xOffset++) + { + for (int yOffset = -length; yOffset <= length; yOffset++) + { + for (int zOffset = -length; zOffset <= length; zOffset++) + { + final Block block = center.getRelative(xOffset, yOffset, zOffset); + if (block.getType() != material) + { + block.setType(material); + } + } + } + } + } + + @SuppressWarnings("deprecation") + public static void generateHollowCube(Location location, int length, Material material) + { + final Block center = location.getBlock(); + for (int xOffset = -length; xOffset <= length; xOffset++) + { + for (int yOffset = -length; yOffset <= length; yOffset++) + { + for (int zOffset = -length; zOffset <= length; zOffset++) + { + // Hollow + if (Math.abs(xOffset) != length && Math.abs(yOffset) != length && Math.abs(zOffset) != length) + { + continue; + } + + final Block block = center.getRelative(xOffset, yOffset, zOffset); + + if (material != Material.PLAYER_HEAD) + { + // Glowstone light + if (material != Material.GLASS && xOffset == 0 && yOffset == 2 && zOffset == 0) + { + block.setType(Material.GLOWSTONE); + continue; + } + + block.setType(material); + } + else + { + if (Math.abs(xOffset) == length && Math.abs(yOffset) == length && Math.abs(zOffset) == length) + { + block.setType(Material.GLOWSTONE); + continue; + } + + block.setType(Material.PLAYER_HEAD); + if (input != null) + { + try + { + Skull skull = (Skull)block.getState(); + // This may or may not work in future versions of spigot + skull.setOwner(input); + skull.update(); + } + catch (ClassCastException ignored) + { + } + } + } + } + } + } + } + + public static String getInput() + { + return input; + } + + public static void setInput(String input) + { + CageData.input = input; + } + + public void cage(Location location, Material outer, Material inner) + { + if (isCaged()) + { + setCaged(false); + } + + this.caged = true; + this.location = location; + this.outerMaterial = outer; + this.innerMaterial = inner; + input = null; + + buildHistory(location); + regenerate(); + } + + public void cage(Location location, Material outer, Material inner, String input) + { + if (isCaged()) + { + setCaged(false); + } + + this.caged = true; + this.location = location; + this.outerMaterial = outer; + this.innerMaterial = inner; + CageData.input = input; + + buildHistory(location); + regenerate(); + } + + public void regenerate() + { + + if (!caged + || location == null + || outerMaterial == null + || innerMaterial == null) + { + return; + } + + generateHollowCube(location, 2, outerMaterial); + generateCube(location, 1, innerMaterial); + } + + // TODO: EventHandler this? + public void playerJoin() + { + if (!isCaged()) + { + return; + } + + cage(fPlayer.getPlayer().getLocation(), outerMaterial, innerMaterial, input); + } + + public void playerQuit() + { + regenerateHistory(); + clearHistory(); + } + + public void clearHistory() + { + cageHistory.clear(); + } + + private void insertHistoryBlock(Location location, Material material) + { + cageHistory.add(new BlockData(location, material)); + } + + private void regenerateHistory() + { + for (BlockData blockdata : this.cageHistory) + { + blockdata.location.getBlock().setType(blockdata.material); + } + } + + private void buildHistory(Location location) + { + final Block center = location.getBlock(); + for (int xOffset = -2; xOffset <= 2; xOffset++) + { + for (int yOffset = -2; yOffset <= 2; yOffset++) + { + for (int zOffset = -2; zOffset <= 2; zOffset++) + { + final Block block = center.getRelative(xOffset, yOffset, zOffset); + insertHistoryBlock(block.getLocation(), block.getType()); + } + } + } + } + + public FPlayer getfPlayer() + { + return fPlayer; + } + + public List getCageHistory() + { + return cageHistory; + } + + public boolean isCaged() + { + return caged; + } + + public void setCaged(boolean cage) + { + if (cage) + { + cage(fPlayer.getPlayer().getLocation(), Material.GLASS, Material.GLASS); + } + else + { + this.caged = false; + regenerateHistory(); + clearHistory(); + } + + } + + public Location getLocation() + { + return location; + } + + public void setLocation(Location location) + { + this.location = location; + } + + public Material getOuterMaterial() + { + return outerMaterial; + } + + public void setOuterMaterial(Material outerMaterial) + { + this.outerMaterial = outerMaterial; + } + + public Material getInnerMaterial() + { + return innerMaterial; + } + + public void setInnerMaterial(Material innerMaterial) + { + this.innerMaterial = innerMaterial; + } + + private static class BlockData + { + + public Material material; + public Location location; + + private BlockData(Location location, Material material) + { + this.location = location; + this.material = material; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/caging/Cager.java b/src/main/java/me/totalfreedom/totalfreedommod/caging/Cager.java new file mode 100644 index 00000000..d520f7ba --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/caging/Cager.java @@ -0,0 +1,116 @@ +package me.totalfreedom.totalfreedommod.caging; + +import io.papermc.lib.PaperLib; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class Cager extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBreakBlock(BlockBreakEvent event) + { + Player player = event.getPlayer(); + if (plugin.al.isAdmin(player)) + { + return; + } + + FPlayer fPlayer = plugin.pl.getPlayer(event.getPlayer()); + CageData cage = fPlayer.getCageData(); + + if (cage.isCaged()) + { + event.setCancelled(true); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) + { + FPlayer player = plugin.pl.getPlayer(event.getPlayer()); + CageData cage = player.getCageData(); + + if (!cage.isCaged()) + { + return; + } + + Location playerLoc = player.getPlayer().getLocation().add(0, 1, 0); + Location cageLoc = cage.getLocation(); + + final boolean outOfCage; + if (!Objects.equals(playerLoc.getWorld(), cageLoc.getWorld())) + { + outOfCage = true; + } + else + { + outOfCage = playerLoc.distanceSquared(cageLoc) > (2.5D * 2.5D); + } + + if (outOfCage) + { + PaperLib.teleportAsync(player.getPlayer(), cageLoc.subtract(0, 0.1, 0)); + FUtil.playerMsg(player.getPlayer(), "You may not leave your cage.", ChatColor.RED); + cage.regenerate(); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerQuit(PlayerQuitEvent event) + { + FPlayer player = plugin.pl.getPlayer(event.getPlayer()); + CageData cage = player.getCageData(); + + if (cage.isCaged()) + { + cage.playerQuit(); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerKick(PlayerKickEvent event) + { + FPlayer player = plugin.pl.getPlayer(event.getPlayer()); + CageData cage = player.getCageData(); + + if (cage.isCaged()) + { + cage.playerQuit(); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent event) + { + FPlayer player = plugin.pl.getPlayer(event.getPlayer()); + CageData cage = player.getCageData(); + + if (cage.isCaged()) + { + cage.playerJoin(); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/CommandFailException.java b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandFailException.java new file mode 100644 index 00000000..9792fd48 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandFailException.java @@ -0,0 +1,13 @@ +package me.totalfreedom.totalfreedommod.command; + +public class CommandFailException extends RuntimeException +{ + + private static final long serialVersionUID = -92333791173123L; + + public CommandFailException(String message) + { + super(message); + } + +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/CommandLoader.java b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandLoader.java new file mode 100644 index 00000000..ed3acd5f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandLoader.java @@ -0,0 +1,85 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.reflections.Reflections; + +public class CommandLoader extends FreedomService +{ + private final List commands; + + public CommandLoader() + { + commands = new ArrayList<>(); + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public void add(FreedomCommand command) + { + commands.add(command); + command.register(); + } + + public FreedomCommand getByName(String name) + { + for (FreedomCommand command : commands) + { + if (name.equals(command.getName())) + { + return command; + } + } + return null; + } + + public boolean isAlias(String alias) + { + for (FreedomCommand command : commands) + { + if (Arrays.asList(command.getAliases().split(",")).contains(alias)) + { + return true; + } + } + return false; + } + + public void loadCommands() + { + Reflections commandDir = new Reflections("me.totalfreedom.totalfreedommod.command"); + + Set> commandClasses = commandDir.getSubTypesOf(FreedomCommand.class); + + for (Class commandClass : commandClasses) + { + try + { + add(commandClass.newInstance()); + } + catch (InstantiationException | IllegalAccessException | ExceptionInInitializerError ex) + { + FLog.warning("Failed to register command: /" + commandClass.getSimpleName().replace("Command_", "")); + } + } + + FLog.info("Loaded " + commands.size() + " commands"); + } + + public List getCommands() + { + return commands; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/CommandParameters.java b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandParameters.java new file mode 100644 index 00000000..bae0e188 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandParameters.java @@ -0,0 +1,15 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandParameters +{ + + String description(); + + String usage(); + + String aliases() default ""; // "alias1,alias2,alias3" - no spaces +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/CommandPermissions.java b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandPermissions.java new file mode 100644 index 00000000..3db48004 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/CommandPermissions.java @@ -0,0 +1,18 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import me.totalfreedom.totalfreedommod.rank.Rank; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandPermissions +{ + + Rank level() default Rank.NON_OP; + + SourceType source() default SourceType.BOTH; + + boolean blockHostConsole() default false; + + int cooldown() default 0; +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminchat.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminchat.java new file mode 100644 index 00000000..d1fd6b30 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminchat.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Talk privately with other admins on the server.", usage = "/ [message]", aliases = "o,sc,ac,staffchat") +public class Command_adminchat extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + if (senderIsConsole) + { + msg("You must be in-game to toggle admin chat, it cannot be toggled via CONSOLE or Telnet."); + return true; + } + + FPlayer userinfo = plugin.pl.getPlayer(playerSender); + userinfo.setAdminChat(!userinfo.inAdminChat()); + msg("Admin chat turned " + (userinfo.inAdminChat() ? "on" : "off") + "."); + } + else + { + plugin.cm.adminChat(sender, StringUtils.join(args, " ")); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_admininfo.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_admininfo.java new file mode 100644 index 00000000..7708e0dd --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_admininfo.java @@ -0,0 +1,33 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Information on how to apply for admin.", usage = "/", aliases = "si,ai,staffinfo") +public class Command_admininfo extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + List adminInfo = ConfigEntry.ADMIN_INFO.getStringList(); + + if (adminInfo.isEmpty()) + { + msg("The admin information section of the config.yml file has not been configured.", ChatColor.RED); + } + else + { + msg(FUtil.colorize(StringUtils.join(adminInfo, "\n"))); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminmode.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminmode.java new file mode 100644 index 00000000..83415650 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminmode.java @@ -0,0 +1,59 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Denies joining of operators and only allows admins to join.", usage = "/ [on | off]", aliases = "staffmode") +public class Command_adminmode extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("off")) + { + ConfigEntry.ADMIN_ONLY_MODE.setBoolean(false); + FUtil.adminAction(sender.getName(), "Opening the server to all players", true); + return true; + } + else if (args[0].equalsIgnoreCase("on")) + { + ConfigEntry.ADMIN_ONLY_MODE.setBoolean(true); + FUtil.adminAction(sender.getName(), "Closing the server to non-admins", true); + for (Player player : server.getOnlinePlayers()) + { + if (!isAdmin(player)) + { + player.kickPlayer("Server is now closed to non-admins."); + } + } + return true; + } + + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isAdmin(sender) && !(sender instanceof Player)) + { + return Arrays.asList("on", "off"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminworld.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminworld.java new file mode 100644 index 00000000..42942e2f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adminworld.java @@ -0,0 +1,201 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.world.WorldTime; +import me.totalfreedom.totalfreedommod.world.WorldWeather; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Allows for admins to configure time, and weather of the AdminWorld, and allows for admins and ops to go to the AdminWorld.", + usage = "/ [time | weather ]", + aliases = "sw,aw,staffworld") +public class Command_adminworld extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + CommandMode commandMode = null; + + if (args.length == 0) + { + commandMode = CommandMode.TELEPORT; + } + else if (args.length >= 2) + { + if ("time".equalsIgnoreCase(args[0])) + { + commandMode = CommandMode.TIME; + } + else if ("weather".equalsIgnoreCase(args[0])) + { + commandMode = CommandMode.WEATHER; + } + } + + if (commandMode == null) + { + return false; + } + + try + { + switch (commandMode) + { + case TELEPORT: + { + if (!(sender instanceof Player) || playerSender == null) + { + return false; + } + + World adminWorld = null; + try + { + adminWorld = plugin.wm.adminworld.getWorld(); + } + catch (Exception ignored) + { + } + + if (adminWorld == null || playerSender.getWorld() == adminWorld) + { + msg("Going to the main world."); + PaperLib.teleportAsync(playerSender, server.getWorlds().get(0).getSpawnLocation()); + } + else + { + msg("Going to the AdminWorld."); + plugin.wm.adminworld.sendToWorld(playerSender); + } + break; + } + case TIME: + { + assertCommandPerms(sender, playerSender); + + if (args.length == 2) + { + WorldTime timeOfDay = WorldTime.getByAlias(args[1]); + if (timeOfDay != null) + { + plugin.wm.adminworld.setTimeOfDay(timeOfDay); + msg("AdminWorld time set to: " + timeOfDay.name()); + } + else + { + msg("Invalid time of day. Can be: sunrise, noon, sunset, midnight"); + } + } + else + { + return false; + } + + break; + } + case WEATHER: + { + assertCommandPerms(sender, playerSender); + + if (args.length == 2) + { + WorldWeather weatherMode = WorldWeather.getByAlias(args[1]); + if (weatherMode != null) + { + plugin.wm.adminworld.setWeatherMode(weatherMode); + msg("AdminWorld weather set to: " + weatherMode.name()); + } + else + { + msg("Invalid weather mode. Can be: off, rain, storm"); + } + } + else + { + return false; + } + + break; + } + default: + { + return false; + } + } + } + catch (PermissionDeniedException ex) + { + if (ex.getMessage().isEmpty()) + { + return noPerms(); + } + msg(ex.getMessage()); + return true; + } + + return true; + } + + // TODO: Redo this properly + private void assertCommandPerms(CommandSender sender, Player playerSender) throws PermissionDeniedException + { + if (!(sender instanceof Player) || playerSender == null || !isAdmin(sender)) + { + throw new PermissionDeniedException(); + } + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + if (args.length == 1) + { + return Arrays.asList("time", "weather"); + } + else if (args.length == 2) + { + if (args[0].equals("time")) + { + return Arrays.asList("morning", "noon", "evening", "night"); + } + else if (args[0].equals("weather")) + { + return Arrays.asList("off", "rain", "storm"); + } + } + return Collections.emptyList(); + } + + private enum CommandMode + { + TELEPORT, TIME, WEATHER + } + + private static class PermissionDeniedException extends Exception + { + + private static final long serialVersionUID = 1L; + + private PermissionDeniedException() + { + super(""); + } + + private PermissionDeniedException(String string) + { + super(string); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adventure.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adventure.java new file mode 100644 index 00000000..605b6de2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_adventure.java @@ -0,0 +1,58 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Quickly change your own gamemode to adventure, define someone's username to change theirs, or change everyone's gamemode on the server.", usage = "/ <[partialname] | -a>", aliases = "gma") +public class Command_adventure extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + if (isConsole()) + { + msg("When used from the console, you must define a target player."); + return true; + } + + playerSender.setGameMode(GameMode.ADVENTURE); + msg("Your gamemode has been set to adventure."); + return true; + } + + checkRank(Rank.ADMIN); + + if (args[0].equals("-a")) + { + for (Player targetPlayer : server.getOnlinePlayers()) + { + targetPlayer.setGameMode(GameMode.ADVENTURE); + } + + FUtil.adminAction(sender.getName(), "Changing everyone's gamemode to adventure", false); + msg("Your gamemode has been set to adventure."); + return true; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg("Setting " + player.getName() + " to game mode adventure."); + msg(player, sender.getName() + " set your game mode to adventure."); + player.setGameMode(GameMode.ADVENTURE); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_aeclear.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_aeclear.java new file mode 100644 index 00000000..41f87b32 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_aeclear.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Clears lingering potion area effect clouds.", usage = "/", aliases = "aec") +public class Command_aeclear extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "Removing all area effect clouds", true); + int removed = 0; + for (World world : server.getWorlds()) + { + for (Entity entity : world.getEntities()) + { + if (entity instanceof AreaEffectCloud) + { + entity.remove(); + removed++; + } + } + } + msg(removed + " area effect clouds removed."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_announce.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_announce.java new file mode 100644 index 00000000..e0e49a7b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_announce.java @@ -0,0 +1,25 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Make an announcement anonymously to operators.", usage = "/ ") +public class Command_announce extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + plugin.an.announce(StringUtils.join(args, " ")); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_attributelist.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_attributelist.java new file mode 100644 index 00000000..6a7697d7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_attributelist.java @@ -0,0 +1,30 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.attribute.Attribute; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Lists all possible attributes.", usage = "/") +public class Command_attributelist extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + StringBuilder list = new StringBuilder("All possible attributes: "); + + for (Attribute attribute : Attribute.values()) + { + list.append(attribute.name()).append(", "); + } + + // Remove extra comma at the end of the list + list = new StringBuilder(list.substring(0, list.length() - 2)); + + msg(list.toString()); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autoclear.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autoclear.java new file mode 100644 index 00000000..b4de03ae --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autoclear.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle whether or not a player has their inventory automatically cleared when they join", usage = "/ ") +public class Command_autoclear extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean enabled = plugin.lp.CLEAR_ON_JOIN.contains(args[0]); + + if (enabled) + { + plugin.lp.CLEAR_ON_JOIN.remove(args[0]); + } + else + { + plugin.lp.CLEAR_ON_JOIN.add(args[0]); + } + + msg(args[0] + " will " + (enabled ? "no longer" : "now") + " have their inventory cleared when they join."); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autotp.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autotp.java new file mode 100644 index 00000000..26bbec56 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_autotp.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle whether or not a player is automatically teleported when they join", usage = "/ ") +public class Command_autotp extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean enabled = plugin.lp.TELEPORT_ON_JOIN.contains(args[0]); + + if (enabled) + { + plugin.lp.TELEPORT_ON_JOIN.remove(args[0]); + } + else + { + plugin.lp.TELEPORT_ON_JOIN.add(args[0]); + } + + msg(args[0] + " will " + (enabled ? "no longer" : "now") + " be automatically teleported when they join."); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ban.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ban.java new file mode 100644 index 00000000..c6b38381 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ban.java @@ -0,0 +1,179 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Bans the specified player.", usage = "/ [reason] [-nrb | -q]", aliases = "gtfo") +public class Command_ban extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + String reason = null; + boolean silent = false; + boolean cancelRollback = false; + if (args.length >= 2) + { + if (args[args.length - 1].equalsIgnoreCase("-nrb") || args[args.length - 1].equalsIgnoreCase("-q")) + { + if (args[args.length - 1].equalsIgnoreCase("-nrb")) + { + cancelRollback = true; + } + + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + silent = true; + } + + if (args.length >= 3) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + } + + final String username; + final List ips = new ArrayList<>(); + + final Player player = getPlayer(args[0]); + if (player == null) + { + final PlayerData entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + username = entry.getName(); + ips.addAll(entry.getIps()); + } + else + { + final PlayerData entry = plugin.pl.getData(player); + username = player.getName(); + //ips.addAll(entry.getIps());/ + ips.add(FUtil.getIp(player)); + + // Deop + player.setOp(false); + + // Gamemode survival + player.setGameMode(GameMode.SURVIVAL); + + // Clear inventory + player.getInventory().clear(); + + if (!silent) + { + // Strike with lightning + final Location targetPos = player.getLocation(); + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + final Location strike_pos = new Location(targetPos.getWorld(), targetPos.getBlockX() + x, targetPos.getBlockY(), targetPos.getBlockZ() + z); + Objects.requireNonNull(targetPos.getWorld()).strikeLightning(strike_pos); + } + } + } + else + { + msg("Banned " + player.getName() + " quietly."); + } + // Kill player + player.setHealth(0.0); + } + + // Checks if CoreProtect is loaded and installed, and skips the rollback and uses CoreProtect directly + if (!cancelRollback) + { + plugin.cpb.rollback(username); + } + + if (player != null && !silent) + { + FUtil.bcastMsg(player.getName() + " has been a VERY naughty, naughty boy.", ChatColor.RED); + } + + // Ban player + Ban ban; + + if (player != null) + { + ban = Ban.forPlayer(player, sender, null, reason); + } + else + { + ban = Ban.forPlayerName(username, sender, null, reason); + } + + for (String ip : ips) + { + ban.addIp(ip); + ban.addIp(FUtil.getFuzzyIp(ip)); + } + plugin.bm.addBan(ban); + + + if (!silent) + { + // Broadcast + final StringBuilder bcast = new StringBuilder() + .append("Banning: ") + .append(username); + if (reason != null) + { + bcast.append(" - Reason: ").append(ChatColor.YELLOW).append(reason); + } + msg(sender, ChatColor.GRAY + username + " has been banned and IP is: " + StringUtils.join(ips, ", ")); + FUtil.adminAction(sender.getName(), bcast.toString(), true); + } + + // Kick player and handle others on IP + if (player != null) + { + player.kickPlayer(ban.bakeKickMessage()); + for (Player p : Bukkit.getOnlinePlayers()) + { + if (FUtil.getIp(p).equals(FUtil.getIp(player))) + { + p.kickPlayer(ChatColor.RED + "You've been kicked because someone on your IP has been banned."); + } + } + } + + // Log ban + plugin.pul.logPunishment(new Punishment(username, ips.get(0), sender.getName(), PunishmentType.BAN, reason)); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banip.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banip.java new file mode 100644 index 00000000..9066e83c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banip.java @@ -0,0 +1,82 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Bans the specified ip.", usage = "/ [reason] [-q]") +public class Command_banip extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean silent = false; + + String reason = null; + + String ip = args[0]; + + if (FUtil.isValidIPv4(ip)) + { + msg(ip + " is not a valid IP address", ChatColor.RED); + return true; + } + + if (plugin.bm.getByIp(ip) != null) + { + msg("The IP " + ip + " is already banned", ChatColor.RED); + return true; + } + + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + silent = true; + + if (args.length >= 2) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else if (args.length > 1) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + + // Ban player + Ban ban = Ban.forPlayerIp(ip, sender, null, reason); + plugin.bm.addBan(ban); + + // Kick player and handle others on IP + for (Player player : server.getOnlinePlayers()) + { + if (FUtil.getIp(player).equals(ip)) + { + player.kickPlayer(ban.bakeKickMessage()); + } + + if (!silent) + { + // Broadcast + FLog.info(ChatColor.RED + sender.getName() + " - Banned the IP " + ip); + String message = sender.getName() + " - Banned " + (plugin.al.isAdmin(player) ? "the IP " + ip : "an IP"); + msg(player, message, ChatColor.RED); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banlist.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banlist.java new file mode 100644 index 00000000..72ae69d4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banlist.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Shows all banned player names. Admins may optionally use 'purge' to clear the list.", usage = "/ [purge]") +public class Command_banlist extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0) + { + if (args[0].equalsIgnoreCase("purge")) + { + checkRank(Rank.SENIOR_ADMIN); + + FUtil.adminAction(sender.getName(), "Purging the ban list", true); + int amount = plugin.bm.purge(); + msg("Purged " + amount + " player bans."); + return true; + } + return false; + } + + msg(plugin.bm.getAllBans().size() + " player bans (" + + plugin.bm.getUsernameBans().size() + " usernames, " + + plugin.bm.getIpBans().size() + " IPs)"); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banname.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banname.java new file mode 100644 index 00000000..f7b16cfa --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_banname.java @@ -0,0 +1,68 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Bans the specified name.", usage = "/ [reason] [-q]") +public class Command_banname extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean silent = false; + + String reason = null; + + String name = args[0]; + + if (plugin.bm.getByUsername(name) != null) + { + msg("The name " + name + " is already banned", ChatColor.RED); + return true; + } + + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + silent = true; + + if (args.length >= 2) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else if (args.length > 1) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + + // Ban player + Ban ban = Ban.forPlayerName(name, sender, null, reason); + plugin.bm.addBan(ban); + + if (!silent) + { + FUtil.adminAction(sender.getName(), "Banned the name " + name, true); + } + + Player player = getPlayer(name); + if (player != null) + { + player.kickPlayer(ban.bakeKickMessage()); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_bird.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_bird.java new file mode 100644 index 00000000..87291599 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_bird.java @@ -0,0 +1,33 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.List; +import java.util.SplittableRandom; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Spawns a random type of fish at your location.", usage = "/") +public class Command_bird extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Location location = playerSender.getTargetBlock(null, 15).getLocation().add(0, 1, 0); + playerSender.getWorld().spawnEntity(location, getRandomFish()); + msg(":goodbird:"); + return true; + } + + public EntityType getRandomFish() + { + List fishTypes = Arrays.asList(EntityType.COD, EntityType.SALMON, EntityType.PUFFERFISH, EntityType.TROPICAL_FISH); + SplittableRandom random = new SplittableRandom(); + return fishTypes.get(random.nextInt(fishTypes.size())); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockcmd.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockcmd.java new file mode 100644 index 00000000..fece338d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockcmd.java @@ -0,0 +1,88 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Block all commands for everyone on the server, or a specific player.", usage = "/ <-a | purge | >", aliases = "blockcommands,blockcommand,bc,bcmd") +public class Command_blockcmd extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (args[0].equals("purge")) + { + FUtil.adminAction(sender.getName(), "Unblocking commands for all players", true); + int counter = 0; + for (Player player : server.getOnlinePlayers()) + { + FPlayer playerdata = plugin.pl.getPlayer(player); + if (playerdata.allCommandsBlocked()) + { + counter += 1; + playerdata.setCommandsBlocked(false); + } + } + msg("Unblocked commands for " + counter + " players."); + return true; + } + + if (args[0].equals("-a")) + { + FUtil.adminAction(sender.getName(), "Blocking commands for all non-admins", true); + int counter = 0; + for (Player player : server.getOnlinePlayers()) + { + if (isAdmin(player)) + { + continue; + } + + counter += 1; + plugin.pl.getPlayer(player).setCommandsBlocked(true); + msg(player, "Your commands have been blocked by an admin.", ChatColor.RED); + } + + msg("Blocked commands for " + counter + " players."); + return true; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + if (isAdmin(player)) + { + msg(player.getName() + " is an admin, and cannot have their commands blocked."); + return true; + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + if (!playerdata.allCommandsBlocked()) + { + playerdata.setCommandsBlocked(true); + FUtil.adminAction(sender.getName(), "Blocking all commands for " + player.getName(), true); + msg("Blocked commands for " + player.getName() + "."); + } + else + { + msg("That players commands are already blocked.", ChatColor.RED); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockedit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockedit.java new file mode 100644 index 00000000..e1904553 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockedit.java @@ -0,0 +1,134 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Restricts/unrestricts block modification abilities for everyone on the server or a certain player.", usage = "/ [[-s] [reason] | list | purge | all]") +public class Command_blockedit extends FreedomCommand +{ + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, String[] args, final boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + if (args[0].equals("list")) + { + msg("The following have block modification abilities restricted:"); + int count = 0; + for (Player player : server.getOnlinePlayers()) + { + final FPlayer info = plugin.pl.getPlayer(player); + if (info.isEditBlocked()) + { + msg("- " + player.getName()); + ++count; + } + } + + if (count == 0) + { + msg("- none"); + } + return true; + } + + if (args[0].equals("purge")) + { + FUtil.adminAction(sender.getName(), "Unblocking block modification abilities for all players", true); + int count = 0; + for (final Player player : this.server.getOnlinePlayers()) + { + final FPlayer info = plugin.pl.getPlayer(player); + if (info.isEditBlocked()) + { + info.setEditBlocked(false); + ++count; + } + } + msg("Unblocked all block modification abilities for " + count + " players."); + return true; + } + + if (args[0].equals("all")) + { + FUtil.adminAction(sender.getName(), "Blocking block modification abilities for all non-admins", true); + int counter = 0; + for (final Player player : this.server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + final FPlayer playerdata = plugin.pl.getPlayer(player); + playerdata.setEditBlocked(true); + ++counter; + } + } + + msg("Blocked block modification abilities for " + counter + " players."); + return true; + } + + final boolean smite = args[0].equals("-s"); + if (smite) + { + args = (String[])ArrayUtils.subarray(args, 1, args.length); + if (args.length < 1) + { + return false; + } + } + + final Player player2 = getPlayer(args[0]); + if (player2 == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + String reason = null; + if (args.length > 1) + { + reason = StringUtils.join(args, " ", 1, args.length); + } + + final FPlayer pd = plugin.pl.getPlayer(player2); + if (pd.isEditBlocked()) + { + FUtil.adminAction(sender.getName(), "Unblocking block modification abilities for " + player2.getName(), true); + pd.setEditBlocked(false); + msg("Unblocking block modification abilities for " + player2.getName()); + msg(player2, "Your block modification abilities have been restored.", ChatColor.RED); + } + else + { + if (plugin.al.isAdmin(player2)) + { + msg(player2.getName() + " is an admin, and cannot have their block edits blocked."); + return true; + } + + FUtil.adminAction(sender.getName(), "Blocking block modification abilities for " + player2.getName(), true); + pd.setEditBlocked(true); + + if (smite) + { + Command_smite.smite(sender, player2, reason); + } + + msg(player2, "Your block modification abilities have been blocked.", ChatColor.RED); + msg("Blocked all block modification abilities for " + player2.getName()); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockpvp.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockpvp.java new file mode 100644 index 00000000..0471724c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockpvp.java @@ -0,0 +1,134 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle PVP mode for everyone or a certain player.", usage = "/ [[-s] [reason] | list | purge | all]", aliases = "pvpblock,pvpmode") +public class Command_blockpvp extends FreedomCommand +{ + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, String[] args, final boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + if (args[0].equals("list")) + { + msg("PVP is blocked for players:"); + int count = 0; + for (Player player : server.getOnlinePlayers()) + { + final FPlayer info = plugin.pl.getPlayer(player); + if (info.isPvpBlocked()) + { + msg(" - " + player.getName()); + ++count; + } + } + + if (count == 0) + { + msg(" - none"); + } + return true; + } + + if (args[0].equals("purge")) + { + FUtil.adminAction(sender.getName(), "Enabling PVP for all players.", true); + int count = 0; + for (Player player : server.getOnlinePlayers()) + { + final FPlayer info = plugin.pl.getPlayer(player); + if (info.isPvpBlocked()) + { + info.setPvpBlocked(false); + ++count; + } + } + + msg("Enabled PVP for " + count + " players."); + return true; + } + + if (args[0].equals("all")) + { + FUtil.adminAction(sender.getName(), "Disabling PVP for all non-admins", true); + int counter = 0; + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + final FPlayer playerdata = plugin.pl.getPlayer(player); + playerdata.setPvpBlocked(true); + ++counter; + } + } + + msg("Disabling PVP for " + counter + " players."); + return true; + } + + final boolean smite = args[0].equals("-s"); + if (smite) + { + args = ArrayUtils.subarray(args, 1, args.length); + if (args.length < 1) + { + return false; + } + } + + final Player p = getPlayer(args[0]); + if (p == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + String reason = null; + if (args.length > 1) + { + reason = StringUtils.join(args, " ", 1, args.length); + } + + final FPlayer pd = plugin.pl.getPlayer(p); + if (pd.isPvpBlocked()) + { + FUtil.adminAction(sender.getName(), "Enabling PVP for " + p.getName(), true); + pd.setPvpBlocked(false); + msg("Enabling PVP for " + p.getName()); + msg(p, "Your PVP have been enabled.", ChatColor.GREEN); + } + else + { + if (plugin.al.isAdmin(p)) + { + msg(p.getName() + " is an admin, and cannot have their PVP disabled."); + return true; + } + + FUtil.adminAction(sender.getName(), "Disabling PVP for " + p.getName(), true); + pd.setPvpBlocked(true); + if (smite) + { + Command_smite.smite(sender, p, reason); + } + + msg(p, "Your PVP has been disabled.", ChatColor.RED); + msg("Disabled PVP for " + p.getName()); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockredstone.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockredstone.java new file mode 100644 index 00000000..f4a13a32 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_blockredstone.java @@ -0,0 +1,41 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Blocks redstone on the server.", usage = "/", aliases = "bre") +public class Command_blockredstone extends FreedomCommand +{ + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (ConfigEntry.ALLOW_REDSTONE.getBoolean()) + { + ConfigEntry.ALLOW_REDSTONE.setBoolean(false); + FUtil.adminAction(sender.getName(), "Blocking all redstone", true); + new BukkitRunnable() + { + public void run() + { + if (!ConfigEntry.ALLOW_REDSTONE.getBoolean()) + { + FUtil.adminAction("TotalFreedom", "Unblocking all redstone", false); + ConfigEntry.ALLOW_REDSTONE.setBoolean(true); + } + } + }.runTaskLater(plugin, 6000L); + } + else + { + ConfigEntry.ALLOW_REDSTONE.setBoolean(true); + FUtil.adminAction(sender.getName(), "Unblocking all redstone", true); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cage.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cage.java new file mode 100644 index 00000000..2ba3812d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cage.java @@ -0,0 +1,149 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Place a cage around someone with certain blocks, or someone's player head.", usage = "/ [head | block] [playername | blockname]") +public class Command_cage extends FreedomCommand +{ + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + String skullName = null; + if (args[0].equalsIgnoreCase("purge")) + { + FUtil.adminAction(sender.getName(), "Uncaging all players", true); + for (Player player : server.getOnlinePlayers()) + { + final FPlayer fPlayer = plugin.pl.getPlayer(player); + fPlayer.getCageData().setCaged(false); + } + return true; + } + + Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + final FPlayer fPlayer = plugin.pl.getPlayer(player); + if (fPlayer.getCageData().isCaged()) + { + msg("That player is already caged.", ChatColor.RED); + return true; + } + + Material outerMaterial = Material.GLASS; + Material innerMaterial = Material.AIR; + if (args.length >= 2 && args[1] != null) + { + final String s = args[1]; + switch (s) + { + case "head": + { + outerMaterial = Material.PLAYER_HEAD; + if (args.length >= 3) + { + skullName = args[2]; + } + else + { + outerMaterial = Material.SKELETON_SKULL; + } + break; + } + case "block": + { + if (Material.matchMaterial(args[2]) != null) + { + outerMaterial = Material.matchMaterial(args[2]); + break; + } + msg("Invalid block!", ChatColor.RED); + break; + } + } + } + + Location location = player.getLocation().clone().add(0.0, 1.0, 0.0); + + if (skullName != null) + { + fPlayer.getCageData().cage(location, outerMaterial, innerMaterial, skullName); + } + else + { + fPlayer.getCageData().cage(location, outerMaterial, innerMaterial); + } + + player.setGameMode(GameMode.SURVIVAL); + + if (outerMaterial == Material.PLAYER_HEAD) + { + FUtil.adminAction(sender.getName(), "Caging " + player.getName() + " in " + skullName, true); + } + else + { + FUtil.adminAction(sender.getName(), "Caging " + player.getName(), true); + } + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return null; + } + + if (args.length == 1) + { + List arguments = new ArrayList<>(); + arguments.add("purge"); + arguments.addAll(FUtil.getPlayerList()); + return arguments; + } + else if (args.length == 2) + { + if (!args[0].equals("purge")) + { + return Arrays.asList("head", "block"); + } + } + else if (args.length == 3) + { + if (args[1].equals("block")) + { + return FUtil.getAllMaterialNames(); + } + else if (args[1].equals("head")) + { + return FUtil.getPlayerList(); + } + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cake.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cake.java new file mode 100644 index 00000000..908e711a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cake.java @@ -0,0 +1,48 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "For the people that are still alive - gives a cake to everyone on the server.", usage = "/") +public class Command_cake extends FreedomCommand +{ + + public static final String CAKE_LYRICS = "But there's no sense crying over every mistake. You just keep on trying till you run out of cake."; + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + final StringBuilder output = new StringBuilder(); + + for (final String word : CAKE_LYRICS.split(" ")) + { + output.append(FUtil.randomChatColor()).append(word).append(" "); + } + + final ItemStack heldItem = new ItemStack(Material.CAKE); + final ItemMeta heldItemMeta = heldItem.getItemMeta(); + assert heldItemMeta != null; + heldItemMeta.setDisplayName(ChatColor.WHITE + "The " + ChatColor.DARK_GRAY + "Lie"); + heldItem.setItemMeta(heldItemMeta); + + for (Player player : server.getOnlinePlayers()) + { + final int firstEmpty = player.getInventory().firstEmpty(); + if (firstEmpty >= 0) + { + player.getInventory().setItem(firstEmpty, heldItem); + } + } + + FUtil.bcastMsg(output.toString()); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cartsit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cartsit.java new file mode 100644 index 00000000..5e90f925 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cartsit.java @@ -0,0 +1,77 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Sit in nearest minecart. If target is in a minecart already, they will disembark.", usage = "/ [partialname]") +public class Command_cartsit extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Player targetPlayer = playerSender; + + if (args.length == 1 && plugin.al.isAdmin(sender)) + { + targetPlayer = getPlayer(args[0]); + + if (targetPlayer == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + } + + if (senderIsConsole) + { + if (targetPlayer == null) + { + msg("When used from the console, you must define a target player: /cartsit "); + return true; + } + } + + if (targetPlayer.isInsideVehicle()) + { + Objects.requireNonNull(targetPlayer.getVehicle()).eject(); + } + else + { + Minecart nearest_cart = null; + for (Minecart cart : targetPlayer.getWorld().getEntitiesByClass(Minecart.class)) + { + if (cart.isEmpty()) + { + if (nearest_cart == null) + { + nearest_cart = cart; + } + else + { + if (cart.getLocation().distanceSquared(targetPlayer.getLocation()) < nearest_cart.getLocation().distanceSquared(targetPlayer.getLocation())) + { + nearest_cart = cart; + } + } + } + } + + if (nearest_cart != null) + { + nearest_cart.addPassenger(targetPlayer); + } + else + { + msg("There are no empty minecarts in the target world."); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearchat.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearchat.java new file mode 100644 index 00000000..3e891b9d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearchat.java @@ -0,0 +1,30 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Clears the chat.", usage = "/", aliases = "cc") +public class Command_clearchat extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + for (int i = 0; i < 100; i++) + { + msg(player, ""); + } + } + } + FUtil.adminAction(sender.getName(), "Cleared chat", true); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java new file mode 100644 index 00000000..c5028626 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cleardiscordqueue.java @@ -0,0 +1,19 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_CONSOLE) +@CommandParameters(description = "Clear the Discord message queue.", usage = "/") +public class Command_cleardiscordqueue extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + plugin.dc.clearQueue(); + msg("Cleared the Discord message queue."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearinventory.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearinventory.java new file mode 100644 index 00000000..a5574813 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clearinventory.java @@ -0,0 +1,79 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Clear your inventory.", usage = "/ [player]", aliases = "ci,clear") +public class Command_clearinventory extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + if (args.length < 1) + { + if (senderIsConsole) + { + return false; + } + + playerSender.getInventory().clear(); + msg("Your inventory has been cleared."); + } + else + { + if (plugin.al.isAdmin(sender)) + { + if (args[0].equals("-a")) + { + FUtil.adminAction(sender.getName(), "Clearing everyone's inventory", true); + for (Player player : server.getOnlinePlayers()) + { + player.getInventory().clear(); + } + msg("Sucessfully cleared everyone's inventory."); + } + else + { + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + player.getInventory().clear(); + msg("Cleared " + player.getName() + "'s inventory."); + msg(player, sender.getName() + " has cleared your inventory."); + } + } + else + { + return noPerms(); + } + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isAdmin(sender)) + { + List players = FUtil.getPlayerList(); + players.add("-a"); + return players; + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clownfish.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clownfish.java new file mode 100644 index 00000000..3f2caaf1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_clownfish.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a clown fish", usage = "/") +public class Command_clownfish extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.CLOWN_FISH) && (!plugin.lp.CLOWNFISH_TOGGLE.contains(playerSender.getName()))) + { + playerSender.getInventory().addItem(plugin.sh.getClownFish()); + msg("You have been given a Clown Fish", ChatColor.GREEN); + } + else + { + msg("You do not own a Clown Fish or an admin has toggled your ability to use it. Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cmdspy.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cmdspy.java new file mode 100644 index 00000000..4d31ca04 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cmdspy.java @@ -0,0 +1,24 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Spy on commands", usage = "/", aliases = "commandspy") +public class Command_cmdspy extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Admin admin = plugin.al.getAdmin(playerSender); + admin.setCommandSpy(!admin.getCommandSpy()); + msg("CommandSpy " + (admin.getCommandSpy() ? "enabled." : "disabled.")); + plugin.al.save(admin); + plugin.al.updateTables(); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_coins.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_coins.java new file mode 100644 index 00000000..f50b8985 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_coins.java @@ -0,0 +1,54 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Shows the amount of coins you have or another player has", usage = "/ [playername]") +public class Command_coins extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!ConfigEntry.SHOP_ENABLED.getBoolean()) + { + msg("The shop is currently disabled!", ChatColor.RED); + return true; + } + Player p; + final String prefix = FUtil.colorize(ConfigEntry.SHOP_PREFIX.getString() + " "); + if (args.length > 0) + { + if (getPlayer(args[0]) != null) + { + p = getPlayer(args[0]); + } + else + { + msg(PLAYER_NOT_FOUND); + return true; + } + } + else + { + if (senderIsConsole) + { + msg(prefix + ChatColor.RED + "You are not a player, use /coins "); + return true; + } + else + { + p = playerSender; + } + } + PlayerData playerData = plugin.pl.getData(p); + msg(prefix + ChatColor.GREEN + (args.length > 0 ? p.getName() + " has " : "You have ") + ChatColor.RED + playerData.getCoins() + ChatColor.GREEN + " coins."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_colorme.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_colorme.java new file mode 100644 index 00000000..e5a3e966 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_colorme.java @@ -0,0 +1,55 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Essentials Interface Command - Set your nickname to a certain color.", usage = "/ ") +public class Command_colorme extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("list")) + { + msg("Colors: " + StringUtils.join(FUtil.CHAT_COLOR_NAMES.keySet(), ", ")); + return true; + } + + final String needle = args[0].trim().toLowerCase(); + ChatColor color = null; + for (Map.Entry entry : FUtil.CHAT_COLOR_NAMES.entrySet()) + { + if (entry.getKey().contains(needle)) + { + color = entry.getValue(); + break; + } + } + + if (color == null) + { + msg("Invalid color: " + needle + " - Use \"/colorme list\" to list colors."); + return true; + } + + final String newNick = color + ChatColor.stripColor(playerSender.getDisplayName()).trim() + ChatColor.WHITE; + + plugin.esb.setNickname(sender.getName(), newNick); + + msg("Your nickname is now: " + newNick); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_commandlist.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_commandlist.java new file mode 100644 index 00000000..b7fb63df --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_commandlist.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Show all commands for all server plugins.", usage = "/", aliases = "cmdlist") +public class Command_commandlist extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + List commands = new ArrayList<>(); + + for (Plugin targetPlugin : server.getPluginManager().getPlugins()) + { + try + { + PluginDescriptionFile desc = targetPlugin.getDescription(); + Map> map = desc.getCommands(); + + for (Entry> entry : map.entrySet()) + { + String command_name = entry.getKey(); + commands.add(command_name); + } + } + catch (Throwable ignored) + { + } + } + + Collections.sort(commands); + + msg(StringUtils.join(commands, ", ")); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_consolesay.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_consolesay.java new file mode 100644 index 00000000..4fb77b68 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_consolesay.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_CONSOLE) +@CommandParameters(description = "Telnet/Console command - Send a chat message with chat formatting over telnet.", usage = "/ ", aliases = "csay") +public class Command_consolesay extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + String message = StringUtils.join(args, " "); + FUtil.bcastMsg(String.format("§7[CONSOLE] §c%s §8\u00BB §f%s", sender.getName(), StringUtils.join(args, " "))); + plugin.dc.messageChatChannel("[CONSOLE] " + sender.getName() + " \u00BB " + ChatColor.stripColor(message)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cookie.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cookie.java new file mode 100644 index 00000000..e404ef14 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_cookie.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "For those who have no friends - gives a cookie to everyone on the server.", usage = "/") +public class Command_cookie extends FreedomCommand +{ + public static final String COOKIE_LYRICS = "Imagine that you have zero cookies and you split them evenly among zero friends. How many cookies does each person get? See? It doesn't make sense. And Cookie Monster is sad that there are no cookies, and you are sad that you have no friends."; + public static final String LORE = "But, you can have a cookie anyways,\nsince you are sad you are have no friends."; + + @Override + public boolean run(CommandSender sender, Player sender_p, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + final StringBuilder output = new StringBuilder(); + + for (final String word : COOKIE_LYRICS.split(" ")) + { + output.append(FUtil.randomChatColor()).append(word).append(" "); + } + + final StringBuilder lore = new StringBuilder(); + + for (final String word : LORE.split(" ")) + { + lore.append(FUtil.randomChatColor()).append(word).append(" "); + } + + final ItemStack heldItem = new ItemStack(Material.COOKIE); + final ItemMeta heldItemMeta = heldItem.getItemMeta(); + String name = ChatColor.DARK_RED + "C" + + ChatColor.GOLD + "o" + + ChatColor.YELLOW + "o" + + ChatColor.DARK_GREEN + "k" + + ChatColor.DARK_BLUE + "i" + + ChatColor.DARK_PURPLE + "e"; + assert heldItemMeta != null; + heldItemMeta.setDisplayName(name); + heldItemMeta.setLore(Arrays.asList(lore.toString().split("\n"))); + heldItem.setItemMeta(heldItemMeta); + + for (final Player player : server.getOnlinePlayers()) + { + final int firstEmpty = player.getInventory().firstEmpty(); + if (firstEmpty >= 0) + { + player.getInventory().setItem(firstEmpty, heldItem); + } + } + + FUtil.bcastMsg(output.toString()); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_creative.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_creative.java new file mode 100644 index 00000000..a24c8043 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_creative.java @@ -0,0 +1,59 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Quickly change your own gamemode to creative, define someone's username to change theirs, or change everyone's gamemode on the server.", usage = "/ <-a | [partialname]>", aliases = "gmc") +public class Command_creative extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + if (isConsole()) + { + msg("When used from the console, you must define a target player."); + return true; + } + + playerSender.setGameMode(GameMode.CREATIVE); + msg("Your gamemode has been set to creative."); + return true; + } + + checkRank(Rank.ADMIN); + + if (args[0].equals("-a")) + { + for (Player targetPlayer : server.getOnlinePlayers()) + { + targetPlayer.setGameMode(GameMode.CREATIVE); + } + + FUtil.adminAction(sender.getName(), "Changing everyone's gamemode to creative", false); + msg("Your gamemode has been set to creative."); + return true; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg("Setting " + player.getName() + " to game mode creative"); + msg(player, sender.getName() + " set your game mode to creative"); + player.setGameMode(GameMode.CREATIVE); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deafen.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deafen.java new file mode 100644 index 00000000..d3358487 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deafen.java @@ -0,0 +1,51 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.SplittableRandom; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Makes random sounds.", usage = "/") +public class Command_deafen extends FreedomCommand +{ + + public static final double STEPS = 10.0; + private static final SplittableRandom random = new SplittableRandom(); + + private static Location randomOffset(Location a) + { + return a.clone().add(randomDoubleRange() * 5.0, randomDoubleRange() * 5.0, randomDoubleRange() * 5.0); + } + + private static Double randomDoubleRange() + { + return -1.0 + (random.nextDouble() * ((1.0 - -1.0) + 1.0)); + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + { + for (double percent = 0.0; percent <= 1.0; percent += (1.0 / STEPS)) + { + final float pitch = (float)(percent * 2.0); + + new BukkitRunnable() + { + @Override + public void run() + { + playerSender.playSound(randomOffset(playerSender.getLocation()), Sound.values()[random.nextInt(Sound.values().length)], 100.0f, pitch); + } + }.runTaskLater(plugin, Math.round(20.0 * percent * 2.0)); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_debugstick.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_debugstick.java new file mode 100644 index 00000000..ea0e0b3d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_debugstick.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Get a stick of happiness.", usage = "/") +public class Command_debugstick extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + ItemStack itemStack = new ItemStack(Material.DEBUG_STICK); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.GOLD.toString() + ChatColor.BOLD.toString() + "Stick of Happiness"); + List lore = Arrays.asList( + ChatColor.RED + "This is the most powerful stick in the game.", + ChatColor.DARK_BLUE + "You can left click to select what you want to change.", + ChatColor.DARK_GREEN + "And then you can right click to change it!", + ChatColor.DARK_PURPLE + "Isn't technology amazing?"); + itemMeta.setLore(lore); + itemStack.setItemMeta(itemMeta); + playerSender.getInventory().addItem(itemStack); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_denick.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_denick.java new file mode 100644 index 00000000..cc90c8f2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_denick.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Essentials Interface Command - Remove the nickname of all players on the server.", usage = "/") +public class Command_denick extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.esb.isEnabled()) + { + msg("Essentials is not enabled on this server."); + return true; + } + + FUtil.adminAction(sender.getName(), "Removing all nicknames", false); + + for (Player player : server.getOnlinePlayers()) + { + plugin.esb.setNickname(player.getName(), null); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deop.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deop.java new file mode 100644 index 00000000..1cdddbe6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deop.java @@ -0,0 +1,62 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Deop a player", usage = "/ ") +public class Command_deop extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + boolean silent = false; + if (args.length == 2) + { + silent = args[1].equalsIgnoreCase("-s"); + } + + final String targetName = args[0].toLowerCase(); + + final List matchedPlayerNames = new ArrayList<>(); + for (Player player : server.getOnlinePlayers()) + { + if (player.getName().toLowerCase().contains(targetName) || player.getDisplayName().toLowerCase().contains(targetName) + || player.getName().contains(targetName) || player.getDisplayName().contains(targetName)) + { + if (player.isOp() && !plugin.al.isVanished(player.getName())) + { + matchedPlayerNames.add(player.getName()); + player.setOp(false); + msg(player, YOU_ARE_NOT_OP); + plugin.rm.updateDisplay(player); + } + } + } + + if (!matchedPlayerNames.isEmpty()) + { + if (!silent) + { + FUtil.adminAction(sender.getName(), "De-opping " + StringUtils.join(matchedPlayerNames, ", "), false); + } + } + else + { + msg("Either the player is already deopped, or the player could not be found."); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deopall.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deopall.java new file mode 100644 index 00000000..84817ad7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_deopall.java @@ -0,0 +1,28 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Deop everyone on the server.", usage = "/") +public class Command_deopall extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "De-opping all players on the server", true); + + for (Player player : server.getOnlinePlayers()) + { + player.setOp(false); + msg(player, YOU_ARE_NOT_OP); + plugin.rm.updateDisplay(player); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_disguisetoggle.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_disguisetoggle.java new file mode 100644 index 00000000..9a035fef --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_disguisetoggle.java @@ -0,0 +1,38 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle LibsDisguises for everyone online.", usage = "/", aliases = "dtoggle") +public class Command_disguisetoggle extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.ldb.isEnabled()) + { + msg("LibsDisguises is not enabled."); + return true; + } + + FUtil.adminAction(sender.getName(), (plugin.ldb.isDisguisesEnabled() ? "Disabling" : "Enabling") + " disguises", false); + + if (plugin.ldb.isDisguisesEnabled()) + { + plugin.ldb.undisguiseAll(true); + plugin.ldb.setDisguisesEnabled(false); + } + else + { + plugin.ldb.setDisguisesEnabled(true); + } + + msg("Disguises are now " + (plugin.ldb.isDisguisesEnabled() ? "enabled." : "disabled.")); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_dispfill.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_dispfill.java new file mode 100644 index 00000000..77d72ef3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_dispfill.java @@ -0,0 +1,102 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Dispenser; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Fill nearby dispensers with a set of items of your choice.", usage = "/ ") +public class Command_dispfill extends FreedomCommand +{ + + private static void setDispenserContents(final Block targetBlock, final ItemStack[] items) + { + if (targetBlock.getType() == Material.DISPENSER) + { + final Inventory dispenserInv = ((Dispenser)targetBlock.getState()).getInventory(); + dispenserInv.clear(); + dispenserInv.addItem(items); + } + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 2) + { + int radius; + + try + { + radius = Math.max(5, Math.min(25, Integer.parseInt(args[0]))); + } + catch (NumberFormatException ex) + { + msg("Invalid radius."); + return true; + } + + final List items = new ArrayList<>(); + + final String[] itemsRaw = StringUtils.split(args[1], ","); + for (final String searchItem : itemsRaw) + { + Material material = Material.matchMaterial(searchItem); + + if (material != null) + { + items.add(new ItemStack(material, 64)); + } + else + { + msg("Skipping invalid item: " + searchItem); + } + } + + final ItemStack[] itemsArray = items.toArray(new ItemStack[0]); + + int affected = 0; + final Location centerLocation = playerSender.getLocation(); + final Block centerBlock = centerLocation.getBlock(); + for (int xOffset = -radius; xOffset <= radius; xOffset++) + { + for (int yOffset = -radius; yOffset <= radius; yOffset++) + { + for (int zOffset = -radius; zOffset <= radius; zOffset++) + { + final Block targetBlock = centerBlock.getRelative(xOffset, yOffset, zOffset); + if (targetBlock.getLocation().distanceSquared(centerLocation) < (radius * radius)) + { + if (targetBlock.getType().equals(Material.DISPENSER)) + { + msg("Filling dispenser @ " + FUtil.formatLocation(targetBlock.getLocation())); + plugin.cpb.getCoreProtectAPI().logContainerTransaction(sender.getName(), targetBlock.getLocation()); + setDispenserContents(targetBlock, itemsArray); + affected++; + } + } + } + } + } + + msg("Done. " + affected + " dispenser(s) filled."); + } + else + { + return false; + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_doom.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_doom.java new file mode 100644 index 00000000..d6711948 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_doom.java @@ -0,0 +1,138 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_CONSOLE, blockHostConsole = true) +@CommandParameters(description = "Sends the specified player to their doom.", usage = "/ [reason]") +public class Command_doom extends FreedomCommand +{ + + @Override + public boolean run(final CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + FUtil.adminAction(sender.getName(), "Casting oblivion over " + player.getName(), true); + FUtil.bcastMsg(player.getName() + " will be completely obliviated!", ChatColor.RED); + + final String ip = Objects.requireNonNull(player.getAddress()).getAddress().getHostAddress().trim(); + + // Remove from admin + Admin admin = getAdmin(player); + if (admin != null) + { + FUtil.adminAction(sender.getName(), "Removing " + player.getName() + " from the admin list", true); + admin.setActive(false); + plugin.al.save(admin); + plugin.al.updateTables(); + plugin.ptero.updateAccountStatus(admin); + if (plugin.dc.enabled && ConfigEntry.DISCORD_ROLE_SYNC.getBoolean()) + { + Discord.syncRoles(admin, plugin.pl.getData(admin.getName()).getDiscordID()); + } + } + + // Remove from whitelist + player.setWhitelisted(false); + + // Deop + player.setOp(false); + + String reason = null; + + if (args.length > 1) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + + // Ban player + Ban ban = Ban.forPlayer(player, sender); + ban.setReason((reason == null ? "FUCKOFF" : reason)); + for (String playerIp : plugin.pl.getData(player).getIps()) + { + ban.addIp(playerIp); + } + plugin.bm.addBan(ban); + + // Set gamemode to survival + player.setGameMode(GameMode.SURVIVAL); + + // Clear inventory + player.closeInventory(); + player.getInventory().clear(); + + // Ignite player + player.setFireTicks(10000); + + // Generate explosion + player.getWorld().createExplosion(player.getLocation(), 0F, false); + + // Shoot the player in the sky + player.setVelocity(player.getVelocity().clone().add(new Vector(0, 20, 0))); + + final String kickReason = (reason == null ? "FUCKOFF, and get your shit together!" : reason); + + // Log doom + plugin.pul.logPunishment(new Punishment(player.getName(), FUtil.getIp(player), sender.getName(), PunishmentType.DOOM, reason)); + + new BukkitRunnable() + { + @Override + public void run() + { + // strike lightning + player.getWorld().strikeLightningEffect(player.getLocation()); + + // kill (if not done already) + player.setHealth(0.0); + } + }.runTaskLater(plugin, 2L * 20L); + + new BukkitRunnable() + { + @Override + public void run() + { + // message + FUtil.adminAction(sender.getName(), "Banning " + player.getName(), true); + msg(sender, player.getName() + " has been banned and IP is: " + ip); + + // generate explosion + player.getWorld().createExplosion(player.getLocation(), 0F, false); + + // kick player + player.kickPlayer(ChatColor.RED + kickReason); + } + }.runTaskLater(plugin, 3L * 20L); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_eject.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_eject.java new file mode 100644 index 00000000..9c72b8ad --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_eject.java @@ -0,0 +1,40 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Eject players that are riding you.", usage = "/") +public class Command_eject extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + List names = new ArrayList<>(); + + for (Entity entity : playerSender.getPassengers()) + { + names.add(entity.getName()); + } + + if (names.isEmpty()) + { + msg("Nothing was ejected.", ChatColor.GREEN); + return true; + } + + msg("Ejecting " + StringUtils.join(names, ", ") + ".", ChatColor.GREEN); + playerSender.eject(); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_enchant.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_enchant.java new file mode 100644 index 00000000..b6d154b6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_enchant.java @@ -0,0 +1,275 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Enchant items.", usage = "/ [level] | remove >") +public class Command_enchant extends FreedomCommand +{ + + public static List stringNumberRange(int min, int max) + { + List range = new ArrayList<>(); + for (int i = min; i <= max; i++) + { + range.add(String.valueOf(i)); + } + + return range; + } + + @SuppressWarnings("deprecation") + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + ItemStack item = Objects.requireNonNull(playerSender.getEquipment()).getItemInMainHand(); + + if (item.getType() == Material.AIR) + { + msg("You have to hold an item to enchant it"); + return true; + } + + if (args[0].equalsIgnoreCase("list")) + { + boolean has_enchantments = false; + + StringBuilder possible_ench = new StringBuilder("Possible enchantments for held item: "); + for (Enchantment ench : Enchantment.values()) + { + if (ench.canEnchantItem(item)) + { + has_enchantments = true; + possible_ench.append(ench.getName()).append(", "); + } + } + + if (has_enchantments) + { + msg(possible_ench.toString()); + } + else + { + msg("The held item has no enchantments."); + } + } + else if (args[0].equalsIgnoreCase("addall")) + { + for (Enchantment ench : Enchantment.values()) + { + try + { + if (ench.canEnchantItem(item)) + { + item.addEnchantment(ench, ench.getMaxLevel()); + } + } + catch (Exception ex) + { + msg("Could not add enchantment: " + ench.getName()); + } + } + + msg("Added all possible enchantments for this item."); + } + else if (args[0].equalsIgnoreCase("reset")) + { + for (Enchantment ench : item.getEnchantments().keySet()) + { + item.removeEnchantment(ench); + } + + msg("Removed all enchantments."); + } + else + { + if (args.length < 2) + { + return false; + } + + Enchantment ench = null; + + try + { + ench = Enchantment.getByName(args[1].toUpperCase()); + } + catch (Exception ignored) + { + } + + if (ench == null) + { + msg(args[1] + " is an invalid enchantment for the held item. Type \"/enchant list\" for valid enchantments for this item."); + return true; + } + + if (args[0].equalsIgnoreCase("add")) + { + if (!ench.canEnchantItem(item) && !ConfigEntry.ALLOW_UNSAFE_ENCHANTMENTS.getBoolean()) + { + msg("Can't use this enchantment on held item."); + return true; + } + int level = ench.getMaxLevel(); + if (args.length > 2) + { + try + { + if (ConfigEntry.ALLOW_UNSAFE_ENCHANTMENTS.getBoolean()) + { + level = Integer.parseInt(args[2]); + } + else + { + level = Math.max(1, Math.min(ench.getMaxLevel(), Integer.parseInt(args[2]))); + } + } + catch (NumberFormatException ex) + { + msg("\"" + args[2] + "\" is not a valid number", ChatColor.RED); + return true; + } + } + if (!ConfigEntry.ALLOW_UNSAFE_ENCHANTMENTS.getBoolean()) + { + item.addEnchantment(ench, level); + } + else + { + item.addUnsafeEnchantment(ench, level); + } + + msg("Added enchantment: " + ench.getName()); + } + else if (args[0].equals("remove")) + { + item.removeEnchantment(ench); + + msg("Removed enchantment: " + ench.getName()); + } + } + + return true; + } + + @SuppressWarnings("deprecation") + public List getAllEnchantments() + { + List enchantments = new ArrayList<>(); + for (Enchantment enchantment : Enchantment.values()) + { + enchantments.add(enchantment.getName()); + } + return enchantments; + } + + @SuppressWarnings("deprecation") + public List getAllEnchantments(ItemStack item) + { + List enchantments = new ArrayList<>(); + for (Enchantment enchantment : Enchantment.values()) + { + if (enchantment.canEnchantItem(item)) + { + enchantments.add(enchantment.getName()); + } + } + return enchantments; + } + + @SuppressWarnings("deprecation") + public List getEnchantments(ItemStack item) + { + List enchantments = new ArrayList<>(); + for (Enchantment enchantment : item.getEnchantments().keySet()) + { + enchantments.add(enchantment.getName()); + } + return enchantments; + } + + @SuppressWarnings("deprecation") + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + Player player; + if (sender instanceof Player) + { + player = (Player)sender; + } + else + { + return Collections.emptyList(); + } + ItemStack item = Objects.requireNonNull(player.getEquipment()).getItemInMainHand(); + + if (item.getType() == Material.AIR) + { + return Collections.emptyList(); + } + + boolean unsafe = ConfigEntry.ALLOW_UNSAFE_ENCHANTMENTS.getBoolean(); + + if (args.length == 1) + { + return Arrays.asList("list", "addall", "reset", "add", "remove"); + } + else if (args.length == 2) + { + if (args[0].equals("add")) + { + if (unsafe) + { + return getAllEnchantments(); + } + else + { + return getAllEnchantments(item); + } + } + else if (args[0].equals("remove")) + { + return getEnchantments(item); + } + } + else if (args.length == 3) + { + if (args[0].equals("add")) + { + Enchantment enchantment = Enchantment.getByName(args[1].toUpperCase()); + if (enchantment != null) + { + if (!unsafe) + { + return stringNumberRange(1, enchantment.getMaxLevel()); + } + else + { + return Collections.singletonList("[level]"); + } + } + } + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_end.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_end.java new file mode 100644 index 00000000..083c0dac --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_end.java @@ -0,0 +1,19 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Go to \"The End\".", usage = "/") +public class Command_end extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + plugin.wm.gotoWorld(playerSender, server.getWorlds().get(0).getName() + "_the_end"); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java new file mode 100644 index 00000000..ff2043b2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_entitywipe.java @@ -0,0 +1,113 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Remove various server entities that may cause lag, such as dropped items, minecarts, and boats.", usage = "/ [name | -a]", aliases = "ew,rd") +public class Command_entitywipe extends FreedomCommand +{ + + public static List getAllEntities() + { + List entityTypes = new ArrayList<>(); + for (EntityType entityType : EntityType.values()) + { + if (!Groups.MOB_TYPES.contains(entityType)) + { + entityTypes.add(entityType); + } + } + return entityTypes; + } + + public static List getAllEntityNames() + { + List names = new ArrayList<>(); + for (EntityType entityType : getAllEntities()) + { + names.add(entityType.name()); + } + return names; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + EntityType type = null; + String entityName = null; + boolean bypassBlacklist = false; + if (args.length > 0) + { + if (args[0].equals("-a")) + { + bypassBlacklist = true; + } + else + { + try + { + type = EntityType.valueOf(args[0].toUpperCase()); + } + catch (Exception e) + { + msg(args[0] + " is not a valid entity type.", ChatColor.RED); + return true; + } + + if (!getAllEntities().contains(type)) + { + msg(FUtil.formatName(type.name()) + " is an entity, however: it is a mob.", ChatColor.RED); + return true; + } + } + } + + if (type != null) + { + entityName = FUtil.formatName(type.name()); + } + + FUtil.adminAction(sender.getName(), "Purging all " + (type != null ? entityName + "s" : "entities"), true); + int count; + if (type != null) + { + count = plugin.ew.wipeEntities(type); + } + else + { + count = plugin.ew.wipeEntities(bypassBlacklist); + } + if (count == 1) + { + msg(count + " " + (type != null ? entityName : "entity") + " removed."); + } + else + { + msg(count + " " + (type != null ? entityName : "entitie") + FUtil.showS(count) + " removed."); + } + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + List names = getAllEntityNames(); + names.add("-a"); + if (args.length == 1) + { + return names; + } + + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_expel.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_expel.java new file mode 100644 index 00000000..2f94d1af --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_expel.java @@ -0,0 +1,90 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Push people away from you.", usage = "/ [radius] [strength]") +public class Command_expel extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + double radius = 20.0; + double strength = 5.0; + + if (args.length >= 1) + { + try + { + radius = Math.max(1.0, Math.min(100.0, Double.parseDouble(args[0]))); + } + catch (NumberFormatException ignored) + { + } + } + + if (args.length >= 2) + { + try + { + strength = Math.max(0.0, Math.min(50.0, Double.parseDouble(args[1]))); + } + catch (NumberFormatException ignored) + { + } + } + + List pushedPlayers = new ArrayList<>(); + + final Vector senderPos = playerSender.getLocation().toVector(); + final List players = playerSender.getWorld().getPlayers(); + for (final Player player : players) + { + if (player.equals(playerSender)) + { + continue; + } + + final Location targetPos = player.getLocation(); + final Vector targetPosVec = targetPos.toVector(); + + boolean inRange = false; + try + { + inRange = targetPosVec.distanceSquared(senderPos) < (radius * radius); + } + catch (IllegalArgumentException ignored) + { + } + + if (inRange) + { + player.getWorld().createExplosion(targetPos, 0.0f, false); + FUtil.setFlying(player, false); + player.setVelocity(targetPosVec.subtract(senderPos).normalize().multiply(strength)); + pushedPlayers.add(player.getName()); + } + } + + if (pushedPlayers.isEmpty()) + { + msg("No players pushed."); + } + else + { + msg("Pushed " + pushedPlayers.size() + " players: " + StringUtils.join(pushedPlayers, ", ")); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explode.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explode.java new file mode 100644 index 00000000..0b407f71 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explode.java @@ -0,0 +1,69 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Surprise someone.", usage = "/ ") +public class Command_explode extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + if (args.length == 0) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + player.setFlying(false); + player.setVelocity(player.getVelocity().clone().add(new Vector(0, 50, 0))); + + for (int i = 1; i <= 3; i++) + { + FUtil.createExplosionOnDelay(player.getLocation(), 2L, i * 10); + } + + new BukkitRunnable() + { + @Override + public void run() + { + for (int i = 0; i < 4; i++) + { + player.getWorld().strikeLightning(player.getLocation()); + player.getWorld().createExplosion(player.getLocation(), 4L); + } + player.setHealth(0.0); + msg("Exploded " + player.getName()); + } + }.runTaskLater(plugin, 40); + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isAdmin(sender)) + { + return FUtil.getPlayerList(); + } + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explosivearrows.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explosivearrows.java new file mode 100644 index 00000000..b1f4f767 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_explosivearrows.java @@ -0,0 +1,31 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Make arrows explode", usage = "/", aliases = "ea") +public class Command_explosivearrows extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean onList = plugin.it.explosivePlayers.contains(playerSender); + if (onList) + { + plugin.it.explosivePlayers.remove(playerSender); + msg("You no longer have explosive arrows", ChatColor.RED); + } + else + { + plugin.it.explosivePlayers.add(playerSender); + msg("You now have explosive arrows", ChatColor.GREEN); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_findip.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_findip.java new file mode 100644 index 00000000..36ecb18e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_findip.java @@ -0,0 +1,35 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Shows all IPs registered to a player", usage = "/ ", aliases = "showip,listip") +public class Command_findip extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + msg("Player IPs: " + StringUtils.join(plugin.pl.getData(player).getIps(), ", ")); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fionn.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fionn.java new file mode 100644 index 00000000..0cb1674a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fionn.java @@ -0,0 +1,31 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Restart the server", usage = "/") +public class Command_fionn extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (!plugin.ptero.isEnabled()) + { + msg("Pterodactyl integration is currently disabled.", ChatColor.RED); + return true; + } + if (!FUtil.isExecutive(sender.getName())) + { + noPerms(); + return true; + } + FUtil.bcastMsg(ChatColor.LIGHT_PURPLE + "Fionn is about to corrupt the worlds again!"); + plugin.ptero.fionnTheServer(); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fireball.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fireball.java new file mode 100644 index 00000000..e5366424 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fireball.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a fire ball", usage = "/") +public class Command_fireball extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.FIRE_BALL)) + { + playerSender.getInventory().addItem(plugin.sh.getFireBall()); + msg("You have been given a Fire Ball", ChatColor.GREEN); + } + else + { + msg("You do not own a Fire Ball! Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_flatlands.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_flatlands.java new file mode 100644 index 00000000..e768bc7f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_flatlands.java @@ -0,0 +1,27 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Go to the Flatlands.", usage = "/") +public class Command_flatlands extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (ConfigEntry.FLATLANDS_GENERATE.getBoolean()) + { + plugin.wm.flatlands.sendToWorld(playerSender); + } + else + { + msg("Flatlands is currently disabled in the TotalFreedomMod configuration."); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java new file mode 100644 index 00000000..dd4f3d77 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_forcekill.java @@ -0,0 +1,37 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Forcefully kill someone - for those who REALLY need to die.", usage = "/ ") +public class Command_forcekill extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.al.isAdmin(sender) && !senderIsConsole) + { + playerSender.setHealth(0); + return true; + } + + if (args.length < 1) + { + return false; + } + + final Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + player.setHealth(0); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_freeze.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_freeze.java new file mode 100644 index 00000000..45de1bde --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_freeze.java @@ -0,0 +1,73 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.freeze.FreezeData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Freeze/Unfreeze a specified player, or all non-admins on the server.", usage = "/ [target | purge]", aliases = "fr") +public class Command_freeze extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + boolean gFreeze = !plugin.fm.isGlobalFreeze(); + plugin.fm.setGlobalFreeze(gFreeze); + + if (!gFreeze) + { + FUtil.adminAction(sender.getName(), "Disabling global player freeze", false); + msg("Players are now free to move."); + return true; + } + + FUtil.adminAction(sender.getName(), "Enabling global player freeze", false); + for (Player player : server.getOnlinePlayers()) + { + if (!isAdmin(player)) + { + player.sendTitle(ChatColor.RED + "You've been globally frozen.", ChatColor.YELLOW + "Please be patient and you will be unfrozen shortly.", 20, 100, 60); + msg(player, "You have been globally frozen due to an OP breaking the rules, please wait and you will be unfrozen soon.", ChatColor.RED); + } + } + msg("Players are now frozen."); + return true; + } + + if (args[0].equalsIgnoreCase("purge")) + { + FUtil.adminAction(sender.getName(), "Unfreezing all players", false); + for (Player player : server.getOnlinePlayers()) + { + if (!isAdmin(player)) + { + player.sendTitle(ChatColor.GREEN + "You've been unfrozen.", ChatColor.YELLOW + "You may now move again.", 20, 100, 60); + } + } + plugin.fm.purge(); + return true; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND, ChatColor.RED); + return true; + } + + final FreezeData fd = plugin.pl.getPlayer(player).getFreezeData(); + fd.setFrozen(!fd.isFrozen()); + + msg(player.getName() + " has been " + (fd.isFrozen() ? "frozen" : "unfrozen") + "."); + msg(player, "You have been " + (fd.isFrozen() ? "frozen" : "unfrozen") + ".", ChatColor.AQUA); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fuckoff.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fuckoff.java new file mode 100644 index 00000000..169e6fbf --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_fuckoff.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "You'll never even see it coming - repeatedly push players away from you until command is untoggled.", usage = "/ ") +public class Command_fuckoff extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + FPlayer player = plugin.pl.getPlayer(playerSender); + + if (!args[0].equals("on")) + { + player.disableFuckoff(); + } + else + { + + double radius = 25.0; + if (args.length >= 2) + { + try + { + radius = Math.max(5.0, Math.min(50, Double.parseDouble(args[1]))); + } + catch (NumberFormatException ex) + { + } + } + + player.setFuckoff(radius); + } + + msg("Fuckoff " + (player.isFuckOff() ? ("enabled. Radius: " + player.getFuckoffRadius() + ".") : "disabled.")); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gcmd.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gcmd.java new file mode 100644 index 00000000..c25f0c12 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gcmd.java @@ -0,0 +1,62 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Send a command as someone else.", usage = "/ ") +public class Command_gcmd extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 2) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + final String outCommand = StringUtils.join(args, " ", 1, args.length); + + if (plugin.cb.isCommandBlocked(outCommand, sender)) + { + return true; + } + + if (plugin.al.isAdmin(player)) + { + msg(ChatColor.RED + "You can not use gcmd on admins"); + return true; + } + + try + { + msg("Sending command as " + player.getName() + ": " + outCommand); + if (server.dispatchCommand(player, outCommand)) + { + msg("Command sent."); + } + else + { + msg("Unknown error sending command."); + } + } + catch (Throwable ex) + { + msg("Error sending command: " + ex.getMessage()); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_glow.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_glow.java new file mode 100644 index 00000000..ca77a8ce --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_glow.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Toggles the glowing outline effect because y'all lazy as fuck", usage = "/") +public class Command_glow extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean glowing = false; + if (playerSender.getPotionEffect(PotionEffectType.GLOWING) != null) + { + playerSender.removePotionEffect(PotionEffectType.GLOWING); + } + else + { + PotionEffect glow = new PotionEffect(PotionEffectType.GLOWING, 1000000, 1, false, false); + playerSender.addPotionEffect(glow); + glowing = true; + } + msg("You are " + (glowing ? "now" : "no longer") + " glowing."); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_grapplinghook.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_grapplinghook.java new file mode 100644 index 00000000..5ee72d2e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_grapplinghook.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a grappling hook", usage = "/") +public class Command_grapplinghook extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.GRAPPLING_HOOK)) + { + playerSender.getInventory().addItem(plugin.sh.getGrapplingHook()); + msg("You have been given a Grappling Hook", ChatColor.GREEN); + } + else + { + msg("You do not own a Grappling Hook! Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gravity.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gravity.java new file mode 100644 index 00000000..f4f24fc2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_gravity.java @@ -0,0 +1,22 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Toggles player gravity on/off.", usage = "/") +public class Command_gravity extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean enabled = !playerSender.hasGravity(); + playerSender.setGravity(enabled); + msg((enabled ? "En" : "Dis") + "abled gravity.", (enabled ? ChatColor.GREEN : ChatColor.RED)); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_health.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_health.java new file mode 100644 index 00000000..5fbd19ee --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_health.java @@ -0,0 +1,110 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.text.DecimalFormat; +import java.util.concurrent.atomic.AtomicInteger; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.lang.math.DoubleRange; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "View server health, such as ticks-per-second, memory, etc.", usage = "/") +public class Command_health extends FreedomCommand +{ + + private static final int BYTES_PER_MB = 1024 * 1024; + private static final DoubleRange TPS_RANGE = new DoubleRange(20.0 - 0.1, 20.0 + 0.1); + + @Override + public boolean run(final CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Runtime runtime = Runtime.getRuntime(); + long usedMem = runtime.totalMemory() - runtime.freeMemory(); + + msg("Reserved Memory: " + (double)runtime.totalMemory() / (double)BYTES_PER_MB + "mb"); + msg("Used Memory: " + new DecimalFormat("#").format((double)usedMem / (double)BYTES_PER_MB) + + "mb (" + new DecimalFormat("#").format(((double)usedMem / (double)runtime.totalMemory()) * 100.0) + "%)"); + msg("Max Memory: " + (double)runtime.maxMemory() / (double)BYTES_PER_MB + "mb"); + msg("Calculating ticks per second, please wait..."); + + new BukkitRunnable() + { + @Override + public void run() + { + try + { + TFM_TickMeter tickMeter = new TFM_TickMeter(plugin); + tickMeter.startTicking(); + Thread.sleep(2500); + final double ticksPerSecond = tickMeter.stopTicking(); + + // Plugin was disabled during async task + if (!plugin.isEnabled()) + { + return; + } + + new BukkitRunnable() + { + @Override + public void run() + { + msg("Ticks per second: " + (TPS_RANGE.containsDouble(ticksPerSecond) ? ChatColor.GREEN : ChatColor.RED) + ticksPerSecond); + } + }.runTask(plugin); + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + }.runTaskAsynchronously(plugin); + + return true; + } + + private static class TFM_TickMeter + { + + private final AtomicInteger ticks = new AtomicInteger(); + private final TotalFreedomMod plugin; + private long startTime; + private BukkitTask task; + + public TFM_TickMeter(TotalFreedomMod plugin) + { + this.plugin = plugin; + } + + public void startTicking() + { + startTime = System.currentTimeMillis(); + ticks.set(0); + + task = new BukkitRunnable() + { + @Override + public void run() + { + ticks.incrementAndGet(); + } + }.runTaskTimer(plugin, 0L, 1L); + } + + public double stopTicking() + { + task.cancel(); + long elapsed = System.currentTimeMillis() - startTime; + int tickCount = ticks.get(); + + return (double)tickCount / ((double)elapsed / 1000.0); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_indefban.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_indefban.java new file mode 100644 index 00000000..6d00f402 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_indefban.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_CONSOLE) +@CommandParameters(description = "Reload the indefinite ban list.", usage = "/ ", aliases = "ib") +public class Command_indefban extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (!args[0].equalsIgnoreCase("reload")) + { + return false; + } + + msg("Reloading the indefinite ban list..."); + plugin.im.onStop(); + plugin.im.onStart(); + msg("Reloaded the indefinite ban list."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java new file mode 100644 index 00000000..87ab52a1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_inspect.java @@ -0,0 +1,69 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.bridge.CoreProtectBridge; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Block inspector tool for operators", usage = "/ [history] ", aliases = "ins") +public class Command_inspect extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + PlayerData playerData = plugin.pl.getData(playerSender); + playerData.setInspect(!playerData.hasInspection()); + plugin.pl.save(playerData); + msg("Block inspector " + (playerData.hasInspection() ? "enabled." : "disabled.")); + return true; + } + + if (args[0].equalsIgnoreCase("history")) + { + int pageIndex = 1; + + if (args.length >= 2) + { + try + { + pageIndex = Integer.parseInt(args[1]); + } + catch (NumberFormatException e) + { + msg("Invalid number", ChatColor.RED); + } + } + + FUtil.PaginationList paged = CoreProtectBridge.HISTORY_MAP.get(playerSender); + if (paged != null) + { + if (pageIndex < 1 || pageIndex > paged.getPageCount()) + { + msg("Not a valid page number", ChatColor.RED); + return true; + } + + msg("---- " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "Block Inspector" + ChatColor.WHITE + " ---- ", ChatColor.WHITE); + + List page = paged.getPage(pageIndex); + for (String entries : page) + { + msg(entries); + } + + msg("Page " + pageIndex + "/" + paged.getPageCount() + " | To index through the pages, type " + net.md_5.bungee.api.ChatColor.of("#30ade4") + "/ins history ", ChatColor.WHITE); + return true; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invis.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invis.java new file mode 100644 index 00000000..01598b32 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invis.java @@ -0,0 +1,88 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Shows (optionally clears) invisible players", usage = "/ [clear]") +public class Command_invis extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean clear = false; + + if (args.length >= 1) + { + if (args[0].equalsIgnoreCase("clear")) + { + if (!plugin.al.isAdmin(sender)) + { + return noPerms(); + } + else + { + FUtil.adminAction(sender.getName(), "Clearing all invisibility potion effects from all players", true); + clear = true; + } + } + else + { + return false; + } + } + + List players = new ArrayList<>(); + int clears = 0; + + for (Player player : server.getOnlinePlayers()) + { + if (player.hasPotionEffect(PotionEffectType.INVISIBILITY) && !plugin.al.isVanished(player.getName())) + { + players.add(player.getName()); + if (clear && !plugin.al.isAdmin(player)) + { + player.removePotionEffect((PotionEffectType.INVISIBILITY)); + clears++; + } + } + } + + if (players.isEmpty()) + { + msg("There are no invisible players"); + return true; + } + + if (clear) + { + msg("Cleared " + clears + " players"); + } + else + { + msg("Invisible players (" + players.size() + "): " + StringUtils.join(players, ", ")); + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isAdmin(sender)) + { + return Collections.singletonList("clear"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invsee.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invsee.java new file mode 100644 index 00000000..5390d710 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_invsee.java @@ -0,0 +1,73 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Look into another player's inventory, or optionally take items out.", usage = "/ [offhand | armor]", aliases = "inv,insee") +public class Command_invsee extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + Player player = getPlayer(args[0], true); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return false; + } + + if (playerSender == player) + { + msg("You cannot run this command on yourself.", ChatColor.RED); + return true; + } + + if (plugin.al.isAdmin(player) && !plugin.al.isAdmin(playerSender)) + { + msg("You cannot see the inventory of admins.", ChatColor.RED); + return true; + } + + Inventory inv; + if (args.length > 1) + { + if (args[1].equals("offhand")) + { + ItemStack offhand = player.getInventory().getItemInOffHand(); + Inventory inventory = server.createInventory(null, 9, player.getName() + "'s offhand"); + inventory.setItem(1, offhand); + playerSender.openInventory(inventory); + return true; + } + else if (args[1].equals("armor")) + { + Inventory inventory = server.createInventory(null, 9, player.getName() + "'s armor"); + inventory.setContents(player.getInventory().getArmorContents()); + playerSender.openInventory(inventory); + return true; + } + } + inv = player.getInventory(); + playerSender.closeInventory(); + if (!plugin.al.isAdmin(player)) + { + FPlayer fPlayer = plugin.pl.getPlayer(playerSender); + fPlayer.setInvSee(true); + } + playerSender.openInventory(inv); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_jumppads.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_jumppads.java new file mode 100644 index 00000000..967eb64c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_jumppads.java @@ -0,0 +1,116 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.fun.Jumppads; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Toggles jumppads on/off, view the status of jumppads, or make them sideways.", usage = "/ >", aliases = "launchpads,jp") +public class Command_jumppads extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0 || args.length > 2) + { + return false; + } + + if (args.length == 1) + { + if (args[0].equalsIgnoreCase("info")) + { + msg("Jumppads: " + (plugin.jp.players.get(playerSender).isOn() ? "Enabled" : "Disabled"), ChatColor.BLUE); + msg("Sideways: " + (plugin.jp.players.get(playerSender) == Jumppads.JumpPadMode.NORMAL_AND_SIDEWAYS ? "Enabled" : "Disabled"), ChatColor.BLUE); + return true; + } + + if (args[0].equalsIgnoreCase("off")) + { + if (plugin.jp.players.get(playerSender) == Jumppads.JumpPadMode.OFF) + { + msg("Your jumppads are already disabled."); + return true; + } + msg("Disabled your jumppads.", ChatColor.GRAY); + plugin.jp.players.put(playerSender, Jumppads.JumpPadMode.OFF); + } + else + { + if (plugin.jp.players.get(playerSender) != Jumppads.JumpPadMode.OFF) + { + msg("Your jumppads are already enabled."); + return true; + } + msg("Enabled your jumpppads.", ChatColor.GRAY); + plugin.jp.players.put(playerSender, Jumppads.JumpPadMode.MADGEEK); + } + } + else + { + if (plugin.jp.players.get(playerSender) == Jumppads.JumpPadMode.OFF) + { + msg("Your jumppads are currently disabled, please enable them before changing jumppads settings."); + return true; + } + + if (args[0].equalsIgnoreCase("sideways")) + { + if ("off".equals(args[1])) + { + if (plugin.jp.players.get(playerSender) == Jumppads.JumpPadMode.MADGEEK) + { + msg("Your jumppads are already set to normal mode."); + return true; + } + msg("Set Jumppads mode to: Normal", ChatColor.GRAY); + plugin.jp.players.put(playerSender, Jumppads.JumpPadMode.MADGEEK); + } + else + { + if (plugin.jp.players.get(playerSender) == Jumppads.JumpPadMode.NORMAL_AND_SIDEWAYS) + { + msg("Your jumppads are already set to normal and sideways mode."); + return true; + } + msg("Set Jumppads mode to: Normal and Sideways", ChatColor.GRAY); + plugin.jp.players.put(playerSender, Jumppads.JumpPadMode.NORMAL_AND_SIDEWAYS); + } + } + else + { + return false; + } + } + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + if (args.length == 1) + { + return Arrays.asList("on", "off", "info", "sideways"); + } + else if (args.length == 2) + { + if (args[0].equals("sideways")) + { + return Arrays.asList("on", "off"); + } + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kick.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kick.java new file mode 100644 index 00000000..b2ff77ce --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kick.java @@ -0,0 +1,86 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Kick the specified player.", usage = "/ [reason] [-q]") +public class Command_kick extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + boolean silent = false; + + String reason = null; + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + silent = true; + FLog.debug("silent"); + + if (args.length >= 2) + { + FLog.debug("set reason (silent)"); + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else if (args.length > 1) + { + FLog.debug("set reason"); + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + + StringBuilder builder = new StringBuilder() + .append(ChatColor.RED).append("You have been kicked from the server.") + .append("\n").append(ChatColor.RED).append("Kicked by: ").append(ChatColor.GOLD).append(sender.getName()); + + if (reason != null) + { + builder.append("\n").append(ChatColor.RED).append("Reason: ").append(ChatColor.GOLD).append(reason); + } + + if (!silent) + { + if (reason != null) + { + FUtil.adminAction(sender.getName(), "Kicking " + player.getName() + " - Reason: " + reason, true); + } + else + { + FUtil.adminAction(sender.getName(), "Kicking " + player.getName(), true); + } + } + else + { + msg("Kicked " + player.getName() + " quietly."); + } + + player.kickPlayer(builder.toString()); + + plugin.pul.logPunishment(new Punishment(player.getName(), FUtil.getIp(player), sender.getName(), PunishmentType.KICK, reason)); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kicknoob.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kicknoob.java new file mode 100644 index 00000000..c009264e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_kicknoob.java @@ -0,0 +1,30 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Kick all non-admins on server.", usage = "/", aliases = "kickall") +public class Command_kicknoob extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "Disconnecting all non-admins", true); + + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + player.kickPlayer(ChatColor.RED + "All non-admins were kicked by " + sender.getName() + "."); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_landmine.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_landmine.java new file mode 100644 index 00000000..f51e7fb5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_landmine.java @@ -0,0 +1,65 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Iterator; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.fun.Landminer.Landmine; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Set a landmine trap.", usage = "/") +public class Command_landmine extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!ConfigEntry.LANDMINES_ENABLED.getBoolean()) + { + msg("The landmine is currently disabled.", ChatColor.GREEN); + return true; + } + + if (!ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + msg("Explosions are currently disabled.", ChatColor.GREEN); + return true; + } + + double radius = 2.0; + + if (args.length >= 1) + { + if ("list".equals(args[0])) + { + final Iterator landmines = plugin.lm.getLandmines().iterator(); + while (landmines.hasNext()) + { + msg(landmines.next().toString()); + } + return true; + } + + try + { + radius = Math.max(2.0, Math.min(6.0, Double.parseDouble(args[0]))); + } + catch (NumberFormatException ignored) + { + } + } + + final Block landmine = playerSender.getLocation().getBlock().getRelative(BlockFace.DOWN); + landmine.setType(Material.TNT); + plugin.lm.add(new Landmine(landmine.getLocation(), playerSender, radius)); + + msg("Landmine planted - Radius = " + radius + " blocks.", ChatColor.GREEN); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lastcmd.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lastcmd.java new file mode 100644 index 00000000..e0271f4a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lastcmd.java @@ -0,0 +1,45 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Show the last command the specified player used.", usage = "/ ") +public class Command_lastcmd extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + final FPlayer playerdata = plugin.pl.getPlayer(player); + + if (playerdata != null) + { + String lastCommand = playerdata.getLastCommand(); + if (lastCommand.isEmpty()) + { + lastCommand = "(none)"; + } + msg(player.getName() + " - Last Command: " + lastCommand, ChatColor.GRAY); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lightningrod.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lightningrod.java new file mode 100644 index 00000000..ee2ffd8a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lightningrod.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a lightning rod", usage = "/") +public class Command_lightningrod extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.LIGHTNING_ROD)) + { + playerSender.getInventory().addItem(plugin.sh.getLightningRod()); + msg("You have been given a Lightning Rod", ChatColor.GREEN); + } + else + { + msg("You do not own a Lightning Rod! Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java new file mode 100644 index 00000000..93e18456 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_linkdiscord.java @@ -0,0 +1,60 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Link your Discord account to your Minecraft account", usage = "/ [ ]") +public class Command_linkdiscord extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (args.length > 1 && plugin.al.isAdmin(playerSender)) + { + PlayerData playerData = plugin.pl.getData(args[0]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + playerData.setDiscordID(args[1]); + msg("Linked " + args[0] + "'s Discord account.", ChatColor.GREEN); + return true; + } + + String code; + + PlayerData data = plugin.pl.getData(playerSender); + if (data.getDiscordID() != null) + { + msg("Your Minecraft account is already linked to a Discord account.", ChatColor.RED); + return true; + } + + if (Discord.LINK_CODES.containsValue(data)) + { + code = Discord.getCode(data); + } + else + { + code = plugin.dc.generateCode(5); + Discord.LINK_CODES.put(code, data); + } + msg("Your linking code is " + ChatColor.AQUA + code, ChatColor.GREEN); + msg("Take this code and DM the server bot (" + plugin.dc.formatBotTag() + ") the code (do not put anything else in the message, only the code)"); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_links.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_links.java new file mode 100644 index 00000000..5bd21b6d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_links.java @@ -0,0 +1,49 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "See TotalFreedom's social media links.", usage = "/", aliases = "link") +public class Command_links extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + YamlConfiguration config = plugin.config.configuration; + ConfigurationSection section = config.getConfigurationSection("social_links"); + if (section != null) + { + Map values = section.getValues(false); + + List lines = new ArrayList<>(); + + for (String key : values.keySet()) + { + if (!(values.get(key) instanceof String)) + { + continue; + } + String link = (String)values.get(key); + lines.add(ChatColor.GOLD + "- " + key + ": " + ChatColor.AQUA + link); + } + + msg("Social Media Links:", ChatColor.AQUA); + sender.sendMessage(lines.toArray(new String[0])); + return true; + } + else + { + msg("There are no links added in the configuration file.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_list.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_list.java new file mode 100644 index 00000000..996ac302 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_list.java @@ -0,0 +1,170 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.admin.AdminList; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.md_5.bungee.api.ChatColor; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +@CommandPermissions(level = Rank.IMPOSTOR, source = SourceType.BOTH) +@CommandParameters(description = "Lists the real names of all online players.", usage = "/ [-s | -i | -f | -v]", aliases = "who,lsit") +public class Command_list extends FreedomCommand +{ + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (args.length > 1) + { + return false; + } + if (FUtil.isFromHostConsole(sender.getName())) + { + List names = new ArrayList<>(); + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isVanished(player.getName())) + { + names.add(player.getName()); + } + } + msg("There are " + names.size() + "/" + server.getMaxPlayers() + " players online:\n" + StringUtils.join(names, ", "), ChatColor.WHITE); + return true; + } + ListFilter listFilter; + if (args.length == 1) + { + String s = args[0]; + switch (s) + { + case "-s": + case "-a": + { + listFilter = ListFilter.ADMINS; + break; + } + case "-v": + { + checkRank(Rank.ADMIN); + listFilter = ListFilter.VANISHED_ADMINS; + break; + } + case "-t": + { + checkRank(Rank.ADMIN); + listFilter = ListFilter.TELNET_SESSIONS; + break; + } + case "-i": + { + listFilter = ListFilter.IMPOSTORS; + break; + } + case "-f": + { + listFilter = ListFilter.FAMOUS_PLAYERS; + break; + } + default: + { + return false; + } + } + } + else + { + listFilter = ListFilter.PLAYERS; + } + StringBuilder onlineStats = new StringBuilder(); + StringBuilder onlineUsers = new StringBuilder(); + + List n = new ArrayList<>(); + + if (listFilter == ListFilter.TELNET_SESSIONS && plugin.al.isAdmin(sender)) + { + List connectedAdmins = plugin.btb.getConnectedAdmins(); + onlineStats.append(ChatColor.BLUE).append("There are ").append(ChatColor.RED).append(connectedAdmins.size()) + .append(ChatColor.BLUE) + .append(" admins connected to telnet."); + for (Admin admin : connectedAdmins) + { + n.add(admin.getName()); + } + } + else + { + onlineStats.append(ChatColor.BLUE).append("There are ").append(ChatColor.RED).append(FUtil.getFakePlayerCount()) + .append(ChatColor.BLUE) + .append(" out of a maximum ") + .append(ChatColor.RED) + .append(server.getMaxPlayers()) + .append(ChatColor.BLUE) + .append(" players online."); + for (Player p : server.getOnlinePlayers()) + { + if (listFilter == ListFilter.ADMINS && !plugin.al.isAdmin(p)) + { + continue; + } + if (listFilter == ListFilter.ADMINS && plugin.al.isVanished(p.getName())) + { + continue; + } + if (listFilter == ListFilter.VANISHED_ADMINS && !plugin.al.isVanished(p.getName())) + { + continue; + } + if (listFilter == ListFilter.IMPOSTORS && !plugin.al.isAdminImpostor(p)) + { + continue; + } + if (listFilter == ListFilter.FAMOUS_PLAYERS && !ConfigEntry.FAMOUS_PLAYERS.getList().contains(p.getName().toLowerCase())) + { + continue; + } + if (listFilter == ListFilter.PLAYERS && plugin.al.isVanished(p.getName())) + { + continue; + } + + final Displayable display = plugin.rm.getDisplay(p); + n.add(display.getColoredTag() + p.getName()); + } + } + String playerType = listFilter.toString().toLowerCase().replace('_', ' '); + onlineUsers.append("Connected ") + .append(playerType) + .append(": ") + .append(StringUtils.join(n, ChatColor.WHITE + ", ")); + if (senderIsConsole) + { + msg(ChatColor.stripColor(onlineStats.toString())); + msg(ChatColor.stripColor(onlineUsers.toString())); + } + else + { + msg(onlineStats.toString()); + msg(onlineUsers.toString()); + } + n.clear(); + return true; + } + + private enum ListFilter + { + PLAYERS, + ADMINS, + VANISHED_ADMINS, + TELNET_SESSIONS, + FAMOUS_PLAYERS, + IMPOSTORS + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_localspawn.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_localspawn.java new file mode 100644 index 00000000..3bea6064 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_localspawn.java @@ -0,0 +1,20 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Teleport to the spawn point for the current world, instead of the Essentials spawn point.", usage = "/", aliases = "worldspawn,gotospawn") +public class Command_localspawn extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + PaperLib.teleportAsync(playerSender, playerSender.getWorld().getSpawnLocation()); + msg("Teleported to spawnpoint for world \"" + playerSender.getWorld().getName() + "\"."); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lockup.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lockup.java new file mode 100644 index 00000000..e707bd70 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_lockup.java @@ -0,0 +1,135 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "This is evil, and I never should have wrote it - blocks specified player's input.", usage = "/ on | off> [-q]>") +public class Command_lockup extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean silent = (args[args.length - 1].equalsIgnoreCase("-q")); + if (args.length == 1) + { + if (args[0].equalsIgnoreCase("all")) + { + FUtil.adminAction(sender.getName(), "Locking up all players", true); + + for (Player player : server.getOnlinePlayers()) + { + startLockup(player); + } + msg("Locked up all players."); + } + else if (args[0].equalsIgnoreCase("purge")) + { + FUtil.adminAction(sender.getName(), "Unlocking all players", true); + for (Player player : server.getOnlinePlayers()) + { + cancelLockup(player); + } + + msg("Unlocked all players."); + } + else + { + return false; + } + } + else if (args.length == 2) + { + if (args[1].equalsIgnoreCase("on")) + { + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (!silent) + { + FUtil.adminAction(sender.getName(), "Locking up " + player.getName(), true); + } + startLockup(player); + msg("Locked up " + player.getName() + "."); + } + else if ("off".equals(args[1])) + { + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (!silent) + { + FUtil.adminAction(sender.getName(), "Unlocking " + player.getName(), true); + } + cancelLockup(player); + msg("Unlocked " + player.getName() + "."); + } + else + { + return false; + } + } + else + { + return false; + } + return true; + } + + private void cancelLockup(FPlayer playerdata) + { + BukkitTask lockupScheduleId = playerdata.getLockupScheduleID(); + if (lockupScheduleId != null) + { + lockupScheduleId.cancel(); + playerdata.setLockedUp(false); + playerdata.setLockupScheduleId(null); + } + } + + private void cancelLockup(final Player player) + { + cancelLockup(plugin.pl.getPlayer(player)); + } + + private void startLockup(final Player player) + { + final FPlayer playerdata = plugin.pl.getPlayer(player); + + cancelLockup(playerdata); + + playerdata.setLockedUp(true); + playerdata.setLockupScheduleId(new BukkitRunnable() + { + @Override + public void run() + { + if (player.isOnline()) + { + player.openInventory(player.getInventory()); + } + else + { + cancelLockup(playerdata); + } + } + }.runTaskTimer(plugin, 0L, 5L)); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_loginmessage.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_loginmessage.java new file mode 100644 index 00000000..0b5efbc2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_loginmessage.java @@ -0,0 +1,51 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Change your login message", usage = "/ [message]") +public class Command_loginmessage extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.pl.getData(playerSender).hasItem(ShopItem.LOGIN_MESSAGES) && !isAdmin(playerSender)) + { + msg("You did not purchase the ability to use login messages! Purchase the ability from the shop.", ChatColor.RED); + return true; + } + + if (args.length == 0) + { + playerSender.openInventory(plugin.sh.generateLoginMessageGUI(playerSender)); + return true; + } + + checkRank(Rank.ADMIN); + + String message = StringUtils.join(args, " "); + if (!message.contains("%rank%") && !message.contains("%coloredrank%")) + { + msg("Your login message must contain your rank. Use either %rank% or %coloredrank% to specify where you want the rank", ChatColor.RED); + return true; + } + int length = message.replace("%name%", "").replace("%rank%", "").replace("%coloredrank%", "").replace("%art%", "").length(); + if (length > 100) + { + msg("Your login message cannot be more than 100 characters (excluding your rank and your name)", ChatColor.RED); + return true; + } + PlayerData data = getData(playerSender); + data.setLoginMessage(message); + plugin.pl.save(data); + msg("Your login message is now the following:\n" + plugin.rm.craftLoginMessage(playerSender, message), ChatColor.GREEN); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_logs.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_logs.java new file mode 100644 index 00000000..e95d29b8 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_logs.java @@ -0,0 +1,26 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.LogViewer.LogsRegistrationMode; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Register your connection with the TFM logviewer.", usage = "/ [off]") +public class Command_logs extends FreedomCommand +{ + + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + LogsRegistrationMode mode = LogsRegistrationMode.ADD; + if (args.length == 1 && "off".equalsIgnoreCase(args[0])) + { + mode = LogsRegistrationMode.DELETE; + } + plugin.lv.updateLogsRegistration(sender, playerSender, mode); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_makeopregion.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_makeopregion.java new file mode 100644 index 00000000..7cf2334f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_makeopregion.java @@ -0,0 +1,96 @@ +package me.totalfreedom.totalfreedommod.command; + +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Make a WorldGuard region for an OP.", usage = "/ ", aliases = "mor") +public class Command_makeopregion extends FreedomCommand +{ + + final Map, Object> flags = new HashMap, Object>() + {{ + put(Flags.BLOCK_PLACE, StateFlag.State.ALLOW); + put(Flags.BLOCK_BREAK, StateFlag.State.ALLOW); + put(Flags.BUILD, StateFlag.State.ALLOW); + put(Flags.PLACE_VEHICLE, StateFlag.State.ALLOW); + put(Flags.DESTROY_VEHICLE, StateFlag.State.ALLOW); + put(Flags.ENTITY_ITEM_FRAME_DESTROY, StateFlag.State.ALLOW); + put(Flags.ENTITY_PAINTING_DESTROY, StateFlag.State.ALLOW); + put(net.goldtreeservers.worldguardextraflags.flags.Flags.WORLDEDIT, StateFlag.State.ALLOW); + }}; + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (args.length < 2) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + String name = args[1]; + + LocalSession session = plugin.web.getWorldEditPlugin().getSession(playerSender); + + Region selection; + + try + { + selection = session.getSelection(session.getSelectionWorld()); + } + catch (IncompleteRegionException e) + { + msg("Please make a WorldEdit selection", ChatColor.RED); + return true; + } + + if (selection == null) + { + msg("Please make a WorldEdit selection", ChatColor.RED); + return true; + } + + ProtectedRegion region = new ProtectedCuboidRegion(name, selection.getMinimumPoint(), selection.getMaximumPoint()); + + DefaultDomain owners = new DefaultDomain(); + owners.addPlayer(playerSender.getName()); + owners.addPlayer(player.getName()); + region.setOwners(owners); + region.setFlags(flags); + + for (Flag flag : flags.keySet()) + { + region.setFlag(flag.getRegionGroupFlag(), RegionGroup.MEMBERS); + } + + RegionManager regionManager = plugin.wgb.getRegionManager(playerSender.getWorld()); + + regionManager.addRegion(region); + + msg("Successfully created the region '" + name + "' for " + player.getName(), ChatColor.GREEN); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manageshop.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manageshop.java new file mode 100644 index 00000000..60c786c3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manageshop.java @@ -0,0 +1,219 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Manage the shop", usage = "/ | items: ", aliases = "ms") +public class Command_manageshop extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + if (!FUtil.isExecutive(sender.getName())) + { + return noPerms(); + } + + if (args.length < 2) + { + return false; + } + if (args[0].equals("coins")) + { + if (args.length < 4) + { + return false; + } + switch (args[1]) + { + + case "add": + try + { + int amount = Math.max(0, Math.min(1000000, Integer.parseInt(args[2]))); + if (!args[3].equals("all")) + { + PlayerData playerData = plugin.pl.getData(args[3]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + playerData.setCoins(playerData.getCoins() + amount); + plugin.pl.save(playerData); + msg("Successfully added " + amount + " coins to " + args[3] + ". Their new balance is " + playerData.getCoins(), ChatColor.GREEN); + Player player = getPlayer(args[3]); + if (player != null) + { + msg(player, sender.getName() + " gave you " + amount + " coins. Your new balance is " + playerData.getCoins(), ChatColor.GREEN); + } + } + else + { + for (Player player : server.getOnlinePlayers()) + { + PlayerData playerData = plugin.pl.getData(player); + playerData.setCoins(playerData.getCoins() + amount); + plugin.pl.save(playerData); + msg(player, sender.getName() + " gave you " + amount + " coins. Your new balance is " + playerData.getCoins(), ChatColor.GREEN); + } + msg("Successfully added " + amount + " coins to all online players.", ChatColor.GREEN); + } + return true; + } + catch (NumberFormatException ex) + { + msg("Invalid number: " + args[2], ChatColor.RED); + return true; + } + case "remove": + try + { + int amount = Math.max(0, Math.min(1000000, Integer.parseInt(args[2]))); + if (!args[3].equals("all")) + { + PlayerData playerData = plugin.pl.getData(args[3]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + playerData.setCoins(playerData.getCoins() - amount); + if (playerData.getCoins() < 0) + { + playerData.setCoins(0); + } + plugin.pl.save(playerData); + msg("Successfully removed " + amount + " coins from " + args[3] + ". Their new balance is " + playerData.getCoins(), ChatColor.GREEN); + Player player = getPlayer(args[3]); + if (player != null) + { + msg(player, sender.getName() + " took " + amount + " coins from you. Your new balance is " + playerData.getCoins(), ChatColor.RED); + } + } + else + { + for (Player player : server.getOnlinePlayers()) + { + PlayerData playerData = plugin.pl.getData(player); + playerData.setCoins(playerData.getCoins() - amount); + if (playerData.getCoins() < 0) + { + playerData.setCoins(0); + } + plugin.pl.save(playerData); + msg(player, sender.getName() + " took " + amount + " coins from you. Your new balance is " + playerData.getCoins(), ChatColor.RED); + } + msg("Successfully took " + amount + " coins from all online players.", ChatColor.GREEN); + } + return true; + } + catch (NumberFormatException ex) + { + msg("Invalid number: " + args[2], ChatColor.RED); + return true; + } + case "set": + try + { + int amount = Math.max(0, Math.min(1000000, Integer.parseInt(args[2]))); + PlayerData playerData = plugin.pl.getData(args[3]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + playerData.setCoins(amount); + plugin.pl.save(playerData); + msg("Successfully set " + args[3] + "'s coins to " + amount, ChatColor.GREEN); + Player player = getPlayer(args[3]); + if (player != null) + { + msg(player, sender.getName() + " set your coin balance to " + amount, ChatColor.GREEN); + } + return true; + } + catch (NumberFormatException ex) + { + msg("Invalid number: " + args[2], ChatColor.RED); + return true; + } + } + } + else if (args[0].equals("items")) + { + if (args[1].equals("list")) + { + msg("List of all shop items: " + StringUtils.join(ShopItem.values(), ", ")); + return true; + } + + if (args.length < 4) + { + return false; + } + + if (args[1].equals("give")) + { + ShopItem item = ShopItem.findItem(args[2].toUpperCase()); + if (item == null) + { + msg(args[2] + " is not a valid item.", ChatColor.RED); + return true; + } + + PlayerData playerData = plugin.pl.getData(args[3]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + playerData.giveItem(item); + plugin.pl.save(playerData); + msg("Successfully gave the " + item.getName() + " to " + args[3], ChatColor.GREEN); + Player player = getPlayer(args[3]); + if (player != null) + { + msg(player, sender.getName() + " gave the " + item.getName() + " to you", ChatColor.GREEN); + } + return true; + } + else if (args[1].equals("take")) + { + ShopItem item = ShopItem.findItem(args[2].toUpperCase()); + if (item == null) + { + msg(args[2] + " is not a valid item.", ChatColor.RED); + return true; + } + + PlayerData playerData = plugin.pl.getData(args[3]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + playerData.removeItem(item); + plugin.pl.save(playerData); + msg("Successfully took the " + item.getName() + " from " + args[3], ChatColor.GREEN); + Player player = getPlayer(args[3]); + if (player != null) + { + msg(player, sender.getName() + " took the " + item.getName() + " from you", ChatColor.RED); + } + return true; + } + + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manuallyverify.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manuallyverify.java new file mode 100644 index 00000000..1776538f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_manuallyverify.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Manually verify someone", usage = "/ ", aliases = "mv") +public class Command_manuallyverify extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean verificationEnabled = ConfigEntry.DISCORD_VERIFICATION.getBoolean(); + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (!verificationEnabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (args.length == 0) + { + return false; + } + + final Player player = getPlayer(args[0]); + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + if (!plugin.pl.IsImpostor(player)) + { + msg("That player is not an impostor."); + return true; + } + + FUtil.adminAction(sender.getName(), "Manually verifying player " + player.getName(), false); + player.setOp(true); + msg(player, YOU_ARE_OP); + + if (plugin.pl.getPlayer(player).getFreezeData().isFrozen()) + { + plugin.pl.getPlayer(player).getFreezeData().setFrozen(false); + msg(player, "You have been unfrozen."); + } + + plugin.pl.verify(player, null); + plugin.rm.updateDisplay(player); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderinfo.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderinfo.java new file mode 100644 index 00000000..a22d1e7b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderinfo.java @@ -0,0 +1,34 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Information on how to apply for Master Builder.", usage = "/", aliases = "mbi") +public class Command_masterbuilderinfo extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + List masterBuilderInfo = ConfigEntry.MASTER_BUILDER_INFO.getStringList(); + + if (masterBuilderInfo.isEmpty()) + { + msg("The master builder information section of the config.yml file has not been configured.", ChatColor.RED); + } + else + { + msg(FUtil.colorize(StringUtils.join(masterBuilderInfo, "\n"))); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderworld.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderworld.java new file mode 100644 index 00000000..ee249abe --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_masterbuilderworld.java @@ -0,0 +1,202 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.world.WorldTime; +import me.totalfreedom.totalfreedommod.world.WorldWeather; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Allows for master builders to configure the time, the weather of the MasterBuilder, and allows for players to go to the MasterBuilderWorld.", + usage = "/ [time | weather ]", + aliases = "mbw,mbworld") +public class Command_masterbuilderworld extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + CommandMode commandMode = null; + + if (args.length == 0) + { + commandMode = CommandMode.TELEPORT; + } + else if (args.length >= 2) + { + if ("time".equalsIgnoreCase(args[0])) + { + commandMode = CommandMode.TIME; + } + else if ("weather".equalsIgnoreCase(args[0])) + { + commandMode = CommandMode.WEATHER; + } + } + + if (commandMode == null) + { + return false; + } + + try + { + switch (commandMode) + { + case TELEPORT: + { + if (!(sender instanceof Player) || playerSender == null) + { + return false; + } + + World masterBuilderWorld = null; + try + { + masterBuilderWorld = plugin.wm.masterBuilderWorld.getWorld(); + } + catch (Exception ignored) + { + } + + if (masterBuilderWorld == null || playerSender.getWorld() == masterBuilderWorld) + { + msg("Going to the main world."); + PaperLib.teleportAsync(playerSender, server.getWorlds().get(0).getSpawnLocation()); + } + else + { + msg("Going to the Master Builder world"); + plugin.wm.masterBuilderWorld.sendToWorld(playerSender); + } + + break; + } + case TIME: + { + assertCommandPerms(sender, playerSender); + + if (args.length == 2) + { + WorldTime timeOfDay = WorldTime.getByAlias(args[1]); + if (timeOfDay != null) + { + plugin.wm.masterBuilderWorld.setTimeOfDay(timeOfDay); + msg("MasterBuilder world time set to: " + timeOfDay.name()); + } + else + { + msg("Invalid time of day. Can be: sunrise, noon, sunset, midnight"); + } + } + else + { + return false; + } + + break; + } + case WEATHER: + { + assertCommandPerms(sender, playerSender); + + if (args.length == 2) + { + WorldWeather weatherMode = WorldWeather.getByAlias(args[1]); + if (weatherMode != null) + { + plugin.wm.masterBuilderWorld.setWeatherMode(weatherMode); + msg("MasterBuilder world weather set to: " + weatherMode.name()); + } + else + { + msg("Invalid weather mode. Can be: off, rain, storm"); + } + } + else + { + return false; + } + + break; + } + default: + { + return false; + } + } + } + catch (PermissionDeniedException ex) + { + if (ex.getMessage().isEmpty()) + { + return noPerms(); + } + msg(ex.getMessage()); + return true; + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + if (args.length == 1) + { + return Arrays.asList("time", "weather"); + } + else if (args.length == 2) + { + if (args[0].equals("time")) + { + return Arrays.asList("morning", "noon", "evening", "night"); + } + else if (args[0].equals("weather")) + { + return Arrays.asList("off", "rain", "storm"); + } + } + return Collections.emptyList(); + } + + // TODO: Redo this properly + private void assertCommandPerms(CommandSender sender, Player playerSender) throws PermissionDeniedException + { + if (!(sender instanceof Player) || playerSender == null || !plugin.al.isAdmin(playerSender)) + { + throw new PermissionDeniedException(); + } + } + + private enum CommandMode + { + TELEPORT, TIME, WEATHER + } + + private static class PermissionDeniedException extends Exception + { + + private static final long serialVersionUID = 1L; + + private PermissionDeniedException() + { + super(""); + } + + private PermissionDeniedException(String string) + { + super(string); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mbconfig.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mbconfig.java new file mode 100644 index 00000000..23320e41 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mbconfig.java @@ -0,0 +1,223 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "List, add, or remove master builders. Master builders can also clear their own IPs.", usage = "/ | clearips | < >>") +public class Command_mbconfig extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + switch (args[0]) + { + case "list": + { + msg("Master Builders: " + StringUtils.join(plugin.pl.getMasterBuilderNames(), ", "), ChatColor.GOLD); + return true; + } + + case "clearips": + { + if (args.length > 1) + { + return false; + } + + if (senderIsConsole) + { + msg("Only in-game players may use this command.", ChatColor.RED); + return true; + } + + PlayerData data = plugin.pl.getData(sender.getName()); + if (!data.isMasterBuilder()) + { + msg("You are not a master builder!", ChatColor.RED); + return true; + } + + int counter = data.getIps().size() - 1; + data.clearIps(); + data.addIp(FUtil.getIp(playerSender)); + plugin.sql.addPlayer(data); + msg(counter + " IPs removed."); + msg(data.getIps().get(0) + " is now your only IP address"); + FUtil.adminAction(sender.getName(), "Clearing my IPs", true); + return true; + } + case "clearip": + { + if (args.length < 2) + { + return false; + } + + if (senderIsConsole) + { + msg("Only in-game players may use this command.", ChatColor.RED); + return true; + } + + PlayerData data = plugin.pl.getData(sender.getName()); + final String targetIp = FUtil.getIp(playerSender); + + if (!data.isMasterBuilder()) + { + msg("You are not a master builder!", ChatColor.RED); + return true; + } + + if (targetIp.equals(args[1])) + { + msg("You cannot remove your current IP."); + return true; + } + data.removeIp(args[1]); + plugin.sql.addPlayer(data); + msg("Removed IP " + args[1]); + msg("Current IPs: " + StringUtils.join(data.getIps(), ", ")); + return true; + } + case "add": + { + if (args.length < 2) + { + return false; + } + + if (plugin.pl.canManageMasterBuilders(sender.getName())) + { + return noPerms(); + } + + final Player player = getPlayer(args[1]); + + PlayerData data = player != null ? plugin.pl.getData(player) : plugin.pl.getData(args[1]); + + if (data == null) + { + msg(PLAYER_NOT_FOUND, ChatColor.RED); + return true; + } + + if (data.isMasterBuilder() && plugin.pl.isPlayerImpostor(player)) + { + FUtil.adminAction(sender.getName(), "Re-adding " + data.getName() + " to the Master Builder list", true); + + if (plugin.pl.getPlayer(player).getFreezeData().isFrozen()) + { + plugin.pl.getPlayer(player).getFreezeData().setFrozen(false); + } + if (player != null) + { + plugin.pl.verify(player, null); + plugin.rm.updateDisplay(player); + player.setOp(true); + msg(player, YOU_ARE_OP); + } + } + else if (!data.isMasterBuilder()) + { + FUtil.adminAction(sender.getName(), "Adding " + data.getName() + " to the Master Builder list", true); + data.setMasterBuilder(true); + data.setVerification(true); + plugin.pl.save(data); + if (player != null) + { + plugin.rm.updateDisplay(player); + } + return true; + } + else + { + msg("That player is already on the Master Builder list."); + return true; + } + } + case "remove": + { + if (args.length < 2) + { + return false; + } + + if (plugin.pl.canManageMasterBuilders(sender.getName())) + { + return noPerms(); + } + + Player player = getPlayer(args[1]); + PlayerData data = player != null ? plugin.pl.getData(player) : plugin.pl.getData(args[1]); + + if (data == null || !data.isMasterBuilder()) + { + msg("Master Builder not found: " + args[1]); + return true; + } + + FUtil.adminAction(sender.getName(), "Removing " + data.getName() + " from the Master Builder list", true); + data.setMasterBuilder(false); + if (data.getDiscordID() == null) + { + data.setVerification(false); + } + plugin.pl.save(data); + if (player != null) + { + plugin.rm.updateDisplay(player); + } + return true; + } + default: + { + return false; + } + } + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return Arrays.asList("add", "remove", "list", "clearips", "clearip"); + } + else if (args.length == 2) + { + if (args[0].equalsIgnoreCase("add")) + { + return FUtil.getPlayerList(); + } + else if (args[0].equalsIgnoreCase("remove")) + { + return plugin.pl.getMasterBuilderNames(); + } + else if (args[0].equalsIgnoreCase("clearip")) + { + PlayerData data = plugin.pl.getData(sender.getName()); + if (data.isMasterBuilder()) + { + return data.getIps(); + } + return Collections.emptyList(); + } + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_moblimiter.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_moblimiter.java new file mode 100644 index 00000000..575ccf8e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_moblimiter.java @@ -0,0 +1,84 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.GameRuleHandler; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Control mob limiting parameters.", usage = "/ |dragon|giant|ghast|slime>") +public class Command_moblimiter extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("on")) + { + ConfigEntry.MOB_LIMITER_ENABLED.setBoolean(true); + } + else if (args[0].equalsIgnoreCase("off")) + { + ConfigEntry.MOB_LIMITER_ENABLED.setBoolean(false); + } + else if (args[0].equalsIgnoreCase("dragon")) + { + ConfigEntry.MOB_LIMITER_DISABLE_DRAGON.setBoolean(!ConfigEntry.MOB_LIMITER_DISABLE_DRAGON.getBoolean()); + } + else if (args[0].equalsIgnoreCase("giant")) + { + ConfigEntry.MOB_LIMITER_DISABLE_GIANT.setBoolean(!ConfigEntry.MOB_LIMITER_DISABLE_GIANT.getBoolean()); + } + else if (args[0].equalsIgnoreCase("slime")) + { + ConfigEntry.MOB_LIMITER_DISABLE_SLIME.setBoolean(!ConfigEntry.MOB_LIMITER_DISABLE_SLIME.getBoolean()); + } + else if (args[0].equalsIgnoreCase("ghast")) + { + ConfigEntry.MOB_LIMITER_DISABLE_GHAST.setBoolean(!ConfigEntry.MOB_LIMITER_DISABLE_GHAST.getBoolean()); + } + else + { + if (args.length < 2) + { + return false; + } + + if (args[0].equalsIgnoreCase("setmax")) + { + try + { + ConfigEntry.MOB_LIMITER_MAX.setInteger(Math.max(0, Math.min(2000, Integer.parseInt(args[1])))); + } + catch (NumberFormatException ignored) + { + } + } + } + + if (ConfigEntry.MOB_LIMITER_ENABLED.getBoolean()) + { + msg("Moblimiter enabled. Maximum mobcount set to: " + ConfigEntry.MOB_LIMITER_MAX.getInteger() + "."); + + msg("Dragon: " + (ConfigEntry.MOB_LIMITER_DISABLE_DRAGON.getBoolean() ? "disabled" : "enabled") + "."); + msg("Giant: " + (ConfigEntry.MOB_LIMITER_DISABLE_GIANT.getBoolean() ? "disabled" : "enabled") + "."); + msg("Slime: " + (ConfigEntry.MOB_LIMITER_DISABLE_SLIME.getBoolean() ? "disabled" : "enabled") + "."); + msg("Ghast: " + (ConfigEntry.MOB_LIMITER_DISABLE_GHAST.getBoolean() ? "disabled" : "enabled") + "."); + } + else + { + msg("Moblimiter is disabled. No mob restrictions are in effect."); + } + + plugin.gr.setGameRule(GameRuleHandler.GameRule.DO_MOB_SPAWNING, !ConfigEntry.MOB_LIMITER_ENABLED.getBoolean()); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java new file mode 100644 index 00000000..7d726cbd --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mobpurge.java @@ -0,0 +1,75 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Purge all mobs in all worlds.", usage = "/ [name]", aliases = "mp") +public class Command_mobpurge extends FreedomCommand +{ + + public static List getAllMobNames() + { + List names = new ArrayList<>(); + for (EntityType entityType : Groups.MOB_TYPES) + { + names.add(entityType.name()); + } + return names; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + EntityType type = null; + String mobName = null; + if (args.length > 0) + { + try + { + type = EntityType.valueOf(args[0].toUpperCase()); + } + catch (Exception e) + { + msg(args[0] + " is not a valid mob type.", ChatColor.RED); + return true; + } + + if (!Groups.MOB_TYPES.contains(type)) + { + msg(FUtil.formatName(type.name()) + " is an entity, however it is not a mob.", ChatColor.RED); + return true; + } + } + + if (type != null) + { + mobName = FUtil.formatName(type.name()); + } + + FUtil.adminAction(sender.getName(), "Purging all " + (type != null ? mobName + "s" : "mobs"), true); + int count = plugin.ew.purgeMobs(type); + msg(count + " " + (type != null ? mobName : "mob") + FUtil.showS(count) + " removed."); + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return getAllMobNames(); + } + + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_modifyitem.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_modifyitem.java new file mode 100644 index 00000000..ae5847d7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_modifyitem.java @@ -0,0 +1,265 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.SplittableRandom; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffectType; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Modify the current item you are holding.", usage = "/ | lore | enchant | potion | attribute | clear>", aliases = "mi") +public class Command_modifyitem extends FreedomCommand +{ + @SuppressWarnings("deprecation") + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + ItemStack item = playerSender.getInventory().getItemInMainHand(); + if (item.getType().equals(Material.AIR)) + { + msg("You must have an item in your hand!"); + return true; + } + + if (args[0].equalsIgnoreCase("clear")) + { + item.setItemMeta(null); + playerSender.getInventory().setItemInMainHand(item); + return true; + } + + if (args.length < 2) + { + return false; + } + + ItemMeta meta = item.getItemMeta(); + assert meta != null; + switch (args[0]) + { + case "name": + String name = FUtil.colorize(StringUtils.join(args, " ", 1, args.length)); + meta.setDisplayName(name); + item.setItemMeta(meta); + break; + + case "lore": + List lore = new ArrayList<>(); + for (String line : StringUtils.join(args, " ", 1, args.length).split("\\\\n")) + { + lore.add(FUtil.colorize(line)); + } + meta.setLore(lore); + item.setItemMeta(meta); + break; + + case "enchant": + if (args.length < 3) + { + return false; + } + + Enchantment enchantment = Enchantment.getByName(args[1].toUpperCase()); + if (enchantment == null) + { + msg("Invalid enchantment. Please run /enchant list for a list of valid enchantments."); + return true; + } + + int level; + try + { + level = Integer.parseInt(args[2]); + } + catch (NumberFormatException ex) + { + msg("The level specified is not a valid integer."); + return true; + } + meta.addEnchant(enchantment, level, true); + item.setItemMeta(meta); + break; + + case "potion": + { + if (!item.getType().equals(Material.POTION) & !item.getType().equals(Material.SPLASH_POTION) & !item.getType().equals(Material.LINGERING_POTION) & !item.getType().equals(Material.TIPPED_ARROW)) + { + msg("This item can not have potion effects added to it."); + return true; + } + + if (args.length < 4) + { + return false; + } + + PotionEffectType type = PotionEffectType.getByName(args[1]); + if (type == null) + { + msg("Invalid potion effect. Please run /potion list for a list of valid potion effects."); + return true; + } + + int duration; + try + { + duration = Math.max(1, Math.min(1000000, Integer.parseInt(args[2]))); + } + catch (NumberFormatException ex) + { + msg("The duration specified is not a valid integer."); + return true; + } + + int amplifier; + try + { + amplifier = Math.max(1, Math.min(256, Integer.parseInt(args[2]))); + } + catch (NumberFormatException ex) + { + msg("The amplifier specified is not a valid integer."); + return true; + } + PotionMeta potionMeta = (PotionMeta)meta; + potionMeta.addCustomEffect(type.createEffect(duration, amplifier), true); + item.setItemMeta(potionMeta); + break; + } + + case "attribute": + if (args.length < 3) + { + return false; + } + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(item); + NBTTagCompound compound = (nmsStack.hasTag()) ? nmsStack.getTag() : new NBTTagCompound(); + NBTTagList modifiers = getAttributeList(nmsStack); + NBTTagCompound cmpnd = new NBTTagCompound(); + Attribute attribute = Attribute.getByName(args[1].toUpperCase()); + if (attribute == null) + { + msg("Invalid attribute. Please run /attributelist for a list of valid attributes."); + return true; + } + cmpnd.setString("AttributeName", attribute.getAttribute()); + cmpnd.setString("Name", attribute.getAttribute()); + double amount; + try + { + amount = Double.parseDouble(args[2]); + } + catch (NumberFormatException ex) + { + msg("The amount specified is not a valid integer."); + return true; + } + if (Double.isNaN(amount)) + { + msg("The amount specified is illegal."); + return true; + } + cmpnd.setDouble("Amount", amount); + cmpnd.setInt("Operation", 0); + SplittableRandom random = new SplittableRandom(); + cmpnd.setIntArray("UUID", new int[] + { + random.nextInt(), + random.nextInt(), + random.nextInt(), + random.nextInt() + }); + cmpnd.setString("Slot", "mainhand"); + modifiers.add(cmpnd); + assert compound != null; + compound.set("AttributeModifiers", modifiers); + nmsStack.setTag(compound); + item = CraftItemStack.asBukkitCopy(nmsStack); + break; + default: + return false; + } + playerSender.getInventory().setItemInMainHand(item); + return true; + } + + private NBTTagList getAttributeList(net.minecraft.world.item.ItemStack stack) + { + if (stack.getTag() == null) + { + stack.setTag(new NBTTagCompound()); + } + NBTTagList attr = stack.getTag().getList("AttributeModifiers", 10); + if (attr == null) + { + stack.getTag().set("AttributeModifiers", new NBTTagList()); + } + return stack.getTag().getList("AttributeModifiers", 10); + } + + private enum Attribute + { + GENERIC_MAX_HEALTH("GENERIC_MAX_HEALTH", "generic.max_health"), + GENERIC_FOLLOW_RANGE("GENERIC_FOLLOW_RANGE", "generic.follow_range"), + GENERIC_KNOCKBACK_RESISTANCE("GENERIC_KNOCKBACK_RESISTANCE", "generic.knockback_resistance"), + GENERIC_MOVEMENT_SPEED("GENERIC_MOVEMENT_SPEED", "generic.movement_speed"), + GENERIC_FLYING_SPEED("GENERIC_FLYING_SPEED", "generic.flying_speed"), + GENERIC_ATTACK_DAMAGE("GENERIC_ATTACK_DAMAGE", "generic.attack_damage"), + GENERIC_ATTACK_SPEED("GENERIC_ATTACK_SPEED", "generic.attack_speed"), + GENERIC_ARMOR("GENERIC_ARMOR", "generic.armor"), + GENERIC_ARMOR_TOUGHNESS("GENERIC_ARMOR_TOUGHNESS", "generic.armor_toughmess"), + GENERIC_LUCK("GENERIC_LUCK", "generic.luck"), + HORSE_JUMP_STRENGTH("GENERIC_MAX_HEALTH", "horse.jump_strength"), + ZOMBIE_SPAWN_REINFORCEMENTS("ZOMBIE_SPAWN_REINFORCEMENTS", "zombie.spawn_reinforcements"); + + private final String name; + private final String attribute; + + Attribute(String name, String attribute) + { + this.name = name; + this.attribute = attribute; + } + + public static Attribute getByName(String name) + { + for (Attribute attr : Attribute.values()) + { + if (attr.toString().toUpperCase().equals(name)) + { + return attr; + } + } + return null; + } + + public String getAttribute() + { + return attribute; + } + + @Override + public String toString() + { + return name; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mp44.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mp44.java new file mode 100644 index 00000000..8cb0a9a9 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mp44.java @@ -0,0 +1,53 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Modern weaponry, FTW. Use 'draw' to start firing, 'sling' to stop firing.", usage = "/ ") +public class Command_mp44 extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!ConfigEntry.MP44_ENABLED.getBoolean()) + { + msg("The mp44 is currently disabled.", ChatColor.GREEN); + return true; + } + + if (args.length == 0) + { + return false; + } + + FPlayer playerdata = plugin.pl.getPlayer(playerSender); + + if (args[0].equalsIgnoreCase("draw")) + { + playerdata.armMP44(); + + msg("mp44 is ARMED! Left click with gunpowder to start firing, left click again to quit.", ChatColor.GREEN); + msg("Type /mp44 sling to disable. -by Madgeek1450", ChatColor.GREEN); + + Objects.requireNonNull(playerSender.getEquipment()).setItemInMainHand(new ItemStack(Material.GUNPOWDER, 1)); + } + else + { + playerdata.disarmMP44(); + + msg("mp44 Disarmed.", ChatColor.GREEN); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mute.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mute.java new file mode 100644 index 00000000..0ce530d4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_mute.java @@ -0,0 +1,181 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Mutes a player with brute force.", usage = "/ <[-s | -q] [reason] | list | purge | all>", aliases = "stfu") +public class Command_mute extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + if (args[0].equalsIgnoreCase("list")) + { + msg("Muted players:"); + FPlayer info; + int count = 0; + for (Player mp : server.getOnlinePlayers()) + { + info = plugin.pl.getPlayer(mp); + if (info.isMuted()) + { + msg("- " + mp.getName()); + count++; + } + } + if (count == 0) + { + msg("- none"); + } + + return true; + } + + if (args[0].equalsIgnoreCase("purge")) + { + FUtil.adminAction(sender.getName(), "Unmuting all players.", true); + FPlayer info; + int count = 0; + for (Player mp : server.getOnlinePlayers()) + { + info = plugin.pl.getPlayer(mp); + if (info.isMuted()) + { + info.setMuted(false); + mp.sendTitle(ChatColor.RED + "You've been unmuted.", ChatColor.YELLOW + "Be sure to follow the rules!", 20, 100, 60); + count++; + } + } + plugin.mu.MUTED_PLAYERS.clear(); + msg("Unmuted " + count + " players."); + return true; + } + + if (args[0].equalsIgnoreCase("all")) + { + FUtil.adminAction(sender.getName(), "Muting all non-admins", true); + + FPlayer playerdata; + int counter = 0; + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + player.sendTitle(ChatColor.RED + "You've been muted globally.", ChatColor.YELLOW + "Please be patient and you will be unmuted shortly.", 20, 100, 60); + playerdata = plugin.pl.getPlayer(player); + playerdata.setMuted(true); + counter++; + } + } + + msg("Muted " + counter + " players."); + return true; + } + + // -s option (smite) + boolean smite = args[0].equals("-s"); + // -q option (shadowmute) + boolean quiet = args[0].equals("-q"); + if (smite || quiet) + { + args = ArrayUtils.subarray(args, 1, args.length); + + if (args.length < 1) + { + return false; + } + } + + final Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + String reason = null; + if (args.length > 1) + { + reason = StringUtils.join(args, " ", 1, args.length); + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + if (plugin.al.isAdmin(player)) + { + msg(player.getName() + " is an admin, and can't be muted."); + return true; + } + + if (!playerdata.isMuted()) + { + playerdata.setMuted(true); + player.sendTitle(ChatColor.RED + "You've been muted.", ChatColor.YELLOW + "Be sure to follow the rules!", 20, 100, 60); + if (reason != null) + { + msg(player, ChatColor.RED + "Reason: " + ChatColor.YELLOW + reason); + } + if (quiet) + { + msg("Muted " + player.getName() + " quietly"); + return true; + } + + FUtil.adminAction(sender.getName(), "Muting " + player.getName(), true); + + if (smite) + { + Command_smite.smite(sender, player, reason); + } + + msg(player, "You have been muted by " + ChatColor.YELLOW + sender.getName(), ChatColor.RED); + msg("Muted " + player.getName()); + + plugin.pul.logPunishment(new Punishment(player.getName(), FUtil.getIp(player), sender.getName(), PunishmentType.MUTE, reason)); + } + else + { + msg(ChatColor.RED + "That player is already muted."); + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return null; + } + + if (args.length == 1) + { + List arguments = new ArrayList<>(); + arguments.addAll(FUtil.getPlayerList()); + arguments.addAll(Arrays.asList("list", "purge", "all")); + return arguments; + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java new file mode 100644 index 00000000..1c38639c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myadmin.java @@ -0,0 +1,260 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Manage your admin entry.", usage = "/ [-o ] | setscformat | clearscformat> | syncroles>") +public class Command_myadmin extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + Player init = null; + Admin target = getAdmin(playerSender); + Player targetPlayer = playerSender; + + // -o switch + if (args[0].equals("-o")) + { + checkRank(Rank.SENIOR_ADMIN); + init = playerSender; + targetPlayer = getPlayer(args[1]); + if (targetPlayer == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + target = getAdmin(targetPlayer); + if (target == null) + { + msg("That player is not an admin", ChatColor.RED); + return true; + } + + // Shift 2 + args = Arrays.copyOfRange(args, 2, args.length); + if (args.length < 1) + { + return false; + } + } + + final String targetIp = FUtil.getIp(targetPlayer); + + switch (args[0]) + { + case "clearips": + { + if (args.length != 1) + { + return false; // Double check: the player might mean "clearip" + } + + if (init == null) + { + FUtil.adminAction(sender.getName(), "Clearing my IPs", true); + } + else + { + FUtil.adminAction(sender.getName(), "Clearing " + target.getName() + "'s IPs", true); + } + + int counter = target.getIps().size() - 1; + target.clearIPs(); + target.addIp(targetIp); + + plugin.al.save(target); + plugin.al.updateTables(); + plugin.pl.syncIps(target); + + msg(counter + " IPs removed."); + msg(targetPlayer, target.getIps().get(0) + " is now your only IP address"); + return true; + } + + case "clearip": + { + if (args.length != 2) + { + return false; // Double check: the player might mean "clearips" + } + + if (!target.getIps().contains(args[1])) + { + if (init == null) + { + msg("That IP is not registered to you."); + } + else + { + msg("That IP does not belong to that player."); + } + return true; + } + + if (targetIp.equals(args[1])) + { + if (init == null) + { + msg("You cannot remove your current IP."); + } + else + { + msg("You cannot remove that admins current IP."); + } + return true; + } + + FUtil.adminAction(sender.getName(), "Removing an IP" + (init == null ? "" : " from " + targetPlayer.getName() + "'s IPs"), true); + + target.removeIp(args[1]); + plugin.al.save(target); + plugin.al.updateTables(); + + plugin.pl.syncIps(target); + + msg("Removed IP " + args[1]); + msg("Current IPs: " + StringUtils.join(target.getIps(), ", ")); + return true; + } + + case "setacformat": + case "setscformat": + { + String format = StringUtils.join(args, " ", 1, args.length); + target.setAcFormat(format); + plugin.al.save(target); + plugin.al.updateTables(); + msg("Set admin chat format to \"" + format + "\".", ChatColor.GRAY); + String example = format.replace("%name%", "ExampleAdmin").replace("%rank%", Rank.ADMIN.getAbbr()).replace("%rankcolor%", Rank.ADMIN.getColor().toString()).replace("%msg%", "The quick brown fox jumps over the lazy dog."); + msg(ChatColor.GRAY + "Example: " + FUtil.colorize(example)); + return true; + } + + case "clearacformat": + case "clearscformat": + { + target.setAcFormat(null); + plugin.al.save(target); + plugin.al.updateTables(); + msg("Cleared admin chat format.", ChatColor.GRAY); + return true; + } + + case "syncroles": + { + if (plugin.dc.enabled) + { + if (!ConfigEntry.DISCORD_ROLE_SYNC.getBoolean()) + { + msg("Role syncing is not enabled.", ChatColor.RED); + return true; + } + PlayerData playerData = plugin.pl.getData(target.getName()); + if (playerData.getDiscordID() == null) + { + msg("Please run /linkdiscord first!", ChatColor.RED); + return true; + } + boolean synced = Discord.syncRoles(target, playerData.getDiscordID()); + if (synced) + { + msg("Successfully synced your roles.", ChatColor.GREEN); + } + else + { + msg("Failed to sync your roles, please check the console.", ChatColor.RED); + } + } + + return true; + } + + default: + { + return false; + } + } + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + + List singleArguments = Arrays.asList("clearips", "setscformat", "setacformat"); + List doubleArguments = Arrays.asList("clearip", "clearscformat", "clearacformat", "syncroles"); + if (args.length == 1) + { + List options = new ArrayList<>(); + options.add("-o"); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + else if (args.length == 2) + { + if (args[0].equals("-o")) + { + return FUtil.getPlayerList(); + } + else + { + if (doubleArguments.contains(args[0])) + { + if (args[0].equals("clearip")) + { + List ips = plugin.al.getAdmin(sender).getIps(); + ips.remove(FUtil.getIp((Player)sender)); + return ips; + } + } + } + } + else if (args.length == 3) + { + if (args[0].equals("-o")) + { + List options = new ArrayList<>(); + options.addAll(singleArguments); + options.addAll(doubleArguments); + return options; + } + } + else if (args.length == 4) + { + if (args[0].equals("-o") && args[2].equals("clearip")) + { + Admin admin = plugin.al.getEntryByName(args[1]); + if (admin != null) + { + return admin.getIps(); + } + } + } + return FUtil.getPlayerList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myinfo.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myinfo.java new file mode 100644 index 00000000..0bd209ea --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_myinfo.java @@ -0,0 +1,18 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Check your personal data", usage = "/") +public class Command_myinfo extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + msg(plugin.pl.getData(playerSender).toString()); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_namehistory.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_namehistory.java new file mode 100644 index 00000000..1bc2033c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_namehistory.java @@ -0,0 +1,23 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.History; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Check the name history of a specified player.", usage = "/ ", aliases = "nh") +public class Command_namehistory extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + History.reportHistory(sender, args[0]); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nether.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nether.java new file mode 100644 index 00000000..6867fb97 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nether.java @@ -0,0 +1,19 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Go to the Nether.", usage = "/", aliases = "hell") +public class Command_nether extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + plugin.wm.gotoWorld(playerSender, server.getWorlds().get(0).getName() + "_nether"); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickclean.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickclean.java new file mode 100644 index 00000000..1ba43927 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickclean.java @@ -0,0 +1,152 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Essentials Interface Command - Remove illegal chatcodes from nicknames of one or all players on server.", usage = "/ [player]", aliases = "nc") +public class Command_nickclean extends FreedomCommand +{ + private final Map colorCodes = new HashMap() + {{ + put("&0", Color.BLACK); + put("&1", Color.BLUE); + put("&2", Color.GREEN); + put("&3", Color.TEAL); + put("&4", Color.MAROON); + put("&5", Color.FUCHSIA); + put("&6", Color.OLIVE); + put("&7", Color.SILVER); + put("&8", Color.GRAY); + put("&9", Color.NAVY); + put("&a", Color.LIME); + put("&b", Color.AQUA); + put("&c", Color.RED); + put("&d", Color.PURPLE); + put("&e", Color.YELLOW); + put("&f", Color.WHITE); + }}; + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 1) + { + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + FUtil.adminAction(sender.getName(), "Cleaning " + player.getName() + "'s nickname", false); + cleanNickname(player); + return true; + } + + FUtil.adminAction(sender.getName(), "Cleaning all nicknames", false); + for (final Player player : server.getOnlinePlayers()) + { + cleanNickname(player); + } + return true; + } + + public void cleanNickname(Player player) + { + final String playerName = player.getName(); + final String nickName = plugin.esb.getNickname(playerName); + StringBuilder newNick = new StringBuilder(); + boolean nickChanged = false; + if (nickName != null) + { + if (nickName.contains("§x")) + { + // Detects colors that are similar to blocked codes. + spliterator: + for (String split : nickName.split("§x")) + { + List colors = new ArrayList<>(); + String hexColorSub; + if (split.length() >= 12 && split.contains("§")) + { + hexColorSub = split.substring(0, 12); + split = String.valueOf(split.charAt(12)); + String hexColorString = "#" + hexColorSub.replaceAll("§", ""); + java.awt.Color hexColor = java.awt.Color.decode(hexColorString); + + // Get a range of nearby colors that are alike to the color blocked. + Color colorFirst; + Color colorSecond; + + colorFirst = Color.fromRGB(Math.min(hexColor.getRed() + 20, 255), Math.min(hexColor.getGreen() + 20, 255), Math.min(hexColor.getBlue() + 20, 255)); + colorSecond = Color.fromRGB(Math.max(hexColor.getRed() - 20, 0), Math.max(hexColor.getGreen() - 20, 0), Math.max(hexColor.getBlue() - 20, 0)); + colors.addAll(FUtil.createColorGradient(colorFirst, colorSecond, 40)); + + colorFirst = Color.fromRGB(Math.min(hexColor.getRed() + 20, 255), Math.min(hexColor.getGreen(), 255), Math.min(hexColor.getBlue(), 255)); + colorSecond = Color.fromRGB(Math.max(hexColor.getRed() - 20, 0), Math.max(hexColor.getGreen(), 0), Math.max(hexColor.getBlue(), 0)); + colors.addAll(FUtil.createColorGradient(colorFirst, colorSecond, 40)); + + colorFirst = Color.fromRGB(Math.min(hexColor.getRed(), 255), Math.min(hexColor.getGreen() + 20, 255), Math.min(hexColor.getBlue(), 255)); + colorSecond = Color.fromRGB(Math.max(hexColor.getRed(), 0), Math.max(hexColor.getGreen() - 20, 0), Math.max(hexColor.getBlue(), 0)); + colors.addAll(FUtil.createColorGradient(colorFirst, colorSecond, 40)); + + colorFirst = Color.fromRGB(Math.min(hexColor.getRed(), 255), Math.min(hexColor.getGreen(), 255), Math.min(hexColor.getBlue() + 20, 255)); + colorSecond = Color.fromRGB(Math.max(hexColor.getRed(), 0), Math.max(hexColor.getGreen(), 0), Math.max(hexColor.getBlue() - 20, 0)); + colors.addAll(FUtil.createColorGradient(colorFirst, colorSecond, 40)); + + for (String colorCode : ConfigEntry.BLOCKED_CHATCODES.getString().split(",")) + { + // Makes sure that there's hex colors in the split. + for (Color color : colors) + { + if (colorCodes.get(colorCode) != null && FUtil.colorClose(color, colorCodes.get(colorCode), 40)) + { + nickChanged = true; + newNick.append(split); + continue spliterator; + } + } + + } + newNick.append("§x").append(hexColorSub).append(split); + } + } + } + else + { + // Falls back on old code if hex isn't used. + final Pattern REGEX = Pattern.compile(FUtil.colorize(ChatColor.COLOR_CHAR + "[" + StringUtils.join(ConfigEntry.BLOCKED_CHATCODES.getString().split(","), "") + "]"), Pattern.CASE_INSENSITIVE); + if (!nickName.isEmpty() && !nickName.equalsIgnoreCase(playerName)) + { + final Matcher matcher = REGEX.matcher(nickName); + if (matcher.find()) + { + nickChanged = true; + newNick.append(matcher.replaceAll("")); + } + } + } + } + + if (nickChanged) + { + msg(ChatColor.RESET + playerName + ": \"" + nickName + ChatColor.RESET + "\" -> \"" + newNick.toString() + ChatColor.RESET + "\"."); + } + plugin.esb.setNickname(playerName, newNick.toString()); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickfilter.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickfilter.java new file mode 100644 index 00000000..fef2ee56 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickfilter.java @@ -0,0 +1,120 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "NickFilter: Prefix any command with this command to replace nicknames in that command with real names. Nicknames should be prefixed with a !.", + usage = "/ !", + aliases = "nf") +public class Command_nickfilter extends FreedomCommand +{ + + private static Player getPlayerByDisplayName(String needle) + { + needle = needle.toLowerCase().trim(); + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (player.getDisplayName().toLowerCase().trim().contains(needle)) + { + return player; + } + } + + return null; + } + + private static Player getPlayerByDisplayNameAlt(String needle) + { + needle = needle.toLowerCase().trim(); + + Integer minEditDistance = null; + Player minEditMatch = null; + + for (Player player : Bukkit.getOnlinePlayers()) + { + String haystack = player.getDisplayName().toLowerCase().trim(); + int editDistance = StringUtils.getLevenshteinDistance(needle, haystack.toLowerCase()); + if (minEditDistance == null || minEditDistance > editDistance) + { + minEditDistance = editDistance; + minEditMatch = player; + } + } + + return minEditMatch; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean nickMatched = false; + + final List outputCommand = new ArrayList<>(); + + if (args.length >= 1) + { + for (String arg : args) + { + Player player = null; + + Matcher matcher = Pattern.compile("^!(.+)$").matcher(arg); + if (matcher.find()) + { + String displayName = matcher.group(1); + + player = getPlayerByDisplayName(displayName); + + if (player == null || plugin.al.isVanished(player.getName()) && !plugin.al.isAdmin(sender)) + { + player = getPlayerByDisplayNameAlt(displayName); + + if (player == null || !plugin.al.isVanished(player.getName()) && !plugin.al.isAdmin(sender)) + { + msg("Can't find player by nickname: " + displayName); + return true; + } + } + } + + if (player == null) + { + outputCommand.add(arg); + } + else + { + nickMatched = true; + outputCommand.add(player.getName()); + } + } + } + + if (!nickMatched) + { + msg("No nicknames replaced in command."); + return true; + } + + String newCommand = StringUtils.join(outputCommand, " "); + + if (plugin.cb.isCommandBlocked(newCommand, sender)) + { + // CommandBlocker handles messages and broadcasts + return true; + } + + msg("Sending command: \"" + newCommand + "\"."); + server.dispatchCommand(sender, newCommand); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickgradient.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickgradient.java new file mode 100644 index 00000000..3fba2d4f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickgradient.java @@ -0,0 +1,94 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Essentials Interface Command - Rainbowify your nickname.", usage = "/ ", aliases = "nickgr") +public class Command_nickgradient extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 3) + { + return false; + } + + String nick = args[2].trim(); + + if (nick.length() < 3 || nick.length() > 30) + { + msg("Your nickname must be between 3 and 30 characters long."); + return true; + } + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (player == playerSender) + { + continue; + } + if (player.getName().equalsIgnoreCase(nick) || ChatColor.stripColor(player.getDisplayName()).trim().equalsIgnoreCase(nick)) + { + msg("That nickname is already in use."); + return true; + } + } + + String from = "", to = ""; + java.awt.Color awt1, awt2; + try + { + if (args[0].equalsIgnoreCase("random") || + args[0].equalsIgnoreCase("r")) + { + awt1 = FUtil.getRandomAWTColor(); + from = " (From: " + FUtil.getHexStringOfAWTColor(awt1) + ")"; + } + else + { + awt1 = java.awt.Color.decode(args[0]); + } + if (args[1].equalsIgnoreCase("random") || + args[1].equalsIgnoreCase("r")) + { + awt2 = FUtil.getRandomAWTColor(); + to = " (To: " + FUtil.getHexStringOfAWTColor(awt2) + ")"; + } + else + { + awt2 = java.awt.Color.decode(args[1]); + } + } + catch (NumberFormatException ex) + { + msg("Invalid hex values."); + return true; + } + Color c1 = FUtil.fromAWT(awt1); + Color c2 = FUtil.fromAWT(awt2); + List gradient = FUtil.createColorGradient(c1, c2, nick.length()); + String[] splitNick = nick.split(""); + for (int i = 0; i < splitNick.length; i++) + { + splitNick[i] = net.md_5.bungee.api.ChatColor.of(FUtil.toAWT(gradient.get(i))) + splitNick[i]; + } + nick = StringUtils.join(splitNick, ""); + final String outputNick = FUtil.colorize(nick); + + plugin.esb.setNickname(sender.getName(), outputNick); + + msg("Your nickname is now: '" + outputNick + ChatColor.GRAY + "'" + from + to); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nicknyan.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nicknyan.java new file mode 100644 index 00000000..706b54d5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nicknyan.java @@ -0,0 +1,72 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Essentials Interface Command - Randomize the colors of your nickname.", usage = "/ < | off>") +public class Command_nicknyan extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("off")) + { + plugin.esb.setNickname(sender.getName(), null); + msg("Nickname cleared."); + return true; + } + + final String nickPlain = ChatColor.stripColor(FUtil.colorize(args[0].trim())); + + if (!nickPlain.matches("^[a-zA-Z_0-9" + ChatColor.COLOR_CHAR + "]+$")) + { + msg("That nickname contains invalid characters."); + return true; + } + else if (nickPlain.length() < 3 || nickPlain.length() > 30) + { + msg("Your nickname must be between 3 and 30 characters long."); + return true; + } + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (player == playerSender) + { + continue; + } + if (player.getName().equalsIgnoreCase(nickPlain) || ChatColor.stripColor(player.getDisplayName()).trim().equalsIgnoreCase(nickPlain)) + { + msg("That nickname is already in use."); + return true; + } + } + + final StringBuilder newNick = new StringBuilder(); + + final char[] chars = nickPlain.toCharArray(); + for (char c : chars) + { + newNick.append(FUtil.randomChatColor()).append(c); + } + + newNick.append(ChatColor.WHITE); + + plugin.esb.setNickname(sender.getName(), newNick.toString()); + + msg("Your nickname is now: " + newNick.toString()); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickrainbow.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickrainbow.java new file mode 100644 index 00000000..d82b4f30 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_nickrainbow.java @@ -0,0 +1,59 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Essentials Interface Command - Rainbowify your nickname.", usage = "/ ") +public class Command_nickrainbow extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + final String nickPlain = ChatColor.stripColor(FUtil.colorize(args[0].trim())); + + if (!nickPlain.matches("^[a-zA-Z_0-9" + ChatColor.COLOR_CHAR + "]+$")) + { + msg("That nickname contains invalid characters."); + return true; + } + + if (nickPlain.length() < 3 || nickPlain.length() > 30) + { + msg("Your nickname must be between 3 and 30 characters long."); + return true; + } + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (player == playerSender) + { + continue; + } + if (player.getName().equalsIgnoreCase(nickPlain) || ChatColor.stripColor(player.getDisplayName()).trim().equalsIgnoreCase(nickPlain)) + { + msg("That nickname is already in use."); + return true; + } + } + + final String newNick = FUtil.rainbowify(ChatColor.stripColor(FUtil.colorize(nickPlain))); + + plugin.esb.setNickname(sender.getName(), newNick); + + msg("Your nickname is now: " + newNick); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java new file mode 100644 index 00000000..8a4754b2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_notes.java @@ -0,0 +1,140 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Manage notes for a player", usage = "/ | remove | clear>") +public class Command_notes extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 2) + { + return false; + } + + PlayerData playerData; + + final Player player = getPlayer(args[0]); + if (player == null) + { + final PlayerData entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + playerData = plugin.pl.getData(entry.getName()); + } + else + { + playerData = plugin.pl.getData(player); + } + + switch (args[1]) + { + case "list": + { + final StringBuilder noteList = new StringBuilder(); + noteList.append(ChatColor.GREEN).append("Player notes for ").append(playerData.getName()).append(":"); + int id = 1; + for (String note : playerData.getNotes()) + { + String noteLine = id + ". " + note; + noteList.append("\n").append(ChatColor.GOLD).append(noteLine); + id++; + } + msg(noteList.toString()); + return true; + } + + case "add": + { + if (args.length < 3) + { + return false; + } + String note = sender.getName() + ": " + StringUtils.join(ArrayUtils.subarray(args, 2, args.length), " "); + playerData.addNote(note); + plugin.pl.save(playerData); + msg("Note added.", ChatColor.GREEN); + return true; + } + + case "remove": + { + if (args.length < 3) + { + return false; + } + + int id; + try + { + id = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) + { + msg("Invalid number: " + args[2], ChatColor.RED); + return true; + } + + id--; + + if (playerData.removeNote(id)) + { + plugin.pl.save(playerData); + msg("Note removed."); + } + else + { + msg("No note with the ID of " + args[2] + " exists.", ChatColor.RED); + } + return true; + } + + case "clear": + { + int count = playerData.getNotes().size(); + playerData.clearNotes(); + plugin.pl.save(playerData); + msg("Cleared " + count + " notes.", ChatColor.GREEN); + return true; + } + } + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return FUtil.getPlayerList(); + } + else if (args.length == 2) + { + return Arrays.asList("list", "add", "remove", "clear"); + } + else if (args.length > 2 && (args[1].equals("add"))) + { + return FUtil.getPlayerList(); + } + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_op.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_op.java new file mode 100644 index 00000000..ece9abba --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_op.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH, cooldown = 5) +@CommandParameters(description = "OP a player", usage = "/ ") +public class Command_op extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + boolean silent = false; + if (args.length == 2) + { + silent = args[1].equalsIgnoreCase("-s"); + } + + final String targetName = args[0].toLowerCase(); + + List matchedPlayerNames = new ArrayList<>(); + for (final Player player : server.getOnlinePlayers()) + { + if (player.getName().toLowerCase().contains(targetName) || player.getDisplayName().toLowerCase().contains(targetName) + || player.getName().contains(targetName) || player.getDisplayName().contains(targetName)) + { + if (!player.isOp() && !plugin.al.isVanished(player.getName())) + { + matchedPlayerNames.add(player.getName()); + player.setOp(true); + msg(player, YOU_ARE_OP); + plugin.rm.updateDisplay(player); + } + } + } + + if (!matchedPlayerNames.isEmpty()) + { + if (!silent) + { + FUtil.adminAction(sender.getName(), "Opping " + StringUtils.join(matchedPlayerNames, ", "), false); + } + } + else + { + msg("Either the player is already opped, or the player could not be found."); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opall.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opall.java new file mode 100644 index 00000000..a02671cc --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opall.java @@ -0,0 +1,31 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH, cooldown = 30) +@CommandParameters(description = "OP everyone on the server.", usage = "/") +public class Command_opall extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "Opping all players on the server", false); + + for (Player player : server.getOnlinePlayers()) + { + if (!player.isOp()) + { + player.setOp(true); + msg(player, YOU_ARE_OP); + plugin.rm.updateDisplay(player); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opme.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opme.java new file mode 100644 index 00000000..93d71319 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_opme.java @@ -0,0 +1,23 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "OPs the command sender.", usage = "/") +public class Command_opme extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "Opping " + sender.getName(), false); + sender.setOp(true); + msg(YOU_ARE_OP); + plugin.rm.updateDisplay(playerSender); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ops.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ops.java new file mode 100644 index 00000000..70686735 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ops.java @@ -0,0 +1,65 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Manage operators", usage = "/ ") +public class Command_ops extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("count")) + { + int totalOps = server.getOperators().size(); + int onlineOps = 0; + + for (Player player : server.getOnlinePlayers()) + { + if (player.isOp()) + { + onlineOps++; + } + } + + msg("Online OPs: " + onlineOps); + msg("Offline OPs: " + (totalOps - onlineOps)); + msg("Total OPs: " + totalOps); + return true; + } + + if (args[0].equalsIgnoreCase("purge")) + { + if (!plugin.al.isAdmin(sender)) + { + noPerms(); + return true; + } + + FUtil.adminAction(sender.getName(), "Purging all operators", true); + + for (OfflinePlayer player : server.getOperators()) + { + player.setOp(false); + if (player.isOnline()) + { + msg(Objects.requireNonNull(player.getPlayer()), FreedomCommand.YOU_ARE_NOT_OP); + } + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_orbit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_orbit.java new file mode 100644 index 00000000..08894999 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_orbit.java @@ -0,0 +1,66 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "POW!!! Right in the kisser! One of these days Alice, straight to the Moon - Sends the specified player into orbit.", + usage = "/ [< | stop>]") +public class Command_orbit extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND, ChatColor.RED); + return true; + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + + double strength = 10.0; + + if (args.length >= 2) + { + if (args[1].equalsIgnoreCase("stop")) + { + msg("Stopped orbiting " + player.getName()); + playerdata.stopOrbiting(); + return true; + } + + try + { + strength = Math.max(1.0, Math.min(150.0, Double.parseDouble(args[1]))); + } + catch (NumberFormatException ex) + { + msg(ex.getMessage(), ChatColor.RED); + return true; + } + } + + player.setGameMode(GameMode.SURVIVAL); + playerdata.startOrbiting(strength); + + player.setVelocity(new Vector(0, strength, 0)); + FUtil.adminAction(sender.getName(), "Orbiting " + player.getName(), false); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java new file mode 100644 index 00000000..a6348b01 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_panel.java @@ -0,0 +1,104 @@ +package me.totalfreedom.totalfreedommod.command; + +import com.google.common.base.Strings; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Manage your Pterodactyl panel account", usage = "/ ") +public class Command_panel extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.ptero.isEnabled()) + { + msg("Pterodactyl integration is currently disabled.", ChatColor.RED); + return true; + } + + PlayerData playerData = getData(playerSender); + + if (playerData.getDiscordID() == null) + { + msg("You must have a linked Discord account.", ChatColor.RED); + return true; + } + + if (args.length == 0) + { + return false; + } + + if (args[0].equalsIgnoreCase("create")) + { + msg("Creating your Pterodactyl account...", ChatColor.GREEN); + Admin admin = getAdmin(playerSender); + + if (admin.getPteroID() != null) + { + msg("You already have a Pterodactyl account.", ChatColor.RED); + return true; + } + + String username = sender.getName(); + String password = FUtil.randomString(30); + + String id = plugin.ptero.createAccount(username, password); + if (Strings.isNullOrEmpty(id)) + { + msg("Failed to create your Pterodactyl account.", ChatColor.RED); + return true; + } + + plugin.ptero.addAccountToServer(id); + admin.setPteroID(id); + plugin.al.save(admin); + plugin.al.updateTables(); + + plugin.dc.sendPteroInfo(playerData, username, password); + msg("Successfully created your Pterodactyl account. Check your DMs from " + plugin.dc.formatBotTag() + " on Discord to get your credentials.", ChatColor.GREEN); + return true; + } + else if (args[0].equalsIgnoreCase("delete")) + { + msg("Deleting your Pterodactyl account...", ChatColor.GREEN); + Admin admin = getAdmin(playerSender); + + if (admin.getPteroID() == null) + { + msg("You do not have a Pterodactyl account.", ChatColor.RED); + return true; + } + + plugin.ptero.deleteAccount(admin.getPteroID()); + admin.setPteroID(null); + plugin.al.save(admin); + plugin.al.updateTables(); + + msg("Successfully deleted your Pterodactyl account.", ChatColor.GREEN); + return true; + } + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isSeniorAdmin(sender)) + { + return Arrays.asList("create", "delete"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_permissions.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_permissions.java new file mode 100644 index 00000000..0adc14f3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_permissions.java @@ -0,0 +1,51 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Check your permissions", usage = "/ [prefix | reload]") +public class Command_permissions extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0 && args[0].equalsIgnoreCase("reload") && plugin.al.isAdmin(sender)) + { + plugin.permissions.load(); + plugin.pem.loadPermissionNodes(); + plugin.pem.updatePlayers(); + msg("Reloaded permissions"); + } + else + { + String prefix = ""; + if (args.length > 0) + { + prefix = args[0]; + } + checkPlayer(); + List permissions = new ArrayList<>(); + for (PermissionAttachmentInfo attachmentInfo : playerSender.getEffectivePermissions()) + { + if (attachmentInfo.getValue()) + { + String permission = attachmentInfo.getPermission(); + if (!prefix.isEmpty() && !permission.startsWith(prefix)) + { + continue; + } + permissions.add(permission); + } + } + msg(String.join(", ", permissions)); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverification.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverification.java new file mode 100644 index 00000000..0bfe476a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playerverification.java @@ -0,0 +1,158 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Manage your verification", usage = "/ | status | genbackupcodes>", aliases = "playerverify,pv") +public class Command_playerverification extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + PlayerData target = plugin.pl.getData(playerSender); + boolean verificationEnabled = ConfigEntry.DISCORD_VERIFICATION.getBoolean(); + List ips = new ArrayList<>(target.getIps()); + + if (verificationEnabled) + { + if (args.length == 1) + { + if (args[0].equalsIgnoreCase("clearips")) + { + int cleared = 0; + for (String ip : ips) + { + if (!ip.equals(FUtil.getIp(playerSender))) + { + target.removeIp(ip); + cleared++; + } + } + + msg("Cleared all IP's except your current IP \"" + FUtil.getIp(playerSender) + "\""); + msg("Cleared " + cleared + " IP's."); + plugin.pl.save(target); + plugin.pl.syncIps(target); + return true; + } + else if (args[0].equalsIgnoreCase("clearip")) + { + return false; + } + } + + if (args.length < 1) + { + return false; + } + + PlayerData data = plugin.pl.getData(playerSender); + + switch (args[0].toLowerCase()) + { + case "enable": + { + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + else if (data.hasVerification()) + { + msg("Discord verification is already enabled for you.", ChatColor.RED); + return true; + } + else if (data.getDiscordID() == null) + { + msg("Please link a discord account with /linkdiscord.", ChatColor.RED); + return true; + } + + data.setVerification(true); + plugin.pl.save(data); + msg("Re-enabled Discord verification.", ChatColor.GREEN); + return true; + } + + case "disable": + { + if (!data.hasVerification()) + { + msg("Discord verification is already disabled for you.", ChatColor.RED); + return true; + } + + data.setVerification(false); + plugin.pl.save(data); + msg("Disabled Discord verification.", ChatColor.GREEN); + return true; + } + + case "status": + { + boolean enabled = target.hasVerification(); + boolean specified = target.getDiscordID() != null; + msg(ChatColor.GRAY + "Discord Verification Enabled: " + (enabled ? ChatColor.GREEN + "true" : ChatColor.RED + "false")); + msg(ChatColor.GRAY + "Discord ID: " + (specified ? ChatColor.GREEN + target.getDiscordID() : ChatColor.RED + "not set")); + msg(ChatColor.GRAY + "Backup Codes: " + data.getBackupCodes().size() + "/" + "10"); + return true; + } + + case "genbackupcodes": + { + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + else if (!data.hasVerification()) + { + msg("Discord verification is not enabled for you.", ChatColor.RED); + return true; + } + + boolean generated = plugin.dc.sendBackupCodes(data); + + if (generated) + { + msg("Your backup codes have been sent to your discord account. They can be re-generated at anytime.", ChatColor.GREEN); + } + else + { + msg("Failed to generate backup codes, please contact a developer.", ChatColor.RED); + } + return true; + } + default: + return false; + } + } + else + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + } + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return Arrays.asList("enable", "disable", "status", "clearips", "genbackupcodes"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playtime.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playtime.java new file mode 100644 index 00000000..c921275d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_playtime.java @@ -0,0 +1,79 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import me.totalfreedom.totalfreedommod.admin.ActivityLogEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Gets your playtime statistics.", usage = "/") +public class Command_playtime extends FreedomCommand +{ + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + ActivityLogEntry entry = plugin.acl.getActivityLog(playerSender); + int seconds = entry.getTotalSecondsPlayed(); + int minutes = 0; + int hours = 0; + while (seconds >= 60) + { + seconds -= 60; + minutes += 1; + } + while (minutes >= 60) + { + minutes -= 60; + hours += 1; + } + if (entry.getTimestamps().size() == 0) + { + entry.addLogin(); + } + String lastLoginString = entry.getTimestamps().get(entry.getTimestamps().size() - 1); + Date currentTime = Date.from(Instant.now()); + lastLoginString = lastLoginString.replace("Login: ", ""); + Date lastLogin = FUtil.stringToDate(lastLoginString); + + long duration = currentTime.getTime() - lastLogin.getTime(); + long cseconds = duration / 1000 % 60; + long cminutes = duration / (60 * 1000) % 60; + long chours = duration / (60 * 60 * 1000); + StringBuilder sb = new StringBuilder() + .append("Playtime - ") + .append(sender.getName()) + .append("\n") + .append("Current Session: ") + .append(chours) + .append(" hours, ") + .append(cminutes) + .append(" minutes, and ") + .append(cseconds) + .append(" seconds") + .append("\n") + .append("Overall: ") + .append(hours) + .append(" hours, ") + .append(minutes) + .append(" minutes, and ") + .append(seconds) + .append(" seconds") + .append("\n"); + List durations = entry.getDurations(); + if (durations.size() >= 3) + { + sb.append("Recent Sessions:"); + for (int i = 0; i < 3; i++) + { + sb.append("\n" + " - ").append(durations.get((durations.size() - 1) - i)); + } + } + msg(sb.toString()); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plotworld.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plotworld.java new file mode 100644 index 00000000..e764e94f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plotworld.java @@ -0,0 +1,28 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Go to the PlotWorld.", usage = "/", aliases = "pw") +public class Command_plotworld extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + World plotworld = server.getWorld("plotworld"); + if (plotworld != null) + { + PaperLib.teleportAsync(playerSender, plotworld.getSpawnLocation()); + } + else + { + msg("\"plotworld\" doesn't exist."); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plugincontrol.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plugincontrol.java new file mode 100644 index 00000000..1dbc5579 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_plugincontrol.java @@ -0,0 +1,193 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Enable, disable, or reload a specified plugin, as well as list all plugins on the server.", usage = "/ < > | list>", aliases = "plc") +public class Command_plugincontrol extends FreedomCommand +{ + + private final List UNTOUCHABLE_PLUGINS = Arrays.asList(plugin.getName()); + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0 || args.length > 2) + { + return false; + } + + final PluginManager pm = server.getPluginManager(); + + if (args.length == 1) + { + if (args[0].equalsIgnoreCase("list")) + { + for (Plugin serverPlugin : pm.getPlugins()) + { + final String version = serverPlugin.getDescription().getVersion(); + msg(ChatColor.GRAY + "- " + (serverPlugin.isEnabled() ? ChatColor.GREEN : ChatColor.RED) + serverPlugin.getName() + + ChatColor.GOLD + (!version.isEmpty() ? " v" + version : "") + " by " + + StringUtils.join(serverPlugin.getDescription().getAuthors(), ", ")); + } + + return true; + } + + return false; + } + + if (args[0].equals("enable")) + { + final Plugin target = getPlugin(args[1]); + if (target == null) + { + msg("Plugin not found!"); + return true; + } + + if (target.isEnabled()) + { + msg("Plugin is already enabled."); + return true; + } + + pm.enablePlugin(target); + + if (!pm.isPluginEnabled(target)) + { + msg("Error enabling plugin " + target.getName()); + return true; + } + + msg(target.getName() + " is now enabled."); + return true; + } + + if (args[0].equals("disable")) + { + final Plugin target = getPlugin(args[1]); + if (target == null) + { + msg("Plugin not found!"); + return true; + } + + if (!target.isEnabled()) + { + msg("Plugin is already disabled."); + return true; + } + + if (UNTOUCHABLE_PLUGINS.contains(target.getName())) + { + msg("You cannot disable " + target.getName()); + return true; + } + + pm.disablePlugin(target); + + if (pm.isPluginEnabled(target)) + { + msg("Error disabling plugin " + target.getName()); + return true; + } + + msg(target.getName() + " is now disabled."); + return true; + } + + if (args[0].equals("reload")) + { + final Plugin target = getPlugin(args[1]); + if (target == null) + { + msg("Plugin not found!"); + return true; + } + + if (UNTOUCHABLE_PLUGINS.contains(target.getName())) + { + msg("You cannot reload " + target.getName()); + return true; + } + + pm.disablePlugin(target); + pm.enablePlugin(target); + msg(target.getName() + " reloaded."); + return true; + } + + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + if (args.length == 1) + { + return Arrays.asList("enable", "disable", "reload", "list"); + } + else if (args.length == 2) + { + if (!args[0].equals("list")) + { + return getAllPluginNames(); + } + } + + return Collections.emptyList(); + } + + public List getAllPluginNames() + { + List names = new ArrayList<>(); + for (Plugin plugin : server.getPluginManager().getPlugins()) + { + if (!UNTOUCHABLE_PLUGINS.contains(plugin.getName())) + { + names.add(plugin.getName()); + } + } + names.remove(plugin.getName()); + return names; + } + + public Plugin getPlugin(String name) + { + for (Plugin serverPlugin : server.getPluginManager().getPlugins()) + { + if (serverPlugin.getName().equalsIgnoreCase(name)) + { + return serverPlugin; + } + } + + if (name.length() >= 3) + { + for (Plugin serverPlugin : server.getPluginManager().getPlugins()) + { + if (serverPlugin.getName().toLowerCase().contains(name.toLowerCase())) + { + return serverPlugin; + } + } + } + return null; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potion.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potion.java new file mode 100644 index 00000000..0f30f24d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potion.java @@ -0,0 +1,243 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters( + description = "Manipulate your potion effects. Duration is measured in server ticks (~20 ticks per second).", + usage = "/ [target name]>", + aliases = "effect") +public class Command_potion extends FreedomCommand +{ + + @SuppressWarnings("deprecation") + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + switch (args.length) + { + case 1: + if (args[0].equalsIgnoreCase("list")) + { + List potionEffectTypeNames = new ArrayList<>(); + for (PotionEffectType potion_effect_type : PotionEffectType.values()) + { + if (potion_effect_type != null) + { + potionEffectTypeNames.add(potion_effect_type.getName()); + } + } + msg("Potion effect types: " + StringUtils.join(potionEffectTypeNames, ", "), ChatColor.AQUA); + } + else if (args[0].equalsIgnoreCase("clearall")) + { + if (!(plugin.al.isAdmin(sender) || senderIsConsole)) + { + noPerms(); + return true; + } + + FUtil.adminAction(sender.getName(), "Cleared all potion effects from all players", true); + for (Player target : server.getOnlinePlayers()) + { + for (PotionEffect potion_effect : target.getActivePotionEffects()) + { + target.removePotionEffect(potion_effect.getType()); + } + } + } + + case 2: + if (args[0].equalsIgnoreCase("clear")) + { + Player target = playerSender; + if (args.length == 2) + { + if (!plugin.al.isAdmin(sender) && !args[1].equalsIgnoreCase(sender.getName())) + { + msg(ChatColor.RED + "Only admins can clear potion effects from other players."); + return true; + } + target = getPlayer(args[1], true); + } + else + { + if (senderIsConsole) + { + msg("You must specify a target player when using this command from the console."); + return true; + } + } + + if (target == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND, ChatColor.RED); + return true; + } + + for (PotionEffect potion_effect : target.getActivePotionEffects()) + { + target.removePotionEffect(potion_effect.getType()); + } + + msg("Cleared all active potion effects " + (!target.equals(playerSender) ? "from player " + target.getName() + "." : "from yourself."), ChatColor.AQUA); + } + break; + + case 4: + case 5: + if (args[0].equalsIgnoreCase("add")) + { + Player target = playerSender; + + if (args.length == 5) + { + if (!plugin.al.isAdmin(sender) && !args[4].equalsIgnoreCase(sender.getName())) + { + msg("Only admins can apply potion effects to other players.", ChatColor.RED); + return true; + } + + target = getPlayer(args[4]); + + if (target == null || plugin.al.isVanished(target.getName()) && !plugin.al.isAdmin(sender)) + { + msg(PLAYER_NOT_FOUND); + return true; + } + } + else + { + if (senderIsConsole) + { + msg("You must specify a target player when using this command from the console."); + return true; + } + } + + PotionEffectType potion_effect_type = PotionEffectType.getByName(args[1]); + if (potion_effect_type == null) + { + msg("Invalid potion effect type.", ChatColor.AQUA); + return true; + } + + int duration; + try + { + duration = Integer.parseInt(args[2]); + duration = Math.min(duration, 100000); + } + catch (NumberFormatException ex) + { + msg("Invalid potion duration.", ChatColor.RED); + return true; + } + + int amplifier; + try + { + amplifier = Integer.parseInt(args[3]); + amplifier = Math.min(amplifier, 100000); + } + catch (NumberFormatException ex) + { + msg("Invalid potion amplifier.", ChatColor.RED); + return true; + } + + PotionEffect new_effect = potion_effect_type.createEffect(duration, amplifier); + target.addPotionEffect(new_effect, true); + msg( + "Added potion effect: " + new_effect.getType().getName() + + ", Duration: " + new_effect.getDuration() + + ", Amplifier: " + new_effect.getAmplifier() + + (!target.equals(playerSender) ? " to player " + target.getName() + "." : " to yourself."), ChatColor.AQUA); + } + break; + default: + return false; + } + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + switch (args.length) + { + case 1: + List arguments = new ArrayList<>(Arrays.asList("list", "clear", "add")); + if (plugin.al.isAdmin(sender)) + { + arguments.add("clearall"); + } + return arguments; + + case 2: + if (args[0].equals("clear")) + { + if (plugin.al.isAdmin(sender)) + { + return FUtil.getPlayerList(); + } + } + else if (args[0].equals("add")) + { + return getAllPotionTypes(); + } + break; + + case 3: + if (args[0].equals("add")) + { + return Collections.singletonList(""); + } + break; + + case 4: + if (args[0].equals("add")) + { + return Collections.singletonList(""); + } + break; + + case 5: + if (plugin.al.isAdmin(sender)) + { + if (args[0].equals("add")) + { + return FUtil.getPlayerList(); + } + } + break; + } + + return Collections.emptyList(); + } + + public List getAllPotionTypes() + { + List types = new ArrayList<>(); + for (PotionEffectType potionEffectType : PotionEffectType.values()) + { + if (potionEffectType != null) + { + types.add(potionEffectType.getName()); + } + } + return types; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potionspy.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potionspy.java new file mode 100644 index 00000000..277b6ac3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_potionspy.java @@ -0,0 +1,222 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.entity.ThrownPotion; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Allows admins to see potions that are thrown.", usage = "/ | history [player] ", aliases = "potspy") +public class Command_potionspy extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Admin admin = plugin.al.getAdmin(playerSender); + + if (args.length <= 0) + { + setPotionSpyState(admin, !admin.getPotionSpy()); + return true; + } + else + { + String titleText = "&8&m------------------&r &ePotionSpy &8&m------------------&r"; + String validPageText = "Please specify a valid page number between 1 and %s."; + String noPlayerRecord = "That player has not thrown any potions yet."; + String splashedText = "&r%s splashed a potion at &eX: %s Y: %s Z: %s&r\nin the world '&e%s&r' about &e%s &rago%s."; + String bottomText = "&8&m--------------------&r &e%s / %s &8&m--------------------&r"; + switch (args[0].toLowerCase()) + { + case "enable": + case "on": + setPotionSpyState(admin, true); + break; + + case "disable": + case "off": + setPotionSpyState(admin, false); + break; + + case "history": + if (args.length == 3) + { + Player player = Bukkit.getPlayer(args[1]); + if (player == null) + { + msg(sender, "Please specify a valid player name."); + return true; + } + + List> thrownPotions = new ArrayList<>(plugin.mo.getPlayerThrownPotions(player)); // Make a copy of the list to avoid modifying the original. + + List potionThrowNotifications = new ArrayList<>(); + int lastPage = (int)Math.ceil(thrownPotions.size() / 5.0); + + if (thrownPotions.isEmpty()) + { + msg(sender, noPlayerRecord); + return true; + } + if (!NumberUtils.isNumber(args[2])) + { + msg(sender, String.format(validPageText, lastPage)); + return true; + } + + Collections.reverse(thrownPotions); + int pageIndex = Integer.parseInt(args[2]); + + for (Map.Entry potionEntry : thrownPotions) + { + ThrownPotion potion = potionEntry.getKey(); + boolean trollPotions = plugin.mo.isTrollPotion(potion); + + potionThrowNotifications.add(ChatColor.translateAlternateColorCodes('&', String.format(splashedText, player.getName(), potion.getLocation().getBlockX(), + potion.getLocation().getBlockY(), potion.getLocation().getBlockZ(), potion.getWorld().getName(), getUnixTimeDifference(potionEntry.getValue(), System.currentTimeMillis()), trollPotions ? " &c(most likely troll potion/potions)" : ""))); + } + + List page = FUtil.getPageFromList(potionThrowNotifications, 5, pageIndex - 1); + if (!page.isEmpty()) + { + msg(sender, ChatColor.translateAlternateColorCodes('&', titleText)); + for (String potionThrowNotification : page) + { + msg(sender, potionThrowNotification); + } + } + else + { + msg(sender, String.format(validPageText, lastPage)); + return true; + } + + msg(sender, ChatColor.translateAlternateColorCodes('&', String.format(bottomText, pageIndex, lastPage))); + } + else if (args.length == 2) + { + List> thrownPotions = new ArrayList<>(); + thrownPotions.addAll(plugin.mo.getAllThrownPotions()); // Make a copy of the list to avoid modifying the original. + + List potionThrowNotifications = new ArrayList<>(); + int lastPage = (int)Math.ceil(thrownPotions.size() / 5.0); + + if (thrownPotions.isEmpty()) + { + if (Bukkit.getPlayer(args[1]) != null) + { + msg(sender, noPlayerRecord); + } + else + { + msg(sender, "No potions have been thrown yet."); + } + return true; + } + if (!NumberUtils.isNumber(args[1])) + { + msg(sender, String.format(validPageText, lastPage)); + return true; + } + + Collections.reverse(thrownPotions); + int pageIndex = Integer.parseInt(args[1]); + + for (Map.Entry potionEntry : thrownPotions) + { + ThrownPotion potion = potionEntry.getKey(); + Player player = (Player)potion.getShooter(); + boolean trollPotions = plugin.mo.isTrollPotion(potion); + + if (player != null) + { + potionThrowNotifications.add(ChatColor.translateAlternateColorCodes('&', String.format(splashedText, player.getName(), potion.getLocation().getBlockX(), + potion.getLocation().getBlockY(), potion.getLocation().getBlockZ(), potion.getWorld().getName(), getUnixTimeDifference(potionEntry.getValue(), System.currentTimeMillis()), trollPotions ? " &c(most likely troll potion/potions)" : ""))); + } + } + + List page = FUtil.getPageFromList(potionThrowNotifications, 5, pageIndex - 1); + if (!page.isEmpty()) + { + msg(sender, ChatColor.translateAlternateColorCodes('&', titleText)); + for (String potionThrowNotification : page) + { + msg(sender, potionThrowNotification); + } + } + else + { + msg(sender, String.format(validPageText, lastPage)); + return true; + } + + msg(sender, ChatColor.translateAlternateColorCodes('&', String.format(bottomText, pageIndex, lastPage))); + } + else + { + return false; + } + break; + default: + return false; + } + } + return true; + } + + private void setPotionSpyState(Admin admin, boolean state) + { + admin.setPotionSpy(state); + plugin.al.save(admin); + plugin.al.updateTables(); + msg("PotionSpy is now " + (admin.getPotionSpy() ? "enabled." : "disabled.")); + } + + /** + * Get the unix time difference in string format (1h, 30m, 15s). + * + * @param past The unix time at the start. + * @param now The current unix time. + * @return A string that displays the time difference between the two unix time values. + */ + private String getUnixTimeDifference(long past, long now) + { + long unix = now - past; + long seconds = Math.round(unix / 1000.0); + if (seconds < 60) + { + return seconds + "s"; + } + else + { + long minutes = Math.round(seconds / 60.0); + if (minutes < 60) + { + return minutes + "m"; + } + else + { + long hours = Math.round(minutes / 60.0); + if (hours < 24) + { + return hours + "h"; + } + else + { + return Math.round(hours / 24.0) + "d"; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java new file mode 100644 index 00000000..efee00ac --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_purgeall.java @@ -0,0 +1,85 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Purge current mutes, command blocks, orbits, freezes, potion effects, cages, and entities.", usage = "/") +public class Command_purgeall extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + FUtil.adminAction(sender.getName(), "Purging all player data", true); + + // Purge entities + for (World world : Bukkit.getWorlds()) + { + for (Entity entity : world.getEntities()) + { + if (!(entity instanceof Player)) + { + entity.remove(); + } + } + } + + for (Player player : server.getOnlinePlayers()) + { + FPlayer fPlayer = plugin.pl.getPlayer(player); + + // Unmute all players + if (fPlayer.isMuted()) + { + fPlayer.setMuted(false); + } + + // Unblock all commands + if (fPlayer.allCommandsBlocked()) + { + fPlayer.setCommandsBlocked(false); + } + + // Stop orbiting + if (fPlayer.isOrbiting()) + { + fPlayer.stopOrbiting(); + } + + // Unfreeze + if (fPlayer.getFreezeData().isFrozen()) + { + fPlayer.getFreezeData().setFrozen(false); + } + + // Purge potion effects + for (PotionEffect potion_effect : player.getActivePotionEffects()) + { + player.removePotionEffect(potion_effect.getType()); + } + + // Uncage + if (fPlayer.getCageData().isCaged()) + { + fPlayer.getCageData().setCaged(false); + } + } + + // Unfreeze all players + plugin.fm.setGlobalFreeze(false); + + // Remove all mobs + plugin.ew.purgeMobs(null); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rank.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rank.java new file mode 100644 index 00000000..d84846f3 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rank.java @@ -0,0 +1,73 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Show the rank of the sender or a specified user.", usage = "/ [player]") +public class Command_rank extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (senderIsConsole && args.length == 0) + { + for (Player player : server.getOnlinePlayers()) + { + msg(message(player)); + } + return true; + } + + if (args.length == 0) + { + msg(message(playerSender)); + return true; + } + + if (args.length > 1) + { + return false; + } + + final Player player = getPlayer(args[0], true); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg(message(player)); + + return true; + } + + public String message(Player player) + { + Displayable display = plugin.rm.getDisplay(player); + Rank rank = plugin.rm.getRank(player); + + StringBuilder sb = new StringBuilder(); + sb.append(ChatColor.AQUA) + .append(player.getName()) + .append(" is ") + .append(display.getColoredLoginMessage()); + + if (rank != display) + { + sb.append(ChatColor.AQUA) + .append(" (") + .append(rank.getColoredName()) + .append(ChatColor.AQUA) + .append(')'); + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rawsay.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rawsay.java new file mode 100644 index 00000000..0610c030 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rawsay.java @@ -0,0 +1,25 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Broadcasts the given message. Supports colors.", usage = "/ ") +public class Command_rawsay extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0) + { + FUtil.bcastMsg(FUtil.colorize(StringUtils.join(args, " "))); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_reactionbar.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_reactionbar.java new file mode 100644 index 00000000..6aa9ab96 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_reactionbar.java @@ -0,0 +1,26 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Forcefully start a reaction", usage = "/") +public class Command_reactionbar extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!FUtil.isDeveloper(playerSender)) + { + return noPerms(); + } + + plugin.sh.forceStartReaction(); + msg("Started a reaction."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_releaseparrots.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_releaseparrots.java new file mode 100644 index 00000000..16a25106 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_releaseparrots.java @@ -0,0 +1,41 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Release parrots from your shoulders.", usage = "/", aliases = "removeparrots") +public class Command_releaseparrots extends FreedomCommand +{ + + @SuppressWarnings("deprecation") + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Entity leftShoulderEntity = playerSender.getShoulderEntityLeft(); + Entity rightShoulderEntity = playerSender.getShoulderEntityRight(); + + if (rightShoulderEntity == null && leftShoulderEntity == null) + { + msg("No parrots were detected on either of your shoulders."); + return true; + } + + if (leftShoulderEntity != null && leftShoulderEntity.getType().equals(EntityType.PARROT)) + { + playerSender.setShoulderEntityLeft(null); + msg("Removed the parrot on your left shoulder."); + } + + if (rightShoulderEntity != null && rightShoulderEntity.getType().equals(EntityType.PARROT)) + { + playerSender.setShoulderEntityRight(null); + msg("Removed the parrot on your right shoulder."); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java new file mode 100644 index 00000000..086bac6c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java @@ -0,0 +1,62 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME, blockHostConsole = true) +@CommandParameters(description = "Report a player for all admins to see.", usage = "/ ") +public class Command_report extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 2) + { + return false; + } + + Player player = getPlayer(args[0], true); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (sender instanceof Player) + { + if (player.equals(playerSender)) + { + msg(ChatColor.RED + "Please, don't try to report yourself."); + return true; + } + } + + if (plugin.al.isAdmin(player)) + { + msg(ChatColor.RED + "You can not report admins."); + return true; + } + + String report = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + plugin.cm.reportAction(playerSender, player, report); + + boolean logged = false; + + if (plugin.dc.enabled) + { + logged = plugin.dc.sendReport(playerSender, player, report); + } + + msg(ChatColor.GREEN + "Thank you, your report has been successfully logged." + + (logged ? ChatColor.RED + "\nNote: This report has been logged to a discord channel, as with any report system, spamming reports can lead to you getting banned." : "")); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java new file mode 100644 index 00000000..105acf8d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_restart.java @@ -0,0 +1,77 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Restart the server", usage = "/ [reason]") +public class Command_restart extends FreedomCommand +{ + private static final Map RESTART_CONFIRM = new HashMap<>(); + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (!plugin.ptero.isEnabled()) + { + msg("Pterodactyl integration is currently disabled.", ChatColor.RED); + return true; + } + + String reason = "Server is restarting!"; + + if (args.length != 0) + { + reason = StringUtils.join(args, " "); + } + + if (sender.getName().equals("CONSOLE")) + { + restart(reason); + return true; + } + else if (RESTART_CONFIRM.containsKey(sender)) + { + restart(RESTART_CONFIRM.get(sender)); + return true; + } + + msg("Warning: You're about to restart the server. Type /restart again to confirm you want to do this."); + + RESTART_CONFIRM.put(sender, reason); + new BukkitRunnable() + { + @Override + public void run() + { + if (RESTART_CONFIRM.containsKey(sender)) + { + RESTART_CONFIRM.remove(sender); + msg("Stop request expired."); + } + } + }.runTaskLater(plugin, 15 * 20); + return true; + } + + public void restart(String reason) + { + FUtil.bcastMsg("Server is restarting!", ChatColor.LIGHT_PURPLE); + + for (Player player : server.getOnlinePlayers()) + { + player.kickPlayer(ChatColor.LIGHT_PURPLE + reason); + } + + RESTART_CONFIRM.remove(sender); + plugin.ptero.restartServer(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ride.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ride.java new file mode 100644 index 00000000..30a1883b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ride.java @@ -0,0 +1,158 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Ride on the top of the specified player.", usage = "/ >") +public class Command_ride extends FreedomCommand +{ + + private final Map RIDE_REQUESTS = new HashMap<>(); // requested, requester + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + final FPlayer fPlayer = plugin.pl.getPlayer(playerSender); + if (fPlayer.getCageData().isCaged()) + { + msg("You cannot use this command while caged."); + return true; + } + + if (args.length < 1) + { + return false; + } + + if (args[0].equalsIgnoreCase("accept") || args[0].equalsIgnoreCase("yes")) + { + if (!RIDE_REQUESTS.containsKey(playerSender)) + { + msg("You don't have any pending requests."); + return true; + } + + Player requester = RIDE_REQUESTS.get(playerSender); + if (requester == null) + { + msg("The player who sent the request is no longer online."); + RIDE_REQUESTS.remove(playerSender); + return true; + } + + msg("Request accepted."); + msg(requester, "Your request has been accepted."); + + if (requester.getWorld() != playerSender.getWorld()) + { + PaperLib.teleportAsync(requester, playerSender.getLocation()); + } + + RIDE_REQUESTS.remove(playerSender); + playerSender.addPassenger(requester); + return true; + } + + if (args[0].equalsIgnoreCase("deny") || args[0].equalsIgnoreCase("no")) + { + if (!RIDE_REQUESTS.containsKey(playerSender)) + { + msg("You don't have any pending requests."); + return true; + } + Player requester = RIDE_REQUESTS.get(playerSender); + if (requester == null) + { + msg("The player who sent the request is no longer online."); + RIDE_REQUESTS.remove(playerSender); + return true; + } + msg("Request denied."); + RIDE_REQUESTS.remove(playerSender); + msg(requester, "Your request has been denied."); + return true; + } + + if (args.length >= 2) + { + if (args[0].equalsIgnoreCase("mode")) + { + if (args[1].equalsIgnoreCase("normal") || args[1].equalsIgnoreCase("off") || args[1].equalsIgnoreCase("ask")) + { + PlayerData playerDataSender = plugin.pl.getData(playerSender); + playerDataSender.setRideMode(args[1].toLowerCase()); + plugin.pl.save(playerDataSender); + msg("Ride mode is now set to " + args[1].toLowerCase() + "."); + return true; + } + } + } + + final Player player = getPlayer(args[0], true); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + final PlayerData playerData = plugin.pl.getData(player); + + if (player == playerSender) + { + msg("You can't ride yourself. smh.", ChatColor.RED); + return true; + } + + if (playerData.getRideMode().equals("off") && !isAdmin(sender)) + { + msg("That player cannot be ridden.", ChatColor.RED); + return true; + } + + if (playerData.getRideMode().equals("ask") && !FUtil.isExecutive(playerSender.getName())) + { + msg("Sent a request to the player.", ChatColor.GREEN); + msg(player, sender.getName() + " has requested to ride you.", ChatColor.AQUA); + msg(player, "Type " + ChatColor.GREEN + "/ride accept" + ChatColor.AQUA + " to allow the player to ride you.", ChatColor.AQUA); + msg(player, "Type " + ChatColor.RED + "/ride deny" + ChatColor.AQUA + " to deny the player permission.", ChatColor.AQUA); + msg(player, "Request will expire in 30 seconds.", ChatColor.AQUA); + RIDE_REQUESTS.put(player, playerSender); + + new BukkitRunnable() + { + public void run() + { + if (!RIDE_REQUESTS.containsKey(player)) + { + return; + } + + RIDE_REQUESTS.remove(player); + msg(playerSender, "It has been 30 seconds and " + player.getName() + " has not accepted your request.", ChatColor.RED); + msg(player, "Request expired.", ChatColor.RED); + } + }.runTaskLater(plugin, 20 * 30); + return true; + } + + if (player.getWorld() != playerSender.getWorld()) + { + PaperLib.teleportAsync(playerSender, player.getLocation()); + } + + player.addPassenger(playerSender); + msg(player, playerSender.getName() + " is now riding you, run /eject to eject them."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rideablepearl.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rideablepearl.java new file mode 100644 index 00000000..b4e02d6f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rideablepearl.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a rideable ender pearl", usage = "/") +public class Command_rideablepearl extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.RIDEABLE_PEARL)) + { + playerSender.getInventory().addItem(plugin.sh.getRideablePearl()); + msg("You have been given a Rideable Ender Pearl", ChatColor.GREEN); + } + else + { + msg("You do not own a Rideable Ender Pearl! Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ro.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ro.java new file mode 100644 index 00000000..e51dc061 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_ro.java @@ -0,0 +1,177 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Remove all blocks of a certain type in the radius of certain players.", usage = "/ [radius (default=50)] [player]") +public class Command_ro extends FreedomCommand +{ + + public static int replaceBlocks(Location center, Material fromMaterial, Material toMaterial, int radius) + { + int affected = 0; + + Block centerBlock = center.getBlock(); + for (int xOffset = -radius; xOffset <= radius; xOffset++) + { + for (int yOffset = -radius; yOffset <= radius; yOffset++) + { + for (int zOffset = -radius; zOffset <= radius; zOffset++) + { + Block block = centerBlock.getRelative(xOffset, yOffset, zOffset); + BlockData data = block.getBlockData(); + if (block.getLocation().distanceSquared(center) < (radius * radius)) + { + if (fromMaterial.equals(Material.WATER) && data instanceof Waterlogged) + { + Waterlogged waterloggedData = (Waterlogged)data; + if (waterloggedData.isWaterlogged()) + { + waterloggedData.setWaterlogged(false); + block.setBlockData(waterloggedData); + affected++; + continue; + } + block.setType(toMaterial); + affected++; + } + else if (block.getType().equals(fromMaterial)) + { + block.setType(toMaterial); + affected++; + } + } + } + } + } + return affected; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1 || args.length > 3) + { + return false; + } + + final List materials = new ArrayList<>(); + String names = null; + if (args[0].equalsIgnoreCase("shulker_boxes") || args[0].equalsIgnoreCase("shulkers")) + { + materials.addAll(Groups.SHULKER_BOXES); + names = "shulker boxes"; + } + else if (args[0].equalsIgnoreCase("banners") || args[0].equalsIgnoreCase("banner")) + { + materials.addAll(Groups.BANNERS); + names = "banners"; + } + else + { + for (String materialName : StringUtils.split(args[0], ",")) + { + Material fromMaterial = Material.matchMaterial(materialName); + + if (fromMaterial == null || fromMaterial == Material.AIR || !fromMaterial.isBlock()) + { + msg("Invalid material: " + materialName, ChatColor.RED); + return true; + } + + materials.add(fromMaterial); + } + } + + int radius = 50; + if (args.length >= 2) + { + try + { + radius = Math.max(1, Math.min(50, Integer.parseInt(args[1]))); + } + catch (NumberFormatException ex) + { + msg("Invalid radius: " + args[1], ChatColor.RED); + return true; + } + } + + final Player targetPlayer; + if (args.length == 3) + { + targetPlayer = getPlayer(args[2]); + if (targetPlayer == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + } + else + { + targetPlayer = null; + } + + if (names == null) + { + names = StringUtils.join(materials, ", "); + } + + World adminWorld = null; + try + { + adminWorld = plugin.wm.adminworld.getWorld(); + } + catch (Exception ignored) + { + } + + int affected = 0; + if (targetPlayer == null) + { + FUtil.adminAction(sender.getName(), "Removing all " + names + " within " + radius + " blocks of all players... Brace for lag!", false); + + for (final Player player : server.getOnlinePlayers()) + { + if (player.getWorld() == adminWorld) + { + continue; + } + + for (final Material material : materials) + { + affected += replaceBlocks(player.getLocation(), material, Material.AIR, radius); + } + } + } + else + { + if (targetPlayer.getWorld() != adminWorld) + { + FUtil.adminAction(sender.getName(), "Removing all " + names + " within " + radius + " blocks of " + targetPlayer.getName(), false); + for (Material material : materials) + { + affected += replaceBlocks(targetPlayer.getLocation(), material, Material.AIR, radius); + } + } + } + + FUtil.adminAction(sender.getName(), "Remove complete! " + affected + " blocks removed.", false); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rock.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rock.java new file mode 100644 index 00000000..fed36ec4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_rock.java @@ -0,0 +1,41 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "You have thrown a rock, but you have also summoned a meteor!", usage = "/") +public class Command_rock extends FreedomCommand +{ + + public static final String ROCK_LYRICS = ChatColor.BLUE + "You have thrown a rock, but you have also summoned a meteor!"; + + @Override + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + final ItemStack heldItem = new ItemStack(Material.STONE); + final ItemMeta heldItemMeta = heldItem.getItemMeta(); + assert heldItemMeta != null; + heldItemMeta.setDisplayName(ChatColor.BLUE + "Rock"); + heldItem.setItemMeta(heldItemMeta); + + for (final Player player : this.server.getOnlinePlayers()) + { + final int firstEmpty = player.getInventory().firstEmpty(); + if (firstEmpty >= 0) + { + player.getInventory().setItem(firstEmpty, heldItem); + } + } + + FUtil.bcastMsg(ROCK_LYRICS); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_saconfig.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_saconfig.java new file mode 100644 index 00000000..0a1c06dc --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_saconfig.java @@ -0,0 +1,330 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "List, add, remove, or set the rank of admins, clean or reload the admin list, or view admin information.", usage = "/ | >", aliases = "slconfig") +public class Command_saconfig extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + switch (args[0]) + { + case "list": + { + msg("Admins: " + StringUtils.join(plugin.al.getAdminNames(), ", "), ChatColor.GOLD); + return true; + } + + case "clean": + { + checkConsole(); + checkRank(Rank.SENIOR_ADMIN); + + FUtil.adminAction(sender.getName(), "Cleaning the admin list", true); + plugin.al.deactivateOldEntries(true); + msg("Admins: " + StringUtils.join(plugin.al.getAdminNames(), ", "), ChatColor.GOLD); + + return true; + } + + case "reload": + { + checkRank(Rank.SENIOR_ADMIN); + + FUtil.adminAction(sender.getName(), "Reloading the admin list", true); + plugin.al.load(); + msg("Admin list reloaded!"); + return true; + } + + case "setrank": + { + checkConsole(); + checkRank(Rank.SENIOR_ADMIN); + + if (args.length < 3) + { + return false; + } + + Rank rank = Rank.findRank(args[2]); + if (rank == null) + { + msg("Unknown rank: " + args[2]); + return true; + } + + if (rank.isConsole()) + { + msg("You cannot set players to a console rank"); + return true; + } + + if (!rank.isAtLeast(Rank.ADMIN)) + { + msg("Rank must be Admin or higher.", ChatColor.RED); + return true; + } + + Admin admin = plugin.al.getEntryByName(args[1]); + if (admin == null) + { + msg("Unknown admin: " + args[1]); + return true; + } + + FUtil.adminAction(sender.getName(), "Setting " + admin.getName() + "'s rank to " + rank.getName(), true); + + admin.setRank(rank); + plugin.al.save(admin); + + Player player = getPlayer(admin.getName()); + if (player != null) + { + plugin.rm.updateDisplay(player); + } + + if (plugin.dc.enabled && ConfigEntry.DISCORD_ROLE_SYNC.getBoolean()) + { + Discord.syncRoles(admin, plugin.pl.getData(admin.getName()).getDiscordID()); + } + + plugin.ptero.updateAccountStatus(admin); + + msg("Set " + admin.getName() + "'s rank to " + rank.getName()); + return true; + } + + case "info": + { + if (args.length < 2) + { + return false; + } + + checkRank(Rank.ADMIN); + + Admin admin = plugin.al.getEntryByName(args[1]); + + if (admin == null) + { + final Player player = getPlayer(args[1]); + if (player != null) + { + admin = plugin.al.getAdmin(player); + } + } + + if (admin == null) + { + msg("Admin not found: " + args[1]); + } + else + { + msg(admin.toString()); + } + + return true; + } + + case "add": + { + if (args.length < 2) + { + return false; + } + + checkConsole(); + checkRank(Rank.ADMIN); + + // Player already admin? + final Player player = getPlayer(args[1]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + if (plugin.al.isAdmin(player)) + { + msg("That player is already an admin."); + return true; + } + + // Find the old admin entry + String name = player.getName(); + Admin admin = null; + for (Admin loopAdmin : plugin.al.getAllAdmins()) + { + if (loopAdmin.getName().equalsIgnoreCase(name) || loopAdmin.getIps().contains(FUtil.getIp(player))) + { + admin = loopAdmin; + break; + } + } + + if (plugin.pl.isPlayerImpostor(player)) + { + msg("This player was labeled as a Player impostor and is not an admin, therefore they cannot be added to the admin list.", ChatColor.RED); + return true; + } + + if (admin == null) // New admin + { + + FUtil.adminAction(sender.getName(), "Adding " + player.getName() + " to the admin list", true); + admin = new Admin(player); + + plugin.al.addAdmin(admin); + plugin.rm.updateDisplay(player); + } + else // Existing admin + { + FUtil.adminAction(sender.getName(), "Re-adding " + player.getName() + " to the admin list", true); + + String oldName = admin.getName(); + if (!oldName.equals(player.getName())) + { + admin.setName(player.getName()); + plugin.sql.updateAdminName(oldName, admin.getName()); + } + admin.addIp(FUtil.getIp(player)); + + admin.setActive(true); + admin.setLastLogin(new Date()); + + if (plugin.al.isVerifiedAdmin(player)) + { + plugin.al.verifiedNoAdmin.remove(player.getName()); + } + + plugin.al.save(admin); + plugin.al.updateTables(); + plugin.rm.updateDisplay(player); + + if (plugin.dc.enabled && ConfigEntry.DISCORD_ROLE_SYNC.getBoolean()) + { + Discord.syncRoles(admin, plugin.pl.getData(player).getDiscordID()); + } + } + plugin.ptero.updateAccountStatus(admin); + + final FPlayer fPlayer = plugin.pl.getPlayer(player); + if (fPlayer.getFreezeData().isFrozen()) + { + fPlayer.getFreezeData().setFrozen(false); + msg(player, "You have been unfrozen."); + } + + if (!player.isOp()) + { + player.setOp(true); + msg(player, YOU_ARE_OP); + } + return true; + } + + case "remove": + { + if (args.length < 2) + { + return false; + } + + checkConsole(); + checkRank(Rank.ADMIN); + + Player player = getPlayer(args[1]); + Admin admin = player != null ? plugin.al.getAdmin(player) : plugin.al.getEntryByName(args[1]); + + if (admin == null) + { + msg("Admin not found: " + args[1]); + return true; + } + + FUtil.adminAction(sender.getName(), "Removing " + admin.getName() + " from the admin list", true); + admin.setActive(false); + + plugin.al.save(admin); + plugin.al.updateTables(); + if (player != null) + { + plugin.rm.updateDisplay(player); + plugin.pl.getPlayer(player).setAdminChat(false); + } + + if (plugin.dc.enabled && ConfigEntry.DISCORD_ROLE_SYNC.getBoolean()) + { + Discord.syncRoles(admin, plugin.pl.getData(admin.getName()).getDiscordID()); + } + + plugin.ptero.updateAccountStatus(admin); + + return true; + } + + default: + { + return false; + } + } + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + List arguments = new ArrayList<>(); + arguments.add("list"); + if (plugin.al.isAdmin(sender)) + { + arguments.add("info"); + arguments.add("add"); + arguments.add("remove"); + } + if (plugin.al.isSeniorAdmin(sender)) + { + arguments.add("reload"); + arguments.add("clean"); + arguments.add("setrank"); + } + return arguments; + } + if (args.length == 2 && (args[0].equals("add") || args[0].equals("remove") || args[0].equals("setrank") || args[0].equals("info"))) + { + return FUtil.getPlayerList(); + } + if (args.length == 3 && args[0].equals("setrank")) + { + return Arrays.asList("ADMIN", "SENIOR_ADMIN"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_say.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_say.java new file mode 100644 index 00000000..a79af9ae --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_say.java @@ -0,0 +1,47 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Broadcasts the given message as the server, includes sender name.", usage = "/ ") +public class Command_say extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + String message = StringUtils.join(args, " "); + + if (senderIsConsole && FUtil.isFromHostConsole(sender.getName())) + { + if (message.equalsIgnoreCase("WARNING: Server is restarting, you will be kicked")) + { + FUtil.bcastMsg("Server is going offline.", ChatColor.GRAY); + + for (Player player : server.getOnlinePlayers()) + { + player.kickPlayer(ChatColor.LIGHT_PURPLE + "Server is going offline, come back in about 20 seconds."); + } + + server.shutdown(); + + return true; + } + } + + FUtil.bcastMsg(String.format("[Server:%s] %s", sender.getName(), message), ChatColor.LIGHT_PURPLE); + plugin.dc.messageChatChannel(String.format("[Server:%s] \u00BB %s", sender.getName(), message)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_scare.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_scare.java new file mode 100644 index 00000000..15da84ec --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_scare.java @@ -0,0 +1,62 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Sends a guardian particle effect with an enderman scream to the specified player.", usage = "/ ") +public class Command_scare extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!FUtil.isPaper()) + { + msg("This command won't work without Paper!", ChatColor.RED); + return true; + } + + if (args.length == 0) + { + return false; + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + msg("Scared " + player.getName()); + msg(player, "ZING", ChatColor.RED); + + player.spawnParticle(Particle.MOB_APPEARANCE, player.getLocation(), 4); + for (int i = 0; i < 10; ++i) + { + player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_SCREAM, 1, 0); + } + + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1 && plugin.al.isAdmin(sender)) + { + return FUtil.getPlayerList(); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_serverstats.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_serverstats.java new file mode 100644 index 00000000..6f3a3f35 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_serverstats.java @@ -0,0 +1,28 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Check the status of the server, including opped players, admins, etc.", usage = "/", aliases = "ss") +public class Command_serverstats extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + msg("-==" + ConfigEntry.SERVER_NAME.getString() + " server stats==-", ChatColor.GOLD); + msg("Total opped players: " + server.getOperators().size(), ChatColor.RED); + msg("Total admins: " + plugin.al.getAllAdmins().size() + " (" + plugin.al.getActiveAdmins().size() + " active)", ChatColor.BLUE); + int bans = plugin.im.getIndefBans().size(); + int nameBans = plugin.im.getNameBanCount(); + int uuidBans = plugin.im.getUuidBanCount(); + int ipBans = plugin.im.getIpBanCount(); + msg("Total indefinite ban entries: " + bans + " (" + nameBans + " name bans, " + uuidBans + " UUID bans, and " + ipBans + " IP bans)", ChatColor.GREEN); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setcompass.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setcompass.java new file mode 100644 index 00000000..aeafe711 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setcompass.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Set your compass to the specified position.", usage = "/ ") +public class Command_setcompass extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 3) + { + return false; + } + + try + { + Location location = new Location(playerSender.getWorld(), Integer.parseInt(args[0]), Integer.parseInt(args[1]), Integer.parseInt(args[2])); + playerSender.setCompassTarget(location); + msg("Successfully set your compass coordinates to X: " + args[0] + ", Y: " + args[1] + ", Z: " + args[2] + ".", ChatColor.GREEN); + } + catch (NumberFormatException e) + { + msg("One or more of your coordinates are not a valid integer.", ChatColor.RED); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlevel.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlevel.java new file mode 100644 index 00000000..551c09d4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlevel.java @@ -0,0 +1,49 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Sets your experience level (XP).", usage = "/ [level]") +public class Command_setlevel extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 1) + { + return false; + } + + int new_level; + + try + { + new_level = Integer.parseInt(args[0]); + + if (new_level < 0) + { + new_level = 0; + } + else if (new_level > 50) + { + new_level = 50; + } + } + catch (NumberFormatException ex) + { + msg("Invalid level.", ChatColor.RED); + return true; + } + + playerSender.setLevel(new_level); + + msg("Your XP level is now set to " + ChatColor.GOLD + new_level); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlever.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlever.java new file mode 100644 index 00000000..b8430169 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlever.java @@ -0,0 +1,93 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Switch; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Set the on/off state of the lever at position x, y, z in world 'worldname'.", usage = "/ ") +public class Command_setlever extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length != 5) + { + return false; + } + + double x, y, z; + try + { + x = Double.parseDouble(args[0]); + y = Double.parseDouble(args[1]); + z = Double.parseDouble(args[2]); + } + catch (NumberFormatException ex) + { + msg("Invalid coordinates."); + return true; + } + + if (x > 29999998 || x < -29999998 || y > 29999998 || y < -29999998 || z > 29999998 || z < -29999998) + { + msg("Coordinates cannot be larger than 29999998 or smaller than -29999998 blocks."); + return true; + } + + World world = null; + final String needleWorldName = args[3].trim(); + final List worlds = server.getWorlds(); + for (final World testWorld : worlds) + { + if (testWorld.getName().trim().equalsIgnoreCase(needleWorldName)) + { + world = testWorld; + break; + } + } + + if (world == null) + { + msg("Invalid world name."); + return true; + } + + final Location leverLocation = new Location(world, x, y, z); + + final boolean leverOn = (args[4].trim().equalsIgnoreCase("on") || args[4].trim().equalsIgnoreCase("1")); + + final Block targetBlock = leverLocation.getBlock(); + + if (targetBlock.getType() == Material.LEVER) + { + BlockState state = targetBlock.getState(); + BlockData data = state.getBlockData(); + Switch caster = (Switch)data; + + caster.setPowered(leverOn); + state.setBlockData(data); + state.update(); + + plugin.cpb.getCoreProtectAPI().logInteraction(sender.getName(), leverLocation); + } + else + { + msg("Target block " + targetBlock + " is not a lever."); + return true; + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlimit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlimit.java new file mode 100644 index 00000000..bfe9511e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setlimit.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Sets everyone's WorldEdit block modification limit to the default limit or to a custom limit.", usage = "/ [limit]", aliases = "setl,swl") +public class Command_setlimit extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + int amount = plugin.web.getDefaultLimit(); + if (args.length > 0) + { + try + { + amount = Math.max(-1, Math.min(plugin.web.getMaxLimit(), Integer.parseInt(args[0]))); + } + catch (NumberFormatException ex) + { + msg("Invalid number: " + args[0], ChatColor.RED); + return true; + } + } + boolean success = false; + for (final Player player : server.getOnlinePlayers()) + { + try + { + plugin.web.setLimit(player, amount); + success = true; + } + catch (NoClassDefFoundError | NullPointerException ex) + { + msg("WorldEdit is not enabled on this server."); + success = false; + } + } + if (success) + { + FUtil.adminAction(sender.getName(), "Setting everyone's WorldEdit block modification limit to " + amount + ".", true); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setplayerlimit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setplayerlimit.java new file mode 100644 index 00000000..fb9f640e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setplayerlimit.java @@ -0,0 +1,67 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Sets a specific player's WorldEdit block modification limit to the default limit or to a custom limit.", usage = "/ [limit]", aliases = "setpl,spl") +public class Command_setplayerlimit extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + int amount; + if (args.length > 0) + { + Player player = Bukkit.getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (args.length == 2) + { + try + { + amount = Math.max(-1, Math.min(plugin.web.getMaxLimit(), Integer.parseInt(args[1]))); + } + catch (NumberFormatException ex) + { + msg("Invalid number: " + args[1], ChatColor.RED); + return true; + } + } + else + { + amount = plugin.web.getDefaultLimit(); + } + } + else + { + return false; + } + boolean success = false; + Player player = Bukkit.getPlayer(args[0]); + try + { + plugin.web.setLimit(player, amount); + success = true; + } + catch (NoClassDefFoundError | NullPointerException ex) + { + msg("WorldEdit is not enabled on this server."); + } + if (success) + { + assert player != null; + FUtil.adminAction(sender.getName(), "Setting " + player.getName() + "'s WorldEdit block modification limit to " + amount + ".", true); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setspawnworld.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setspawnworld.java new file mode 100644 index 00000000..49c2dee8 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_setspawnworld.java @@ -0,0 +1,25 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Set the spawn point of the world you are in.", usage = "/") +public class Command_setspawnworld extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + Location pos = playerSender.getLocation(); + playerSender.getWorld().setSpawnLocation(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + + msg("Spawn location for this world set to: " + FUtil.formatLocation(playerSender.getWorld().getSpawnLocation())); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_settotalvotes.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_settotalvotes.java new file mode 100644 index 00000000..3c6de7be --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_settotalvotes.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.SENIOR_ADMIN, source = SourceType.ONLY_CONSOLE) +@CommandParameters(description = "Set a player's total votes", usage = "/ ") +public class Command_settotalvotes extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + if (!ConfigEntry.SERVER_OWNERS.getStringList().contains(sender.getName())) + { + return noPerms(); + } + + if (args.length < 2) + { + return false; + } + + int votes; + try + { + votes = Integer.parseInt(args[0]); + } + catch (NumberFormatException e) + { + msg("Invalid number: " + args[0]); + return true; + } + + PlayerData playerData = plugin.pl.getData(args[1]); + + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg("Set " + args[1] + "'s votes to " + args[0]); + + playerData.setTotalVotes(votes); + plugin.pl.save(playerData); + + Player player = getPlayer(args[1]); + + if (player != null) + { + msg(player, sender.getName() + " has set your total votes to " + votes, ChatColor.GREEN); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_shop.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_shop.java new file mode 100644 index 00000000..89c6381a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_shop.java @@ -0,0 +1,26 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Open the shop GUI", usage = "/", aliases = "sh") +public class Command_shop extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!ConfigEntry.SHOP_ENABLED.getBoolean()) + { + msg("The shop is currently disabled!", ChatColor.RED); + return true; + } + playerSender.openInventory(plugin.sh.generateShopGUI(plugin.pl.getData(playerSender))); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_sit.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_sit.java new file mode 100644 index 00000000..f54b677d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_sit.java @@ -0,0 +1,35 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Sit at the current place you are at.", usage = "/") +public class Command_sit extends FreedomCommand +{ + public static List STANDS = new ArrayList<>(); + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + if (args.length != 0) + { + return false; + } + + ArmorStand stand = (ArmorStand)playerSender.getWorld().spawnEntity(playerSender.getLocation().clone().subtract(0.0, 1.7, 0.0), EntityType.ARMOR_STAND); + stand.setGravity(false); + stand.setAI(false); + stand.setVisible(false); + stand.setInvulnerable(true); + stand.addPassenger(playerSender); + STANDS.add(stand); + msg("You are now sitting."); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_smite.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_smite.java new file mode 100644 index 00000000..92b73d0d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_smite.java @@ -0,0 +1,141 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Someone being a little bitch? Smite them down...", usage = "/ [reason] [-ci | -q]") +public class Command_smite extends FreedomCommand +{ + + public static void smite(CommandSender sender, Player player) + { + smite(sender, player, null, false, false); + } + + public static void smite(CommandSender sender, Player player, String reason) + { + smite(sender, player, reason, false, false); + } + + public static void smite(CommandSender sender, Player player, String reason, Boolean silent, Boolean clearinv) + { + player.sendTitle(ChatColor.RED + "You've been smitten.", ChatColor.YELLOW + "Be sure to follow the rules!", 20, 100, 60); + + if (!silent) + { + FUtil.bcastMsg(player.getName() + " has been a naughty, naughty boy.", ChatColor.RED); + if (reason != null) + { + FUtil.bcastMsg(" Reason: " + ChatColor.YELLOW + reason, ChatColor.RED); + } + FUtil.bcastMsg(" Smitten by: " + ChatColor.YELLOW + sender.getName(), ChatColor.RED); + } + else + { + sender.sendMessage("Smitten " + player.getName() + " quietly."); + } + + // Deop + player.setOp(false); + + // Set gamemode to survival + player.setGameMode(GameMode.SURVIVAL); + + // Clear inventory + if (clearinv) + { + player.getInventory().clear(); + } + + // Strike with lightning effect + final Location targetPos = player.getLocation(); + final World world = player.getWorld(); + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + final Location strike_pos = new Location(world, targetPos.getBlockX() + x, targetPos.getBlockY(), targetPos.getBlockZ() + z); + world.strikeLightning(strike_pos); + } + } + + // Kill + player.setHealth(0.0); + + if (reason != null) + { + player.sendMessage(ChatColor.RED + "You've been smitten. Reason: " + ChatColor.YELLOW + reason); + player.sendTitle(ChatColor.RED + "You've been smitten.", ChatColor.YELLOW + "Reason: " + reason, 20, 100, 60); + } + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + String reason = null; + boolean silent = false; + boolean clearinv = false; + if (args.length >= 2) + { + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + if (args[args.length - 1].equalsIgnoreCase("-q")) + { + silent = true; + } + + if (args.length >= 3) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else if (args[args.length - 1].equalsIgnoreCase("-ci")) + { + if (args[args.length - 1].equalsIgnoreCase("-ci")) + { + clearinv = true; + } + + if (args.length >= 3) + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length - 1), " "); + } + } + else + { + reason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + } + } + + final Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + smite(sender, player, reason, silent, clearinv); + + plugin.pul.logPunishment(new Punishment(player.getName(), FUtil.getIp(player), sender.getName(), PunishmentType.SMITE, reason)); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spawnmob.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spawnmob.java new file mode 100644 index 00000000..5306ceda --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spawnmob.java @@ -0,0 +1,88 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.EnumUtils; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Spawns the specified entity.", usage = "/ [amount]", aliases = "spawnentity") +public class Command_spawnmob extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0 && args[0].equalsIgnoreCase("list")) + { + List types = EnumUtils.getEnumList(EntityType.class); + String typeList = StringUtils.join(types, ", ").toLowerCase(); + msg(typeList); + return true; + } + + if (args.length < 1) + { + return false; + } + + EntityType type = null; + for (EntityType loop : EntityType.values()) + { + if (loop != null && loop.name().equalsIgnoreCase(args[0])) + { + type = loop; + break; + } + } + + if (type == null) + { + msg("Unknown entity type: " + args[0], ChatColor.RED); + return true; + } + + if (!type.isSpawnable() || !type.isAlive()) + { + msg("Can not spawn entity type: " + type.name().toLowerCase()); + return true; + } + + int amount = 1; + if (args.length > 1) + { + try + { + amount = Integer.parseInt(args[1]); + } + catch (NumberFormatException nfex) + { + msg("Invalid amount: " + args[1], ChatColor.RED); + return true; + } + } + + if (amount > 10 || amount < 1) + { + msg("Invalid amount: " + args[1] + ". Must be 1-10.", ChatColor.RED); + return true; + } + + Location l = playerSender.getTargetBlock(null, 30).getLocation().add(0, 1, 0); + World w = playerSender.getWorld(); + msg("Spawning " + amount + " " + type.name().toLowerCase() + (amount > 1 ? "s." : ".")); + + for (int i = 0; i < amount; i++) + { + w.spawnEntity(l, type); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectate.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectate.java new file mode 100644 index 00000000..c3d8edbe --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectate.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Quickly spectate someone.", usage = "/ ", aliases = "spec") +public class Command_spectate extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (player.getGameMode().equals(GameMode.SPECTATOR)) + { + msg("You cannot spectate other players that are in spectator mode.", ChatColor.RED); + return true; + } + + if (!playerSender.getGameMode().equals(GameMode.SPECTATOR)) + { + playerSender.setGameMode(GameMode.SPECTATOR); + } + + if (playerSender.getWorld() != player.getWorld()) + { + PaperLib.teleportAsync(playerSender, player.getLocation()); + } + + playerSender.setSpectatorTarget(player); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectator.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectator.java new file mode 100644 index 00000000..808beb93 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_spectator.java @@ -0,0 +1,43 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Quickly change your own gamemode to spectator, or define someone's username to change theirs.", usage = "/ <[partialname]>", aliases = "gmsp") +public class Command_spectator extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + if (isConsole()) + { + msg("When used from the console, you must define a target player."); + return true; + } + + playerSender.setGameMode(GameMode.SPECTATOR); + msg("Your gamemode has been set to spectator."); + return true; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg("Setting " + player.getName() + " to game mode spectator"); + msg(player, sender.getName() + " set your game mode to spectator"); + player.setGameMode(GameMode.SPECTATOR); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stackingpotato.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stackingpotato.java new file mode 100644 index 00000000..7a1514f5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stackingpotato.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Obtain a stacking potato", usage = "/") +public class Command_stackingpotato extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.pl.getData(playerSender).hasItem(ShopItem.STACKING_POTATO)) + { + playerSender.getInventory().addItem(plugin.sh.getStackingPotato()); + msg("You have been given a Stacking Potato", ChatColor.GREEN); + } + else + { + msg("You do not own the Stacking Potato! Purchase one from the shop.", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_status.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_status.java new file mode 100644 index 00000000..17b7578f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_status.java @@ -0,0 +1,44 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Shows Minecraft server info, such as authentication status.", usage = "/") +public class Command_status extends FreedomCommand +{ + + public static final Map SERVICE_MAP = new HashMap<>(); + + static + { + SERVICE_MAP.put("minecraft.net", "Minecraft.net"); + SERVICE_MAP.put("login.minecraft.net", "Minecraft Logins"); + SERVICE_MAP.put("session.minecraft.net", "Minecraft Multiplayer Sessions"); + SERVICE_MAP.put("account.mojang.com", "Mojang Accounts Website"); + SERVICE_MAP.put("auth.mojang.com", "Mojang Accounts Login"); + SERVICE_MAP.put("skins.minecraft.net", "Minecraft Skins"); + } + + @Override + public boolean run(final CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + msg("For information about TotalFreedomMod, try /tfm", ChatColor.GREEN); // Temporary + + msg("Server is currently running with 'online-mode=" + (server.getOnlineMode() ? "true" : "false") + "'.", ChatColor.YELLOW); + msg("Loaded worlds:", ChatColor.BLUE); + int i = 0; + for (World world : server.getWorlds()) + { + msg(String.format("World %d: %s - %d players.", i++, world.getName(), world.getPlayers().size()), ChatColor.BLUE); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java new file mode 100644 index 00000000..e4ea5880 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stop.java @@ -0,0 +1,73 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Kicks everyone and stops the server.", usage = "/ [reason]") +public class Command_stop extends FreedomCommand +{ + private static final Map STOP_CONFIRM = new HashMap<>(); + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + String reason = "Server is going offline, come back in about 20 seconds."; + + if (args.length != 0) + { + reason = StringUtils.join(args, " "); + } + + if (sender.getName().equals("CONSOLE")) + { + shutdown(reason); + return true; + } + else if (STOP_CONFIRM.containsKey(sender)) + { + shutdown(STOP_CONFIRM.get(sender)); + return true; + } + + + msg("Warning: You're about to stop the server. Type /stop again to confirm you want to do this."); + + STOP_CONFIRM.put(sender, reason); + new BukkitRunnable() + { + @Override + public void run() + { + if (STOP_CONFIRM.containsKey(sender)) + { + STOP_CONFIRM.remove(sender); + msg("Stop request expired."); + } + } + }.runTaskLater(plugin, 15 * 20); + return true; + } + + public void shutdown(String reason) + { + FUtil.bcastMsg("Server is going offline!", ChatColor.LIGHT_PURPLE); + + for (Player player : server.getOnlinePlayers()) + { + player.kickPlayer(ChatColor.LIGHT_PURPLE + reason); + } + + STOP_CONFIRM.remove(sender); + + server.shutdown(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stopsound.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stopsound.java new file mode 100644 index 00000000..1e0acb92 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_stopsound.java @@ -0,0 +1,73 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.Sound; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Stops all sounds or a specified sound.", usage = "/ [sound]") +public class Command_stopsound extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0) + { + Sound sound = null; + + for (Sound loop : Sound.values()) + { + if (loop != null && loop.name().equalsIgnoreCase(args[0])) + { + sound = Sound.valueOf(args[0].toUpperCase()); + break; + } + } + + if (sound == null) + { + msg(args[0] + " is not a valid sound.", ChatColor.RED); + return true; + } + + playerSender.stopSound(sound); + msg("Stopped all " + sound.name() + " sounds", ChatColor.GREEN); + return true; + } + + for (Sound sound : Sound.values()) + { + playerSender.stopSound(sound); + } + + msg("Stopped all sounds.", ChatColor.GREEN); + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return soundList(); + } + return Collections.emptyList(); + } + + public List soundList() + { + List sounds = new ArrayList<>(); + for (Sound sound : Sound.values()) + { + sounds.add(sound.name()); + } + return sounds; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_survival.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_survival.java new file mode 100644 index 00000000..2a600dfe --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_survival.java @@ -0,0 +1,58 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Quickly change your own gamemode to survival, or define someone's username to change theirs.", usage = "/ <[partialname] | -a>", aliases = "gms") +public class Command_survival extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + if (isConsole()) + { + msg("When used from the console, you must define a target player."); + return true; + } + + playerSender.setGameMode(GameMode.SURVIVAL); + msg("Your gamemode has been set to survival."); + return true; + } + + checkRank(Rank.ADMIN); + + if (args[0].equals("-a")) + { + for (Player targetPlayer : server.getOnlinePlayers()) + { + targetPlayer.setGameMode(GameMode.SURVIVAL); + } + + FUtil.adminAction(sender.getName(), "Changing everyone's gamemode to survival", false); + msg("Your gamemode has been set to survival."); + return true; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + msg("Setting " + player.getName() + " to game mode survival."); + msg(player, sender.getName() + " set your game mode to survival."); + player.setGameMode(GameMode.SURVIVAL); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tag.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tag.java new file mode 100644 index 00000000..6f6a1520 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tag.java @@ -0,0 +1,293 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Allows you to set your own prefix.", usage = "/ [-s[ave]] | list | gradient | off | clear | clearall>") +public class Command_tag extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + boolean save = false; + + if (args.length < 1) + { + return false; + } + + if (args[0].equals("-s") || args[0].equals("-save")) + { + save = true; + args = ArrayUtils.remove(args, 0); + } + + if (args.length == 1) + { + switch (args[0].toLowerCase()) + { + case "list": + { + msg("Tags for all online players:"); + + for (final Player player : server.getOnlinePlayers()) + { + if (plugin.al.isVanished(player.getName()) && !plugin.al.isAdmin(sender)) + { + continue; + } + + final FPlayer playerdata = plugin.pl.getPlayer(player); + if (playerdata.getTag() != null) + { + msg(player.getName() + ": " + playerdata.getTag()); + } + } + return true; + } + + case "clearall": + { + if (!plugin.al.isAdmin(sender)) + { + noPerms(); + return true; + } + + FUtil.adminAction(sender.getName(), "Removing all tags", false); + + int count = 0; + for (final Player player : server.getOnlinePlayers()) + { + final FPlayer playerdata = plugin.pl.getPlayer(player); + if (playerdata.getTag() != null) + { + count++; + playerdata.setTag(null); + } + } + + msg(count + " tag(s) removed."); + return true; + } + + case "off": + { + if (senderIsConsole) + { + msg("\"/tag off\" can't be used from the console. Use \"/tag clear \" or \"/tag clearall\" instead."); + } + else + { + plugin.pl.getPlayer(playerSender).setTag(null); + + if (save) + { + save(playerSender, null); + } + + msg("Your tag has been removed." + (save ? " (Saved)" : "")); + } + return true; + } + } + } + else if (args.length >= 2) + { + switch (args[0].toLowerCase()) + { + case "clear": + { + if (!plugin.al.isAdmin(sender)) + { + noPerms(); + return true; + } + + final Player player = getPlayer(args[1]); + + if (player == null) + { + msg(FreedomCommand.PLAYER_NOT_FOUND); + return true; + } + + plugin.pl.getPlayer(player).setTag(null); + if (save) + { + save(player, null); + } + + msg("Removed " + player.getName() + "'s tag." + (save ? " (Saved)" : "")); + return true; + } + + case "set": + { + if (senderIsConsole) + { + msg("\"/tag set\" can't be used from the console."); + return true; + } + + final String inputTag = StringUtils.join(args, " ", 1, args.length); + final String strippedTag = StringUtils.replaceEachRepeatedly(StringUtils.strip(inputTag), + new String[] + { + "" + ChatColor.COLOR_CHAR, "&k" + }, + new String[] + { + "", "" + }); + + final String outputTag = FUtil.colorize(strippedTag); + int tagLimit = (plugin.al.isAdmin(sender) ? 30 : 20); + final String rawTag = ChatColor.stripColor(outputTag).toLowerCase(); + + if (rawTag.length() > tagLimit) + { + msg("That tag is too long (Max is " + tagLimit + " characters)."); + return true; + } + + if (!plugin.al.isAdmin(sender)) + { + for (String word : ConfigEntry.FORBIDDEN_WORDS.getStringList()) + { + if (rawTag.contains(word)) + { + msg("That tag contains a forbidden word."); + return true; + } + } + } + + plugin.pl.getPlayer(playerSender).setTag(outputTag); + + if (save) + { + save(playerSender, strippedTag); + } + + msg("Tag set to '" + outputTag + ChatColor.GRAY + "'." + (save ? " (Saved)" : "")); + return true; + } + + case "gradient": + { + if (senderIsConsole) + { + msg("\"/tag gradient\" can't be used from the console."); + return true; + } + + if (args.length < 4) + { + return false; + } + + String from = "", to = ""; + java.awt.Color awt1, awt2; + + try + { + if (args[1].equalsIgnoreCase("random") || args[1].equalsIgnoreCase("r")) + { + awt1 = FUtil.getRandomAWTColor(); + from = " (From: " + FUtil.getHexStringOfAWTColor(awt1) + ")"; + } + else + { + awt1 = java.awt.Color.decode(args[1]); + } + + if (args[2].equalsIgnoreCase("random") || args[2].equalsIgnoreCase("r")) + { + awt2 = FUtil.getRandomAWTColor(); + to = " (To: " + FUtil.getHexStringOfAWTColor(awt2) + ")"; + } + else + { + awt2 = java.awt.Color.decode(args[2]); + } + } + catch (NumberFormatException ex) + { + msg("Invalid hex values."); + return true; + } + + Color c1 = FUtil.fromAWT(awt1); + Color c2 = FUtil.fromAWT(awt2); + String tag = StringUtils.join(args, " ", 3, args.length); + List gradient = FUtil.createColorGradient(c1, c2, tag.length()); + String[] splitTag = tag.split(""); + + for (int i = 0; i < splitTag.length; i++) + { + splitTag[i] = net.md_5.bungee.api.ChatColor.of(FUtil.toAWT(gradient.get(i))) + splitTag[i]; + } + + tag = StringUtils.join(splitTag, ""); + final String outputTag = FUtil.colorize(tag); + + int tagLimit = (plugin.al.isAdmin(sender) ? 30 : 20); + + final String rawTag = ChatColor.stripColor(outputTag).toLowerCase(); + + if (rawTag.length() > tagLimit) + { + msg("That tag is too long (Max is " + tagLimit + " characters)."); + return true; + } + + if (!plugin.al.isAdmin(sender)) + { + for (String word : ConfigEntry.FORBIDDEN_WORDS.getStringList()) + { + if (rawTag.contains(word)) + { + msg("That tag contains a forbidden word."); + return true; + } + } + } + + plugin.pl.getPlayer(playerSender).setTag(outputTag); + + if (save) + { + save(playerSender, tag); + } + + msg("Tag set to '" + outputTag + ChatColor.GRAY + "'." + (save ? " (Saved)" : "") + from + to); + return true; + } + } + } + return false; + } + + public void save(Player player, String tag) + { + PlayerData playerData = plugin.pl.getData(player); + playerData.setTag(tag); + plugin.pl.save(playerData); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagnyan.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagnyan.java new file mode 100644 index 00000000..f2618fd8 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagnyan.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Give yourself a prefix with random colors", usage = "/ ", aliases = "tn") +public class Command_tagnyan extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + final StringBuilder tag = new StringBuilder(); + + for (char c : ChatColor.stripColor(FUtil.colorize(StringUtils.join(args, " "))).toCharArray()) + { + tag.append(FUtil.randomChatColor()).append(c); + } + + String tagStr = tag.toString(); + + int tagLimit = (plugin.al.isAdmin(sender) ? 30 : 20); + + final String rawTag = ChatColor.stripColor(tagStr).toLowerCase(); + + if (rawTag.length() > tagLimit) + { + msg("That tag is too long (Max is " + tagLimit + " characters)."); + return true; + } + + if (!plugin.al.isAdmin(sender)) + { + for (String word : ConfigEntry.FORBIDDEN_WORDS.getStringList()) + { + if (rawTag.contains(word)) + { + msg("That tag contains a forbidden word."); + return true; + } + } + } + + final FPlayer data = plugin.pl.getPlayer(playerSender); + data.setTag(tagStr); + + msg("Set tag to " + tag); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagrainbow.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagrainbow.java new file mode 100644 index 00000000..3795a8c6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tagrainbow.java @@ -0,0 +1,51 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Give yourself a prefix with rainbow colors.", usage = "/ ") +public class Command_tagrainbow extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + final String tag = ChatColor.stripColor(FUtil.colorize(StringUtils.join(args, " "))); + + if (!plugin.al.isAdmin(sender)) + { + final String rawTag = ChatColor.stripColor(tag).toLowerCase(); + + if (rawTag.length() > 20) + { + msg("That tag is too long (Max is 20 characters)."); + return true; + } + + for (String word : ConfigEntry.FORBIDDEN_WORDS.getStringList()) + { + if (rawTag.contains(word)) + { + msg("That tag contains a forbidden word."); + return true; + } + } + } + + plugin.pl.getPlayer(playerSender).setTag(FUtil.rainbowify(tag)); + msg("Set tag to " + tag); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tban.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tban.java new file mode 100644 index 00000000..c4c4c62f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tban.java @@ -0,0 +1,143 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Temporarily bans a player for five minutes.", usage = "/ [-q] [reason]", aliases = "noob") +public class Command_tban extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean quiet = args[0].equalsIgnoreCase("-q"); + if (quiet) + { + args = org.apache.commons.lang3.ArrayUtils.subarray(args, 1, args.length); + + if (args.length < 1) + { + return false; + } + } + + final String username; + + final Player player = getPlayer(args[0]); + final PlayerData entry; + if (player == null) + { + entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + username = entry.getName(); + } + else + { + entry = plugin.pl.getData(player); + username = player.getName(); + } + final List ips = new ArrayList<>(entry.getIps()); + + String reason = null; + if (args.length > 1) + { + reason = StringUtils.join(args, " ", 1, args.length); + } + + StringBuilder kick = new StringBuilder() + .append(ChatColor.RED) + .append("You have been temporarily banned for five minutes. Please read totalfreedom.me for more info."); + + if (!quiet) + { + // Strike with lightning + if (player != null) + { + final Location targetPos = player.getLocation(); + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + final Location strike_pos = new Location(targetPos.getWorld(), targetPos.getBlockX() + x, targetPos.getBlockY(), targetPos.getBlockZ() + z); + Objects.requireNonNull(targetPos.getWorld()).strikeLightning(strike_pos); + } + } + + // Kill player + player.setHealth(0.0); + + if (reason != null) + { + FUtil.adminAction(sender.getName(), "Tempbanning " + player.getName() + " for 5 minutes - Reason: " + reason, true); + kick.append("\n") + .append(ChatColor.RED) + .append("Reason: ") + .append(ChatColor.GOLD) + .append(reason); + } + else + { + FUtil.adminAction(sender.getName(), "Tempbanning " + player.getName() + " for 5 minutes", true); + } + } + } + else + { + if (player != null) + { + if (reason != null) + { + msg("Quietly temporarily banned " + player.getName() + " for 5 minutes."); + kick.append("\n") + .append(ChatColor.RED) + .append("Reason: ") + .append(ChatColor.GOLD) + .append(reason); + } + } + } + + // Ban player + Ban ban = Ban.forPlayerName(username, sender, FUtil.parseDateOffset("5m"), reason); + for (String ip : ips) + { + ban.addIp(ip); + } + plugin.bm.addBan(ban); + + // Kick player + if (player != null) + { + player.kickPlayer(kick.toString()); + } + + // Log ban + plugin.pul.logPunishment(new Punishment(username, ips.get(0), sender.getName(), PunishmentType.TEMPBAN, reason)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tempban.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tempban.java new file mode 100644 index 00000000..dfc412ce --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tempban.java @@ -0,0 +1,146 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.punishments.Punishment; +import me.totalfreedom.totalfreedommod.punishments.PunishmentType; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Temporarily ban someone.", usage = "/ [-q] [duration] [reason]") +public class Command_tempban extends FreedomCommand +{ + + private static final SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + boolean quiet = args[0].equalsIgnoreCase("-q"); + if (quiet) + { + args = org.apache.commons.lang3.ArrayUtils.subarray(args, 1, args.length); + + if (args.length < 1) + { + return false; + } + } + + final String username; + final List ips = new ArrayList<>(); + + final Player player = getPlayer(args[0]); + final PlayerData entry; + if (player == null) + { + entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + username = entry.getName(); + ips.addAll(entry.getIps()); + } + else + { + entry = plugin.pl.getData(player); + username = player.getName(); + ips.add(FUtil.getIp(player)); + } + + final StringBuilder message = new StringBuilder("Temporarily banned " + username); + + Date expires = FUtil.parseDateOffset("30m"); + message.append(" until ").append(date_format.format(expires)); + + String reason = null; + if (args.length >= 2) + { + Date parsed_offset = FUtil.parseDateOffset(args[1]); + reason = StringUtils.join(ArrayUtils.subarray(args, parsed_offset == null ? 1 : 2, args.length), " ") + " (" + sender.getName() + ")"; + if (parsed_offset != null) + { + expires = parsed_offset; + } + message.append(", Reason: \"").append(reason).append("\""); + } + + Ban ban; + + if (player != null) + { + ban = Ban.forPlayer(player, sender, expires, reason); + } + else + { + ban = Ban.forPlayerName(username, sender, expires, reason); + } + + for (String ip : ips) + { + ban.addIp(ip); + } + plugin.bm.addBan(ban); + + if (!quiet) + { + if (player != null) + { + // Strike with lightning + final Location targetPos = player.getLocation(); + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + final Location strike_pos = new Location(targetPos.getWorld(), targetPos.getBlockX() + x, targetPos.getBlockY(), targetPos.getBlockZ() + z); + Objects.requireNonNull(targetPos.getWorld()).strikeLightningEffect(strike_pos); + } + } + } + + FUtil.adminAction(sender.getName(), message.toString(), true); + } + else + { + msg("Quietly temporarily banned " + username + "."); + } + + if (player != null) + { + player.kickPlayer(ban.bakeKickMessage()); + for (Player p : Bukkit.getOnlinePlayers()) + { + if (FUtil.getIp(p).equals(FUtil.getIp(player))) + { + p.kickPlayer(ChatColor.RED + "You've been kicked because someone on your IP has been banned."); + } + } + } + + plugin.pul.logPunishment(new Punishment(username, ips.get(0), sender.getName(), PunishmentType.TEMPBAN, reason)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggle.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggle.java new file mode 100644 index 00000000..c25ebe9c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggle.java @@ -0,0 +1,334 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.GameRuleHandler; +import me.totalfreedom.totalfreedommod.LoginProcess; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggles TotalFreedomMod settings", usage = "/ [option] [value] [value]") +public class Command_toggle extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + msg("Available toggles: "); + msg("- waterplace"); + msg("- fireplace"); + msg("- lavaplace"); + msg("- fluidspread"); + msg("- lavadmg"); + msg("- firespread"); + msg("- frostwalk"); + msg("- firework"); + msg("- prelog"); + msg("- lockdown"); + msg("- petprotect"); + msg("- entitywipe"); + msg("- nonuke [range] [count]"); + msg("- explosives [radius]"); + msg("- unsafeenchs"); + msg("- bells"); + msg("- armorstands"); + msg("- structureblocks"); + msg("- jigsaws"); + msg("- grindstones"); + msg("- jukeboxes"); + msg("- spawners"); + msg("- 4chan"); + msg("- beehives"); + msg("- respawnanchors"); + msg("- autotp"); + msg("- autoclear"); + msg("- minecarts"); + msg("- landmines"); + msg("- mp44"); + msg("- tossmob"); + msg("- gravity"); + return false; + } + + switch (args[0].toLowerCase()) + { + case "waterplace": + { + toggle("Water placement is", ConfigEntry.ALLOW_WATER_PLACE); + break; + } + + case "frostwalk": + { + toggle("Frost walker enchantment is", ConfigEntry.ALLOW_FROSTWALKER); + break; + } + + case "fireplace": + { + toggle("Fire placement is", ConfigEntry.ALLOW_FIRE_PLACE); + break; + } + + case "lavaplace": + { + toggle("Lava placement is", ConfigEntry.ALLOW_LAVA_PLACE); + break; + } + + case "fluidspread": + { + toggle("Fluid spread is", ConfigEntry.ALLOW_FLUID_SPREAD); + break; + } + + case "lavadmg": + { + toggle("Lava damage is", ConfigEntry.ALLOW_LAVA_DAMAGE); + break; + } + + case "firespread": + { + toggle("Fire spread is", ConfigEntry.ALLOW_FIRE_SPREAD); + plugin.gr.setGameRule(GameRuleHandler.GameRule.DO_FIRE_TICK, ConfigEntry.ALLOW_FIRE_SPREAD.getBoolean()); + break; + } + + case "prelog": + { + toggle("Command prelogging is", ConfigEntry.ENABLE_PREPROCESS_LOG); + break; + } + + case "lockdown": + { + boolean active = !LoginProcess.isLockdownEnabled(); + LoginProcess.setLockdownEnabled(active); + FUtil.adminAction(sender.getName(), (active ? "A" : "De-a") + "ctivating server lockdown", true); + break; + } + + case "petprotect": + { + toggle("Tamed pet protection is", ConfigEntry.ENABLE_PET_PROTECT); + break; + } + + case "entitywipe": + { + toggle("Automatic entity wiping is", ConfigEntry.AUTO_ENTITY_WIPE); + break; + } + + case "firework": + { + toggle("Firework explosion is", ConfigEntry.ALLOW_FIREWORK_EXPLOSION); + break; + } + + case "nonuke": + { + if (args.length >= 2) + { + try + { + ConfigEntry.NUKE_MONITOR_RANGE.setDouble(Math.max(1.0, Math.min(500.0, Double.parseDouble(args[1])))); + } + catch (NumberFormatException ex) + { + msg("The input provided is not a valid integer."); + return true; + } + } + + if (args.length >= 3) + { + try + { + ConfigEntry.NUKE_MONITOR_COUNT_BREAK.setInteger(Math.max(1, Math.min(500, Integer.parseInt(args[2])))); + } + catch (NumberFormatException ex) + { + msg("The input provided is not a valid integer."); + return true; + } + } + + toggle("Nuke monitor is", ConfigEntry.NUKE_MONITOR_ENABLED); + + if (ConfigEntry.NUKE_MONITOR_ENABLED.getBoolean()) + { + msg("Anti-freecam range is set to " + ConfigEntry.NUKE_MONITOR_RANGE.getDouble() + " blocks."); + msg("Block throttle rate is set to " + ConfigEntry.NUKE_MONITOR_COUNT_BREAK.getInteger() + " blocks destroyed per 5 seconds."); + } + break; + } + + case "explosives": + { + if (args.length == 2) + { + try + { + ConfigEntry.EXPLOSIVE_RADIUS.setDouble(Math.max(1.0, Math.min(30.0, Double.parseDouble(args[1])))); + } + catch (NumberFormatException ex) + { + msg("The input provided is not a valid integer."); + return true; + } + } + + toggle("Explosions are", ConfigEntry.ALLOW_EXPLOSIONS); + + if (ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()) + { + msg("Radius set to " + ConfigEntry.EXPLOSIVE_RADIUS.getDouble()); + } + break; + } + + case "unsafeenchs": + { + toggle("Unsafe enchantments are", ConfigEntry.ALLOW_UNSAFE_ENCHANTMENTS); + break; + } + + case "bells": + { + toggle("The ringing of bells is", ConfigEntry.ALLOW_BELLS); + break; + } + + case "armorstands": + { + toggle("The placement of armor stands is", ConfigEntry.ALLOW_ARMOR_STANDS); + break; + } + + case "structureblocks": + { + toggle("Structure blocks are", ConfigEntry.ALLOW_STRUCTURE_BLOCKS); + break; + } + + case "jigsaws": + { + toggle("Jigsaws are", ConfigEntry.ALLOW_JIGSAWS); + break; + } + + case "grindstones": + { + toggle("Grindstones are", ConfigEntry.ALLOW_GRINDSTONES); + break; + } + + case "jukeboxes": + { + toggle("Jukeboxes are", ConfigEntry.ALLOW_JUKEBOXES); + break; + } + + case "spawners": + { + toggle("Spawners are", ConfigEntry.ALLOW_SPAWNERS); + break; + } + + case "4chan": + { + toggle("4chan mode is", ConfigEntry.FOURCHAN_ENABLED); + break; + } + + case "beehives": + { + toggle("Beehives are", ConfigEntry.ALLOW_BEEHIVES); + break; + } + + case "respawnanchors": + { + toggle("Respawn anchors are", ConfigEntry.ALLOW_RESPAWN_ANCHORS); + break; + } + + case "autotp": + { + toggle("Teleportation on join is", ConfigEntry.AUTO_TP); + break; + } + + case "autoclear": + { + toggle("Clearing inventories on join is", ConfigEntry.AUTO_CLEAR); + break; + } + + case "minecarts": + { + toggle("Minecarts are", ConfigEntry.ALLOW_MINECARTS); + break; + } + + case "landmines": + { + toggle("Landmines are", ConfigEntry.LANDMINES_ENABLED); + break; + } + + case "mp44": + { + toggle("MP44 is", ConfigEntry.MP44_ENABLED); + break; + } + + case "tossmob": + { + toggle("Tossmob is", ConfigEntry.TOSSMOB_ENABLED); + break; + } + + case "gravity": + { + toggle("Block gravity is", ConfigEntry.ALLOW_GRAVITY); + break; + } + } + return true; + } + + private void toggle(final String name, final ConfigEntry entry) + { + msg(name + " now " + (entry.setBoolean(!entry.getBoolean()) ? "enabled." : "disabled.")); + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (!plugin.al.isAdmin(sender)) + { + return Collections.emptyList(); + } + + if (args.length == 1) + { + return Arrays.asList( + "waterplace", "fireplace", "lavaplace", "fluidspread", "lavadmg", "firespread", "frostwalk", + "firework", "prelog", "lockdown", "petprotect", "entitywipe", "nonuke", "explosives", "unsafeenchs", + "bells", "armorstands", "structureblocks", "jigsaws", "grindstones", "jukeboxes", "spawners", "4chan", "beehives", + "respawnanchors", "autotp", "autoclear", "minecarts", "mp44", "landmines", "tossmob", "gravity"); + } + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglechat.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglechat.java new file mode 100644 index 00000000..30a3fb28 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglechat.java @@ -0,0 +1,21 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle online players' ability to chat.", usage = "/", aliases = "tc") +public class Command_togglechat extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + ConfigEntry.TOGGLE_CHAT.setBoolean(!ConfigEntry.TOGGLE_CHAT.getBoolean()); + FUtil.adminAction(sender.getName(), "Chat " + (ConfigEntry.TOGGLE_CHAT.getBoolean() ? "enabled" : "disabled") + ".", true); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggleclownfish.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggleclownfish.java new file mode 100644 index 00000000..a4f05a39 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_toggleclownfish.java @@ -0,0 +1,36 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Toggle whether or not a player has the ability to use clownfish", usage = "/ ", aliases = "togglecf") +public class Command_toggleclownfish extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean enabled = plugin.lp.CLOWNFISH_TOGGLE.contains(args[0]); + + if (enabled) + { + plugin.lp.CLOWNFISH_TOGGLE.remove(args[0]); + } + else + { + plugin.lp.CLOWNFISH_TOGGLE.add(args[0]); + } + + msg(args[0] + " will " + (enabled ? "now" : "no longer") + " have the ability to use clownfish."); + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglediscord.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglediscord.java new file mode 100644 index 00000000..69397339 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglediscord.java @@ -0,0 +1,22 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Toggle the display of Discord messages in-game.", usage = "/", aliases = "tdiscord,tdisc") +public class Command_togglediscord extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + PlayerData data = plugin.pl.getData(playerSender); + data.setDisplayDiscord(!data.doesDisplayDiscord()); + plugin.pl.save(data); + msg("Discord messages will " + (data.doesDisplayDiscord() ? "now" : "no longer") + " be shown."); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglepickup.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglepickup.java new file mode 100644 index 00000000..df9c8294 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_togglepickup.java @@ -0,0 +1,22 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Toggle item pickup.", usage = "/") +public class Command_togglepickup extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean enabled = !playerSender.getCanPickupItems(); + playerSender.setCanPickupItems(enabled); + msg((enabled ? "En" : "Dis") + "abled item pickup.", (enabled ? ChatColor.GREEN : ChatColor.RED)); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tossmob.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tossmob.java new file mode 100644 index 00000000..28470eb6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tossmob.java @@ -0,0 +1,117 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Throw a mob in the direction you are facing when you right click with a bone.", + usage = "/ ") +public class Command_tossmob extends FreedomCommand +{ + + public static List getAllMobNames() + { + List names = new ArrayList<>(); + for (EntityType entityType : Groups.MOB_TYPES) + { + names.add(entityType.name()); + } + return names; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!ConfigEntry.TOSSMOB_ENABLED.getBoolean()) + { + msg("Tossmob is currently disabled."); + return true; + } + + if (args.length == 0) + { + return false; + } + + FPlayer playerData = plugin.pl.getPlayer(playerSender); + + EntityType type = null; + if (args[0].equalsIgnoreCase("off")) + { + playerData.disableMobThrower(); + msg("Turned off.", ChatColor.GREEN); + return true; + } + + if (args[0].equalsIgnoreCase("list")) + { + msg("Supported mobs: " + getAllMobNames(), ChatColor.GREEN); + return true; + } + + for (EntityType loop : EntityType.values()) + { + if (loop != null && loop.name().equalsIgnoreCase(args[0])) + { + type = loop; + break; + } + } + + if (type == null) + { + msg("Unknown entity type: " + args[0], ChatColor.RED); + return true; + } + + if (!Groups.MOB_TYPES.contains(type)) + { + msg(FUtil.formatName(type.name()) + " is an entity, however it is not a mob.", ChatColor.RED); + return true; + } + + double speed = 1.0; + if (args.length >= 2) + { + try + { + speed = Double.parseDouble(args[1]); + } + catch (NumberFormatException ex) + { + msg("The input provided is not a valid integer."); + return true; + } + } + + if (speed < 1.0) + { + speed = 1.0; + } + else if (speed > 5.0) + { + speed = 5.0; + } + + playerData.enableMobThrower(type, speed); + msg("MobThrower is enabled. Mob: " + type + " - Speed: " + speed + ".", ChatColor.GREEN); + msg("Right click while holding a " + Material.BONE.toString() + " to throw mobs!", ChatColor.GREEN); + msg("Type '/tossmob off' to disable. -By Madgeek1450", ChatColor.GREEN); + + Objects.requireNonNull(playerSender.getEquipment()).setItemInMainHand(new ItemStack(Material.BONE, 1)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_totalfreedommod.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_totalfreedommod.java new file mode 100644 index 00000000..aad66572 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_totalfreedommod.java @@ -0,0 +1,71 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/* + * See https://github.com/TotalFreedom/License - This file may not be edited or removed. + */ +@CommandPermissions(level = Rank.NON_OP, source = SourceType.BOTH) +@CommandParameters(description = "Shows information about TotalFreedomMod or reloads it", usage = "/ [reload]", aliases = "tfm") +public class Command_totalfreedommod extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 1) + { + if (!args[0].equals("reload")) + { + return false; + } + + if (!plugin.al.isAdmin(sender)) + { + noPerms(); + return true; + } + + plugin.config.load(); + + plugin.fsh.stopServices(); + plugin.fsh.startServices(); + + final String message = String.format("%s v%s reloaded.", + TotalFreedomMod.pluginName, + TotalFreedomMod.pluginVersion); + + msg(message); + FLog.info(message); + return true; + } + + TotalFreedomMod.BuildProperties build = TotalFreedomMod.build; + msg("TotalFreedomMod for 'Total Freedom', the original all-op server.", ChatColor.GOLD); + msg("Running on " + ConfigEntry.SERVER_NAME.getString() + ".", ChatColor.GOLD); + msg("Created by Madgeek1450 and Prozza.", ChatColor.GOLD); + msg(String.format("Version " + + ChatColor.BLUE + "%s - %s Build %s " + ChatColor.GOLD + "(" + + ChatColor.BLUE + "%s" + ChatColor.GOLD + ")", + build.codename, + build.version, + build.number, + build.head), ChatColor.GOLD); + msg(String.format("Compiled " + + ChatColor.BLUE + "%s" + ChatColor.GOLD + " by " + + ChatColor.BLUE + "%s", + build.date, + build.author), ChatColor.GOLD); + msg("Visit " + ChatColor.AQUA + "http://github.com/TotalFreedom/TotalFreedomMod" + + ChatColor.GREEN + " for more information.", ChatColor.GREEN); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tprandom.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tprandom.java new file mode 100644 index 00000000..ab4ef62e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_tprandom.java @@ -0,0 +1,27 @@ +package me.totalfreedom.totalfreedommod.command; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Go to a random location in the current world you are in", usage = "/", aliases = "tpr,rtp") +public class Command_tprandom extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + int x = FUtil.randomInteger(-50000, 50000); + int z = FUtil.randomInteger(-50000, 50000); + int y = playerSender.getWorld().getHighestBlockYAt(x, z); + Location location = new Location(playerSender.getLocation().getWorld(), x, y, z); + PaperLib.teleportAsync(playerSender, location); + msg("Poof!", ChatColor.GREEN); + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_trail.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_trail.java new file mode 100644 index 00000000..44129bd4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_trail.java @@ -0,0 +1,29 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Trails rainbow wool behind you as you walk/fly.", usage = "/") +public class Command_trail extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (plugin.tr.contains(playerSender)) + { + plugin.tr.remove(playerSender); + msg("Trail disabled."); + } + else + { + plugin.tr.add(playerSender); + msg("Trail enabled. Use \"/trail off\" to disable."); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unban.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unban.java new file mode 100644 index 00000000..b6ca9452 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unban.java @@ -0,0 +1,66 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Unbans the specified player.", usage = "/ [-r]") +public class Command_unban extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length > 0) + { + String username; + final PlayerData entry = plugin.pl.getData(args[0]); + + if (entry == null) + { + msg("Can't find that user. If target is not logged in, make sure that you spelled the name exactly."); + return true; + } + + username = entry.getName(); + final List ips = new ArrayList<>(entry.getIps()); + + FUtil.adminAction(sender.getName(), "Unbanning " + username, true); + msg(username + " has been unbanned along with the following IPs: " + StringUtils.join(ips, ", ")); + plugin.bm.removeBan(plugin.bm.getByUsername(username)); + + if (args.length >= 2) + { + if (args[1].equalsIgnoreCase("-r")) + { + plugin.cpb.restore(username); + msg("Restored edits for: " + username); + } + } + + for (String ip : ips) + { + Ban ban = plugin.bm.getByIp(ip); + if (ban != null) + { + plugin.bm.removeBan(ban); + } + ban = plugin.bm.getByIp(FUtil.getFuzzyIp(ip)); + if (ban != null) + { + plugin.bm.removeBan(ban); + } + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanip.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanip.java new file mode 100644 index 00000000..03cbaef7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanip.java @@ -0,0 +1,62 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Unbans the specified ip.", usage = "/ [-q]") +public class Command_unbanip extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean silent = false; + + String ip = args[0]; + + if (FUtil.isValidIPv4(ip)) + { + msg(ip + " is not a valid IP address", ChatColor.RED); + return true; + } + + Ban ban = plugin.bm.getByIp(ip); + + if (ban == null) + { + msg("The ip " + ip + " is not banned", ChatColor.RED); + return true; + } + + if (ban.hasUsername()) + { + msg("This ban is not an ip-only ban."); + return true; + } + + if (args.length > 1 && args[1].equals("-q")) + { + silent = true; + } + + plugin.bm.removeBan(ban); + + if (!silent) + { + FUtil.adminAction(sender.getName(), "Unbanned the ip " + ip, true); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanname.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanname.java new file mode 100644 index 00000000..cf925674 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unbanname.java @@ -0,0 +1,56 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Unbans the specified name.", usage = "/ [-q]") +public class Command_unbanname extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean silent = false; + + String name = args[0]; + + Ban ban = plugin.bm.getByUsername(name); + + if (ban == null) + { + msg("The name " + name + " is not banned", ChatColor.RED); + return true; + } + + if (ban.hasIps()) + { + msg("This ban is not a name-only ban."); + return true; + } + + if (args.length > 1 && args[1].equals("-q")) + { + silent = true; + } + + plugin.bm.removeBan(ban); + + if (!silent) + { + FUtil.adminAction(sender.getName(), "Unbanned the name " + name, true); + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unblockcmd.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unblockcmd.java new file mode 100644 index 00000000..d6f551bb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unblockcmd.java @@ -0,0 +1,44 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Unblocks commands for a player.", usage = "/ ", aliases = "unblockcommand,unblockcommands,ubcmds,unblockcmds,ubc") +public class Command_unblockcmd extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + FPlayer fPlayer = plugin.pl.getPlayer(player); + if (fPlayer.allCommandsBlocked()) + { + fPlayer.setCommandsBlocked(false); + FUtil.adminAction(sender.getName(), "Unblocking all commands for " + player.getName(), true); + msg("Unblocked commands for " + player.getName() + "."); + } + else + { + msg("That players commands aren't blocked.", ChatColor.RED); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_uncage.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_uncage.java new file mode 100644 index 00000000..ccfd1d1d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_uncage.java @@ -0,0 +1,44 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Uncage a player", usage = "/ ") +public class Command_uncage extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + + if (args.length == 0) + { + return false; + } + + Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + final FPlayer fPlayer = plugin.pl.getPlayer(player); + if (fPlayer.getCageData().isCaged()) + { + FUtil.adminAction(sender.getName(), "Uncaging " + player.getName(), true); + fPlayer.getCageData().setCaged(false); + } + else + { + msg("That player is not caged!", ChatColor.RED); + } + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_undisguiseall.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_undisguiseall.java new file mode 100644 index 00000000..ba510c73 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_undisguiseall.java @@ -0,0 +1,35 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Undisguise all online players on the server", usage = "/ [-a]", aliases = "uall") +public class Command_undisguiseall extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.ldb.isEnabled()) + { + msg("LibsDisguises is not enabled."); + return true; + } + + boolean admins = false; + + if (args.length > 0 && args[0].equalsIgnoreCase("-a")) + { + admins = true; + } + + FUtil.adminAction(sender.getName(), "Undisguising all " + (admins ? "players" : "non-admins"), true); + + plugin.ldb.undisguiseAll(admins); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unlinkdiscord.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unlinkdiscord.java new file mode 100644 index 00000000..922db3a4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unlinkdiscord.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Unlink your Discord account from your Minecraft account", usage = "/ [player]") +public class Command_unlinkdiscord extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (args.length != 0 && plugin.al.isAdmin(playerSender)) + { + PlayerData playerData = plugin.pl.getData(args[0]); + if (playerData == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + playerData.setDiscordID(null); + msg("Unlinked " + args[0] + "'s discord account.", ChatColor.GREEN); + return true; + } + + PlayerData data = plugin.pl.getData(playerSender); + if (data.getDiscordID() == null) + { + msg("Your Minecraft account is not linked to a Discord account.", ChatColor.RED); + return true; + } + data.setDiscordID(null); + data.setVerification(false); + plugin.pl.save(data); + msg("Your Minecraft account has been successfully unlinked from the Discord account.", ChatColor.GREEN); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unmute.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unmute.java new file mode 100644 index 00000000..725f6146 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_unmute.java @@ -0,0 +1,66 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Unmutes a player", usage = "/ [-q] ") +public class Command_unmute extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + // -q option (shadowmute) + boolean quiet = args[0].equals("-q"); + if (quiet) + { + args = ArrayUtils.subarray(args, 1, args.length); + + if (args.length < 1) + { + return false; + } + } + + final Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + FPlayer playerdata = plugin.pl.getPlayer(player); + if (playerdata.isMuted()) + { + playerdata.setMuted(false); + player.sendTitle(ChatColor.RED + "You've been unmuted.", ChatColor.YELLOW + "Be sure to follow the rules!", 20, 100, 60); + + if (quiet) + { + msg("Unmuted " + player.getName() + " quietly"); + return true; + } + + FUtil.adminAction(sender.getName(), "Unmuting " + player.getName(), true); + msg("Unmuted " + player.getName()); + msg(player, "You have been unmuted.", ChatColor.RED); + } + else + { + msg(ChatColor.RED + "That player is not muted."); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vanish.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vanish.java new file mode 100644 index 00000000..68ce1b41 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vanish.java @@ -0,0 +1,113 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.admin.AdminList; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.ONLY_IN_GAME) +@CommandParameters(description = "Vanish/unvanish yourself.", usage = "/ [-s[ilent]]", aliases = "v") +public class Command_vanish extends FreedomCommand +{ + + public boolean run(final CommandSender sender, final Player playerSender, final Command cmd, final String commandLabel, final String[] args, final boolean senderIsConsole) + { + Displayable display = plugin.rm.getDisplay(playerSender); + String displayName = display.getColor() + playerSender.getName(); + String tag = display.getColoredTag(); + boolean silent = false; + if (args.length > 0) + { + if (args[0].equalsIgnoreCase("-s") || args[0].equalsIgnoreCase("-silent")) + { + silent = true; + } + } + + if (plugin.al.isVanished(playerSender.getName())) + { + if (silent) + { + msg(ChatColor.GOLD + "Silently unvanished."); + } + else + { + msg("You have unvanished.", ChatColor.GOLD); + FUtil.bcastMsg(plugin.rm.craftLoginMessage(playerSender, null)); + FUtil.bcastMsg(playerSender.getName() + " joined the game.", ChatColor.YELLOW); + plugin.dc.messageChatChannel("**" + playerSender.getName() + " joined the server" + "**"); + } + + PlayerData playerData = plugin.pl.getData(playerSender); + if (playerData.getTag() != null) + { + tag = FUtil.colorize(playerData.getTag()); + } + + plugin.pl.getData(playerSender).setTag(tag); + FLog.info(playerSender.getName() + " is no longer vanished."); + plugin.al.messageAllAdmins(ChatColor.YELLOW + sender.getName() + " has unvanished and is now visible to everyone."); + + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + player.showPlayer(plugin, playerSender); + } + } + plugin.esb.setVanished(playerSender.getName(), false); + playerSender.setPlayerListName(StringUtils.substring(displayName, 0, 16)); + AdminList.vanished.remove(playerSender.getName()); + } + else + { + new BukkitRunnable() + { + @Override + public void run() + { + if (plugin.al.isVanished(playerSender.getName())) + { + playerSender.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(ChatColor.GOLD + "You are hidden from other players.")); + } + } + }.runTaskTimer(plugin, 0L, 4L); + + if (silent) + { + msg("Silently vanished.", ChatColor.GOLD); + } + else + { + msg("You have vanished.", ChatColor.GOLD); + FUtil.bcastMsg(playerSender.getName() + " left the game.", ChatColor.YELLOW); + plugin.dc.messageChatChannel("**" + playerSender.getName() + " left the server" + "**"); + } + + FLog.info(playerSender.getName() + " is now vanished."); + plugin.al.messageAllAdmins(ChatColor.YELLOW + sender.getName() + " has vanished and is now only visible to admins."); + + for (Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(player)) + { + player.hidePlayer(plugin, playerSender); + } + } + + plugin.esb.setVanished(playerSender.getName(), true); + AdminList.vanished.add(playerSender.getName()); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java new file mode 100644 index 00000000..fb33dca6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verify.java @@ -0,0 +1,101 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.IMPOSTOR, source = SourceType.BOTH) +@CommandParameters(description = "Sends a verification code to the player, or the player can input the sent code. Admins can manually verify a player impostor.", usage = "/ >") +public class Command_verify extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + boolean verificationEnabled = ConfigEntry.DISCORD_VERIFICATION.getBoolean(); + if (!plugin.dc.enabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (!verificationEnabled) + { + msg("The Discord verification system is currently disabled.", ChatColor.RED); + return true; + } + + if (senderIsConsole) + { + msg("/manuallyverify ", ChatColor.WHITE); + return true; + } + + if (!plugin.pl.IsImpostor(playerSender)) + { + msg("You are not an impostor, therefore you do not need to verify.", ChatColor.RED); + return true; + } + + PlayerData playerData = plugin.pl.getData(playerSender); + String discordId = playerData.getDiscordID(); + + if (playerData.getDiscordID() == null) + { + msg("You do not have a Discord account linked to your Minecraft account, please verify the manual way.", ChatColor.RED); + return true; + } + + if (args.length == 0) + { + String code = plugin.dc.generateCode(10); + plugin.dc.addVerificationCode(code, playerData); + plugin.dc.getUser(discordId).openPrivateChannel().complete().sendMessage("A user with the IP `" + FUtil.getIp(playerSender) + "` has sent a verification request. Please run the following in-game command: `/verify " + code + "`").complete(); + msg("A verification code has been sent to your account, please copy the code and run /verify ", ChatColor.GREEN); + return true; + } + + String code = args[0]; + String backupCode = null; + + if (plugin.pl.IsImpostor(playerSender)) + { + PlayerData mapPlayer = plugin.dc.getVerificationCodes().get(code); + if (mapPlayer == null) + { + if (!playerData.getBackupCodes().contains(Discord.getMD5(code))) + { + msg("You have entered an invalid verification code", ChatColor.RED); + return true; + } + else + { + backupCode = Discord.getMD5(code); + } + } + else + { + plugin.dc.removeVerificationCode(code); + } + + final FPlayer fPlayer = plugin.pl.getPlayer(playerSender); + if (fPlayer.getFreezeData().isFrozen()) + { + fPlayer.getFreezeData().setFrozen(false); + msg("You have been unfrozen."); + } + FUtil.bcastMsg(playerSender.getName() + " has verified!", ChatColor.GOLD); + playerSender.setOp(true); + plugin.pl.verify(playerSender, backupCode); + return true; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verifynoadmin.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verifynoadmin.java new file mode 100644 index 00000000..426566f1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_verifynoadmin.java @@ -0,0 +1,92 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Verify an admin without giving them admin permissions.", usage = "/ ", aliases = "vns,verifynostaff,vna") +public class Command_verifynoadmin extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + Player player = getPlayer(args[0]); + + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (plugin.al.isAdminImpostor(player)) + { + String ip = FUtil.getIp(player); + if (!plugin.al.verifiedNoAdmin.containsKey(player.getName())) + { + List ips = new ArrayList<>(); + ips.add(ip); + plugin.al.verifiedNoAdmin.put(player.getName(), ips); + } + else + { + List ips = plugin.al.verifiedNoAdmin.get(player.getName()); + if (!ips.contains(ip)) + { + ips.add(ip); + plugin.al.verifiedNoAdmin.remove(player.getName()); + plugin.al.verifiedNoAdmin.put(player.getName(), ips); + } + } + plugin.rm.updateDisplay(player); + FUtil.adminAction(sender.getName(), "Verified " + player.getName() + ", without admin permissions.", true); + player.setOp(true); + msg(player, YOU_ARE_OP); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + if (fPlayer.getFreezeData().isFrozen()) + { + fPlayer.getFreezeData().setFrozen(false); + msg(player, "You have been unfrozen."); + } + msg("Verified " + player.getName() + " but didn't give them admin permissions", ChatColor.GREEN); + } + else + { + msg(player.getName() + " is not an admin imposter.", ChatColor.RED); + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + List adminImposters = new ArrayList<>(); + for (Player player : server.getOnlinePlayers()) + { + if (plugin.al.isAdminImpostor(player)) + { + adminImposters.add(player.getName()); + } + } + return adminImposters; + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vote.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vote.java new file mode 100644 index 00000000..3f1df406 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_vote.java @@ -0,0 +1,34 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.List; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Information on how to vote", usage = "/") +public class Command_vote extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + List voteInfo = ConfigEntry.VOTING_INFO.getStringList(); + + if (voteInfo.isEmpty()) + { + msg("The voting information section of the config.yml file has not been configured.", ChatColor.RED); + } + else + { + msg(FUtil.colorize(StringUtils.join(voteInfo, "\n"))); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_warn.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_warn.java new file mode 100644 index 00000000..827a5b04 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_warn.java @@ -0,0 +1,77 @@ +package me.totalfreedom.totalfreedommod.command; + +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH) +@CommandParameters(description = "Warns the specified player.", usage = "/ [-q] ") +public class Command_warn extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + boolean quiet = args[0].equalsIgnoreCase("-q"); + if (quiet) + { + args = org.apache.commons.lang3.ArrayUtils.subarray(args, 1, args.length); + + if (args.length < 1) + { + return false; + } + } + + Player player = getPlayer(args[0]); + if (player == null) + { + msg(PLAYER_NOT_FOUND); + return true; + } + + if (playerSender == player) + { + msg(ChatColor.RED + "Please, don't try to warn yourself."); + return true; + } + + if (plugin.al.isAdmin(player)) + { + msg(ChatColor.RED + "You can not warn admins"); + return true; + } + + String warnReason = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); + player.sendTitle(ChatColor.RED + "You've been warned.", ChatColor.YELLOW + "Reason: " + warnReason, 20, 100, 60); + msg(ChatColor.GREEN + "You have successfully warned " + player.getName()); + + if (quiet) + { + msg("Warned " + player.getName() + " quietly"); + return true; + } + + msg(player, ChatColor.RED + "[WARNING] You received a warning from " + sender.getName() + ": " + warnReason); + String adminNotice = ChatColor.RED + + sender.getName() + + " - " + + "Warning: " + + player.getName() + + " - Reason: " + + ChatColor.YELLOW + + warnReason; + plugin.al.messageAllAdmins(adminNotice); + plugin.pl.getPlayer(player).incrementWarnings(); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whitelist.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whitelist.java new file mode 100644 index 00000000..b07356e7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whitelist.java @@ -0,0 +1,207 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.DepreciationAggregator; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "Manage the whitelist.", usage = "/ | remove | addall | purge>") +public class Command_whitelist extends FreedomCommand +{ + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + // list + if (args[0].equalsIgnoreCase("list")) + { + if (server.getWhitelistedPlayers().isEmpty()) + { + msg("There are no whitelisted players."); + return true; + } + msg("Whitelisted players: " + FUtil.playerListToNames(server.getWhitelistedPlayers())); + return true; + } + + // count + if (args[0].equalsIgnoreCase("count")) + { + int onlineWPs = 0; + int offlineWPs = 0; + int totalWPs = 0; + + for (OfflinePlayer player : server.getWhitelistedPlayers()) + { + if (player.isOnline()) + { + onlineWPs++; + } + else + { + offlineWPs++; + } + totalWPs++; + } + + msg("Online whitelisted players: " + onlineWPs); + msg("Offline whitelisted players: " + offlineWPs); + msg("Total whitelisted players: " + totalWPs); + return true; + } + + // Commands below are restricted to admins + checkRank(Rank.ADMIN); + + // on + if (args[0].equalsIgnoreCase("on")) + { + FUtil.adminAction(sender.getName(), "Turning the whitelist on", true); + server.setWhitelist(true); + return true; + } + + // off + if (args[0].equalsIgnoreCase("off")) + { + FUtil.adminAction(sender.getName(), "Turning the whitelist off", true); + server.setWhitelist(false); + return true; + } + + // add + if (args[0].equalsIgnoreCase("add")) + { + if (args.length < 2) + { + return false; + } + + String search_name = args[1].trim().toLowerCase(); + + OfflinePlayer player = getPlayer(search_name); + + if (player == null) + { + player = DepreciationAggregator.getOfflinePlayer(server, search_name); + } + + FUtil.adminAction(sender.getName(), "Adding " + player.getName() + " to the whitelist", false); + player.setWhitelisted(true); + return true; + } + + // remove + if (args[0].equalsIgnoreCase("remove")) + { + if (args.length < 2) + { + return false; + } + + String search_name = args[1].trim().toLowerCase(); + + OfflinePlayer player = getPlayer(search_name); + + if (player == null) + { + player = DepreciationAggregator.getOfflinePlayer(server, search_name); + } + + if (player.isWhitelisted()) + { + FUtil.adminAction(sender.getName(), "Removing " + player.getName() + " from the whitelist", false); + player.setWhitelisted(false); + } + else + { + msg("That player is not whitelisted"); + } + return true; + } + + // addall + if (args[0].equalsIgnoreCase("addall")) + { + FUtil.adminAction(sender.getName(), "Adding all online players to the whitelist", false); + int counter = 0; + for (Player player : server.getOnlinePlayers()) + { + if (!player.isWhitelisted()) + { + player.setWhitelisted(true); + counter++; + } + } + + msg("Whitelisted " + counter + " players."); + return true; + } + + // Telnet only + checkConsole(); + checkRank(Rank.ADMIN); + + // purge + if (args[0].equalsIgnoreCase("purge")) + { + FUtil.adminAction(sender.getName(), "Removing all players from the whitelist", false); + msg("Removed " + plugin.si.purgeWhitelist() + " players from the whitelist."); + return true; + } + return false; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + List arguments = new ArrayList<>(Arrays.asList("list", "count")); + if (plugin.al.isAdmin(sender)) + { + arguments.addAll(Arrays.asList("on", "off", "add", "remove", "addall")); + if (!(sender instanceof Player)) + { + arguments.add("purge"); + } + } + return arguments; + } + else if (args.length == 2 && plugin.al.isAdmin(sender)) + { + if (args[0].equals("add")) + { + return FUtil.getPlayerList(); + } + else if (args[0].equals("remove")) + { + return getWhitelistedNames(); + } + } + + return Collections.emptyList(); + } + + public List getWhitelistedNames() + { + List names = new ArrayList<>(); + for (Object name : plugin.si.getWhitelisted()) + { + names.add(String.valueOf(name)); + } + return names; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whohas.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whohas.java new file mode 100644 index 00000000..ac819030 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_whohas.java @@ -0,0 +1,96 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.OP, source = SourceType.BOTH) +@CommandParameters(description = "See who has an item and optionally clear the specified item.", usage = "/ [clear]", aliases = "wh") +public class Command_whohas extends FreedomCommand +{ + + public static List getAllMaterials() + { + List names = new ArrayList<>(); + for (Material material : Material.values()) + { + names.add(material.name()); + } + return names; + } + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length < 1) + { + return false; + } + + final boolean doClear = args.length >= 2 && "clear".equalsIgnoreCase(args[1]); + + final String materialName = args[0]; + Material material = Material.matchMaterial(materialName); + + if (material == null) + { + msg("Invalid item: " + materialName, ChatColor.RED); + return true; + } + + final List players = new ArrayList<>(); + + for (final Player player : server.getOnlinePlayers()) + { + if (!plugin.al.isAdmin(sender) && plugin.al.isVanished(player.getName())) + { + continue; + } + if (player.getInventory().contains(material)) + { + players.add(player.getName()); + if (plugin.al.isAdmin(sender)) + { + if (doClear && !plugin.al.isAdmin(player)) + { + player.getInventory().remove(material); + } + } + } + } + + if (players.isEmpty()) + { + msg("There are no players with that item"); + } + else + { + msg("Players with item " + material.name() + ": " + StringUtils.join(players, ", ")); + } + + return true; + } + + @Override + public List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + if (args.length == 1) + { + return getAllMaterials(); + } + + if (args.length == 2 && plugin.al.isAdmin(sender)) + { + return Collections.singletonList("clear"); + } + + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_wildcard.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_wildcard.java new file mode 100644 index 00000000..a4d74e4c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_wildcard.java @@ -0,0 +1,75 @@ +package me.totalfreedom.totalfreedommod.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandPermissions(level = Rank.ADMIN, source = SourceType.BOTH, blockHostConsole = true) +@CommandParameters(description = "Run any command on all users, username placeholder = ?.", usage = "/ [fluff] ? [fluff] ?") +public class Command_wildcard extends FreedomCommand +{ + + @Override + public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) + { + if (args.length == 0) + { + return false; + } + + Command runCmd = server.getPluginCommand(args[0]); + FreedomCommand fCmd = plugin.cl.getByName(args[0]); + boolean alias = plugin.cl.isAlias(args[0]); + if (runCmd == null && fCmd == null && !alias) + { + msg("Unknown command: " + args[0], ChatColor.RED); + return true; + } + + List aliases = new ArrayList<>(); + + if (runCmd != null) + { + aliases = runCmd.getAliases(); + } + + if (fCmd != null) + { + aliases = Arrays.asList(fCmd.getAliases().split(",")); + } + + for (String blockedCommand : ConfigEntry.WILDCARD_BLOCKED_COMMANDS.getStringList()) + { + if (blockedCommand.equals(args[0].toLowerCase()) || aliases.contains(blockedCommand)) + { + msg("Did you really think that was going to work?", ChatColor.RED); + return true; + } + } + + String baseCommand = StringUtils.join(args, " "); + + if (plugin.cb.isCommandBlocked(baseCommand, sender)) + { + // CommandBlocker handles messages and broadcasts + return true; + } + + for (Player player : server.getOnlinePlayers()) + { + String runCommand = baseCommand.replaceAll("\\x3f", player.getName()); + msg("Running Command: " + runCommand); + server.dispatchCommand(sender, runCommand); + } + + return true; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java new file mode 100644 index 00000000..8d2dd093 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java @@ -0,0 +1,431 @@ +package me.totalfreedom.totalfreedommod.command; + +import com.google.common.collect.Lists; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; +import org.jetbrains.annotations.NotNull; + +public abstract class FreedomCommand implements CommandExecutor, TabCompleter +{ + public static final String COMMAND_PREFIX = "Command_"; + public static final String YOU_ARE_OP = ChatColor.YELLOW + "You are now op!"; + public static final String YOU_ARE_NOT_OP = ChatColor.YELLOW + "You are no longer op!"; + public static final String PLAYER_NOT_FOUND = ChatColor.GRAY + "Player not found!"; + public static final String ONLY_CONSOLE = ChatColor.RED + "Only console senders may execute this command!"; + public static final String ONLY_IN_GAME = ChatColor.RED + "Only in-game players may execute this command!"; + public static final String NO_PERMISSION = ChatColor.RED + "You do not have permission to execute this command."; + public static final Timer timer = new Timer(); + public static final Map COOLDOWN_TIMERS = new HashMap<>(); + private static CommandMap commandMap; + protected final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + protected final Server server = plugin.getServer(); + private final String name; + private final String description; + private final String usage; + private final String aliases; + private final Rank level; + private final SourceType source; + private final boolean blockHostConsole; + private final int cooldown; + private final CommandParameters params; + private final CommandPermissions perms; + protected CommandSender sender; + + FreedomCommand() + { + params = getClass().getAnnotation(CommandParameters.class); + perms = getClass().getAnnotation(CommandPermissions.class); + this.name = getClass().getSimpleName().replace(COMMAND_PREFIX, "").toLowerCase(); + this.description = params.description(); + this.usage = params.usage(); + this.aliases = params.aliases(); + this.level = perms.level(); + this.source = perms.source(); + this.blockHostConsole = perms.blockHostConsole(); + this.cooldown = perms.cooldown(); + } + + public static CommandMap getCommandMap() + { + if (commandMap == null) + { + try + { + final Field f = Bukkit.getServer().getPluginManager().getClass().getDeclaredField("commandMap"); + f.setAccessible(true); + commandMap = (CommandMap)f.get(Bukkit.getServer().getPluginManager()); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + return commandMap; + } + + public static FreedomCommand getFrom(Command command) + { + try + { + return (FreedomCommand)(((PluginCommand)command).getExecutor()); + } + catch (Exception ex) + { + return null; + } + } + + public static String getCommandPrefix() + { + return COMMAND_PREFIX; + } + + public void register() + { + FCommand cmd = new FCommand(this.name); + if (this.aliases != null) + { + cmd.setAliases(Arrays.asList(StringUtils.split(this.aliases, ","))); + } + if (this.description != null) + { + cmd.setDescription(this.description); + } + if (this.usage != null) + { + cmd.setUsage(this.usage); + } + getCommandMap().register("totalfreedommod", cmd); + cmd.setExecutor(this); + } + + protected void msg(CommandSender sender, String message) + { + sender.sendMessage(ChatColor.GRAY + message); + } + + protected void msg(Player player, String message) + { + player.sendMessage(ChatColor.GRAY + message); + } + + protected void msg(Player player, String message, ChatColor color) + { + player.sendMessage(color + message); + } + + protected void msg(String message) + { + msg(sender, message); + } + + protected void msg(String message, ChatColor color) + { + msg(color + message); + } + + protected void msg(String message, net.md_5.bungee.api.ChatColor color) + { + msg(color + message); + } + + protected boolean isAdmin(Player player) + { + return plugin.al.isAdmin(player); + } + + protected boolean isAdmin(CommandSender sender) + { + return plugin.al.isAdmin(sender); + } + + protected void checkConsole() + { + if (!isConsole()) + { + throw new CommandFailException(ONLY_CONSOLE); + } + } + + protected void checkPlayer() + { + if (isConsole()) + { + throw new CommandFailException(ONLY_IN_GAME); + } + } + + protected void checkRank(Rank rank) + { + if (!plugin.rm.getRank(sender).isAtLeast(rank)) + { + noPerms(); + } + } + + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String commandLabel, @NotNull String[] args) + { + try + { + boolean run = run(sender, sender instanceof ConsoleCommandSender ? null : (Player)sender, cmd, commandLabel, args, sender instanceof ConsoleCommandSender); + if (!run) + { + msg(ChatColor.WHITE + cmd.getUsage().replace("", cmd.getLabel())); + return true; + } + } + catch (CommandFailException ex) + { + msg(ChatColor.RED + ex.getMessage()); + } + return false; + } + + @NotNull + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) + { + List options = getTabCompleteOptions(sender, command, alias, args); + if (options == null) + { + return new ArrayList<>(); + } + return StringUtil.copyPartialMatches(args[args.length - 1], options, Lists.newArrayList()); + } + + public abstract boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole); + + protected List getTabCompleteOptions(CommandSender sender, Command command, String alias, String[] args) + { + return FUtil.getPlayerList(); + } + + protected boolean isConsole() + { + return sender instanceof ConsoleCommandSender; + } + + protected Player getPlayer(String name) + { + return Bukkit.getPlayer(name); + } + + protected Player getPlayer(String name, Boolean nullVanished) + { + Player player = Bukkit.getPlayer(name); + if (player != null) + { + if (nullVanished && plugin.al.isVanished(player.getName()) && !plugin.al.isAdmin(sender)) + { + return null; + } + } + return player; + } + + protected Admin getAdmin(CommandSender sender) + { + return plugin.al.getAdmin(sender); + } + + protected Admin getAdmin(Player player) + { + return plugin.al.getAdmin(player); + } + + protected PlayerData getData(Player player) + { + return plugin.pl.getData(player); + } + + protected boolean noPerms() + { + throw new CommandFailException(NO_PERMISSION); + } + + public String getName() + { + return name; + } + + public String getDescription() + { + return description; + } + + public String getUsage() + { + return usage; + } + + public String getAliases() + { + return aliases; + } + + public Rank getLevel() + { + return level; + } + + public SourceType getSource() + { + return source; + } + + public boolean isBlockHostConsole() + { + return blockHostConsole; + } + + public int getCooldown() + { + return cooldown; + } + + public CommandParameters getParams() + { + return params; + } + + public CommandPermissions getPerms() + { + return perms; + } + + private final class FCommand extends Command + { + private FreedomCommand cmd = null; + + private FCommand(String command) + { + super(command); + } + + public void setExecutor(FreedomCommand cmd) + { + this.cmd = cmd; + } + + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, String[] args) + { + if (cmd != null) + { + cmd.sender = sender; + + if (func4()) + { + return true; + } + + if (func1()) + { + return true; + } + + if (func2()) + { + return true; + } + + func3(); + + return cmd.onCommand(sender, this, commandLabel, args); + } + return false; + } + + public boolean func1() + { + if (perms.source() == SourceType.ONLY_CONSOLE && sender instanceof Player) + { + msg(ONLY_CONSOLE); + return true; + } + + if (perms.source() == SourceType.ONLY_IN_GAME && sender instanceof ConsoleCommandSender) + { + msg(ONLY_IN_GAME); + return true; + } + + return false; + } + + public boolean func2() + { + if (!plugin.rm.getRank(sender).isAtLeast(perms.level())) + { + msg(NO_PERMISSION); + return true; + } + + if (perms.blockHostConsole() && FUtil.isFromHostConsole(sender.getName()) && !FUtil.inDeveloperMode()) + { + msg(ChatColor.RED + "Host console is not allowed to use this command!"); + return true; + } + return false; + } + + public void func3() + { + if (perms.cooldown() != 0 && !isAdmin(sender)) + { + COOLDOWN_TIMERS.put(sender, cmd); + timer.schedule(new TimerTask() + { + @Override + public void run() + { + COOLDOWN_TIMERS.remove(sender); + } + }, perms.cooldown() * 1000L); + } + } + + public boolean func4() + { + if (COOLDOWN_TIMERS.containsKey(sender) && COOLDOWN_TIMERS.containsValue(cmd)) + { + msg(ChatColor.RED + "You are on cooldown for this command."); + return true; + } + return false; + } + + @NotNull + @Override + public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, String[] args) + { + if (cmd != null) + { + return cmd.onTabComplete(sender, this, alias, args); + } + return new ArrayList<>(); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/SourceType.java b/src/main/java/me/totalfreedom/totalfreedommod/command/SourceType.java new file mode 100644 index 00000000..20dc8211 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/SourceType.java @@ -0,0 +1,6 @@ +package me.totalfreedom.totalfreedommod.command; + +public enum SourceType +{ + ONLY_IN_GAME, ONLY_CONSOLE, BOTH +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java new file mode 100644 index 00000000..05051905 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java @@ -0,0 +1,256 @@ +package me.totalfreedom.totalfreedommod.config; + +import java.util.List; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; + + +public enum ConfigEntry +{ + FORCE_IP_ENABLED(Boolean.class, "forceip.enabled"), + FORCE_IP_PORT(Integer.class, "forceip.port"), + FORCE_IP_KICKMSG(String.class, "forceip.kickmsg"), + // + ALLOW_EXPLOSIONS(Boolean.class, "allow.explosions"), + ALLOW_FIRE_PLACE(Boolean.class, "allow.fire_place"), + ALLOW_FIRE_SPREAD(Boolean.class, "allow.fire_spread"), + ALLOW_FLUID_SPREAD(Boolean.class, "allow.fluid_spread"), + ALLOW_LAVA_DAMAGE(Boolean.class, "allow.lava_damage"), + ALLOW_LAVA_PLACE(Boolean.class, "allow.lava_place"), + ALLOW_TNT_MINECARTS(Boolean.class, "allow.tnt_minecarts"), + ALLOW_WATER_PLACE(Boolean.class, "allow.water_place"), + ALLOW_REDSTONE(Boolean.class, "allow.redstone"), + ALLOW_FIREWORK_EXPLOSION(Boolean.class, "allow.fireworks"), + ALLOW_FROSTWALKER(Boolean.class, "allow.frostwalker"), + ALLOW_UNSAFE_ENCHANTMENTS(Boolean.class, "allow.unsafe_enchantments"), + ALLOW_BELLS(Boolean.class, "allow.bells"), + ALLOW_ARMOR_STANDS(Boolean.class, "allow.armorstands"), + ALLOW_MINECARTS(Boolean.class, "allow.minecarts"), + ALLOW_STRUCTURE_BLOCKS(Boolean.class, "allow.structureblocks"), + ALLOW_JIGSAWS(Boolean.class, "allow.jigsaws"), + ALLOW_GRINDSTONES(Boolean.class, "allow.grindstones"), + ALLOW_JUKEBOXES(Boolean.class, "allow.jukeboxes"), + ALLOW_SPAWNERS(Boolean.class, "allow.spawners"), + ALLOW_BEEHIVES(Boolean.class, "allow.beehives"), + ALLOW_RESPAWN_ANCHORS(Boolean.class, "allow.respawnanchors"), + AUTO_TP(Boolean.class, "allow.auto_tp"), + AUTO_CLEAR(Boolean.class, "allow.auto_clear"), + ALLOW_GRAVITY(Boolean.class, "allow.gravity"), + // + BLOCKED_CHATCODES(String.class, "blocked_chatcodes"), + // + MOB_LIMITER_ENABLED(Boolean.class, "moblimiter.enabled"), + MOB_LIMITER_MAX(Integer.class, "moblimiter.max"), + MOB_LIMITER_DISABLE_DRAGON(Boolean.class, "moblimiter.disable.dragon"), + MOB_LIMITER_DISABLE_GHAST(Boolean.class, "moblimiter.disable.ghast"), + MOB_LIMITER_DISABLE_GIANT(Boolean.class, "moblimiter.disable.giant"), + MOB_LIMITER_DISABLE_SLIME(Boolean.class, "moblimiter.disable.slime"), + // + HTTPD_ENABLED(Boolean.class, "httpd.enabled"), + HTTPD_HOST(String.class, "httpd.host"), + HTTPD_PORT(Integer.class, "httpd.port"), + HTTPD_PUBLIC_FOLDER(String.class, "httpd.public_folder"), + // + SERVER_COLORFUL_MOTD(Boolean.class, "server.colorful_motd"), + SERVER_NAME(String.class, "server.name"), + SERVER_ADDRESS(String.class, "server.address"), + SERVER_MOTD(String.class, "server.motd"), + SERVER_LOGIN_TITLE(String.class, "server.login_title.title"), + SERVER_LOGIN_SUBTITLE(String.class, "server.login_title.subtitle"), + SERVER_OWNERS(List.class, "server.owners"), + SERVER_EXECUTIVES(List.class, "server.executives"), + SERVER_ASSISTANT_EXECUTIVES(List.class, "server.assistant_executives"), + SERVER_MASTER_BUILDER_MANAGEMENT(List.class, "server.master_builder_management"), + SERVER_BAN_URL(String.class, "server.ban_url"), + SERVER_INDEFBAN_URL(String.class, "server.indefban_url"), + SERVER_TABLIST_HEADER(String.class, "server.tablist_header"), + SERVER_TABLIST_FOOTER(String.class, "server.tablist_footer"), + // + SERVER_BAN_MOTD(String.class, "server.motds.ban"), + SERVER_ADMINMODE_MOTD(String.class, "server.motds.adminmode"), + SERVER_LOCKDOWN_MOTD(String.class, "server.motds.lockdown"), + SERVER_WHITELIST_MOTD(String.class, "server.motds.whitelist"), + SERVER_FULL_MOTD(String.class, "server.motds.full"), + // + DISCORD_VERIFICATION(Boolean.class, "discord.verification"), + DISCORD_TOKEN(String.class, "discord.token"), + DISCORD_REPORT_CHANNEL_ID(String.class, "discord.report_channel_id"), + DISCORD_CHAT_CHANNEL_ID(String.class, "discord.chat_channel_id"), + DISCORD_ADMINCHAT_CHANNEL_ID(String.class, "discord.adminchat_channel_id"), + + DISCORD_ROLE_SYNC(Boolean.class, "discord.role_sync"), + DISCORD_SERVER_ID(String.class, "discord.server_id"), + DISCORD_MASTER_BUILDER_ROLE_ID(String.class, "discord.master_builder_role_id"), + DISCORD_NEW_ADMIN_ROLE_ID(String.class, "discord.admin_role_id"), + DISCORD_SENIOR_ADMIN_ROLE_ID(String.class, "discord.senior_admin_role_id"), + DISCORD_DEVELOPER_ROLE_ID(String.class, "discord.developer_role_id"), + DISCORD_ASSISTANT_EXECUTIVE_ROLE_ID(String.class, "discord.assistant_executive_role_id"), + DISCORD_EXECUTIVE_ROLE_ID(String.class, "discord.executive_role_id"), + DISCORD_SERVER_OWNER_ROLE_ID(String.class, "discord.server_owner_role_id"), + // + PTERO_URL(String.class, "ptero.url"), + PTERO_DEFAULT_EMAIL_DOMAIN(String.class, "ptero.default_email_domain"), + PTERO_SERVER_UUID(String.class, "ptero.server_uuid"), + PTERO_ADMIN_KEY(String.class, "ptero.admin_key"), + PTERO_SERVER_KEY(String.class, "ptero.server_key"), + // + SHOP_ENABLED(Boolean.class, "shop.enabled"), + SHOP_TITLE(String.class, "shop.title"), + SHOP_PREFIX(String.class, "shop.prefix"), + SHOP_COINS_PER_VOTE(Integer.class, "shop.coins_per_vote"), + SHOP_REACTIONS_ENABLED(Boolean.class, "shop.reactions.enabled"), + SHOP_REACTIONS_INTERVAL(Integer.class, "shop.reactions.interval"), + SHOP_REACTIONS_TIME(Double.class, "shop.reactions.time"), + SHOP_REACTIONS_COINS_PER_WIN(Integer.class, "shop.reactions.coins_per_win"), + SHOP_REACTIONS_STRING_LENGTH(Integer.class, "shop.reactions.string_length"), + SHOP_LOGIN_MESSAGES(List.class, "shop.login_messages"), + SHOP_PRICES_GRAPPLING_HOOK(Integer.class, "shop.prices.grappling_hook"), + SHOP_PRICES_LIGHTNING_ROD(Integer.class, "shop.prices.lightning_rod"), + SHOP_PRICES_FIRE_BALL(Integer.class, "shop.prices.fire_ball"), + SHOP_PRICES_RIDEABLE_PEARL(Integer.class, "shop.prices.rideable_pearl"), + SHOP_PRICES_STACKING_POTATO(Integer.class, "shop.prices.stacking_potato"), + SHOP_PRICES_CLOWN_FISH(Integer.class, "shop.prices.clown_fish"), + SHOP_PRICES_LOGIN_MESSAGES(Integer.class, "shop.prices.login_messages"), + // + ADMINLIST_CLEAN_THESHOLD_HOURS(Integer.class, "adminlist.clean_threshold_hours"), + ADMINLIST_CONSOLE_IS_ADMIN(Boolean.class, "adminlist.console_is_admin"), + // + COREPROTECT_MYSQL_ENABLED(Boolean.class, "coreprotect.enabled"), + COREPROTECT_MYSQL_HOST(String.class, "coreprotect.host"), + COREPROTECT_MYSQL_PORT(String.class, "coreprotect.port"), + COREPROTECT_MYSQL_USERNAME(String.class, "coreprotect.username"), + COREPROTECT_MYSQL_PASSWORD(String.class, "coreprotect.password"), + COREPROTECT_MYSQL_DATABASE(String.class, "coreprotect.database"), + // + DISABLE_NIGHT(Boolean.class, "disable.night"), + DISABLE_WEATHER(Boolean.class, "disable.weather"), + // + ENABLE_PREPROCESS_LOG(Boolean.class, "preprocess_log"), + ENABLE_PET_PROTECT(Boolean.class, "petprotect.enabled"), + // + LANDMINES_ENABLED(Boolean.class, "landmines_enabled"), + TOSSMOB_ENABLED(Boolean.class, "tossmob_enabled"), + AUTOKICK_ENABLED(Boolean.class, "autokick.enabled"), + MP44_ENABLED(Boolean.class, "mp44_enabled"), + FOURCHAN_ENABLED(Boolean.class, "4chan_enabled"), + // + NUKE_MONITOR_ENABLED(Boolean.class, "nukemonitor.enabled"), + NUKE_MONITOR_COUNT_BREAK(Integer.class, "nukemonitor.count_break"), + NUKE_MONITOR_COUNT_PLACE(Integer.class, "nukemonitor.count_place"), + NUKE_MONITOR_RANGE(Double.class, "nukemonitor.range"), + // + AUTOKICK_THRESHOLD(Double.class, "autokick.threshold"), + AUTOKICK_TIME(Integer.class, "autokick.time"), + // + LOGS_SECRET(String.class, "logs.secret"), + LOGS_URL(String.class, "logs.url"), + // + FLATLANDS_GENERATE(Boolean.class, "flatlands.generate"), + FLATLANDS_GENERATE_PARAMS(String.class, "flatlands.generate_params"), + // + ANNOUNCER_ENABLED(Boolean.class, "announcer.enabled"), + ANNOUNCER_INTERVAL(Integer.class, "announcer.interval"), + ANNOUNCER_PREFIX(String.class, "announcer.prefix"), + ANNOUNCER_ANNOUNCEMENTS(List.class, "announcer.announcements"), + // + EXPLOSIVE_RADIUS(Double.class, "explosive_radius"), + FREECAM_TRIGGER_COUNT(Integer.class, "freecam_trigger_count"), + SERVICE_CHECKER_URL(String.class, "service_checker_url"), + BLOCKED_COMMANDS(List.class, "blocked_commands.global"), + MUTED_BLOCKED_COMMANDS(List.class, "blocked_commands.muted"), + WILDCARD_BLOCKED_COMMANDS(List.class, "blocked_commands.wildcard"), + FORBIDDEN_WORDS(List.class, "forbidden_words"), + HOST_SENDER_NAMES(List.class, "host_sender_names"), + FAMOUS_PLAYERS(List.class, "famous_players"), + ADMIN_ONLY_MODE(Boolean.class, "admin_only_mode"), + ADMIN_INFO(List.class, "admininfo"), + VOTING_INFO(List.class, "votinginfo"), + MASTER_BUILDER_INFO(List.class, "masterbuilderinfo"), + AUTO_ENTITY_WIPE(Boolean.class, "auto_wipe"), + TOGGLE_CHAT(Boolean.class, "toggle_chat"), + DEVELOPER_MODE(Boolean.class, "developer_mode"), + ANTISPAM_MINUTES(Integer.class, "antispam_minutes"); + // + private final Class type; + private final String configName; + + ConfigEntry(Class type, String configName) + { + this.type = type; + this.configName = configName; + } + + public static ConfigEntry findConfigEntry(String name) + { + name = name.toLowerCase().replace("_", ""); + for (ConfigEntry entry : values()) + { + if (entry.toString().toLowerCase().replace("_", "").equals(name)) + { + return entry; + } + } + return null; + } + + public Class getType() + { + return type; + } + + public String getConfigName() + { + return configName; + } + + public String getString() + { + return getConfig().getString(this); + } + + public Double getDouble() + { + return getConfig().getDouble(this); + } + + public void setDouble(Double value) + { + getConfig().setDouble(this, value); + } + + public Boolean getBoolean() + { + return getConfig().getBoolean(this); + } + + public Boolean setBoolean(Boolean value) + { + getConfig().setBoolean(this, value); + return value; + } + + public Integer getInteger() + { + return getConfig().getInteger(this); + } + + public void setInteger(Integer value) + { + getConfig().setInteger(this, value); + } + + public List getList() + { + return getConfig().getList(this); + } + + @SuppressWarnings("unchecked") + public List getStringList() + { + return (List)getList(); + } + + private MainConfig getConfig() + { + return TotalFreedomMod.getPlugin().config; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/IConfig.java b/src/main/java/me/totalfreedom/totalfreedommod/config/IConfig.java new file mode 100644 index 00000000..4773a682 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/IConfig.java @@ -0,0 +1,12 @@ +package me.totalfreedom.totalfreedommod.config; + +import org.bukkit.configuration.ConfigurationSection; + +public interface IConfig +{ + void loadFrom(ConfigurationSection cs); + + void saveTo(ConfigurationSection cs); + + boolean isValid(); +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/MainConfig.java b/src/main/java/me/totalfreedom/totalfreedommod/config/MainConfig.java new file mode 100644 index 00000000..e3569923 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/MainConfig.java @@ -0,0 +1,312 @@ +package me.totalfreedom.totalfreedommod.config; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.EnumMap; +import java.util.List; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FileUtils; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class MainConfig extends FreedomService +{ + + public static final String CONFIG_FILENAME = "config.yml"; + // + private final EnumMap entries; + private final ConfigDefaults defaults; + public YamlConfiguration configuration; + + public MainConfig() + { + entries = new EnumMap<>(ConfigEntry.class); + + ConfigDefaults tempDefaults = null; + try + { + try + { + try (InputStream defaultConfig = getDefaultConfig()) + { + tempDefaults = new ConfigDefaults(defaultConfig); + for (ConfigEntry entry : ConfigEntry.values()) + { + entries.put(entry, tempDefaults.get(entry.getConfigName())); + } + } + } + catch (IOException ex) + { + FLog.severe(ex); + } + + copyDefaultConfig(getConfigFile()); + + load(); + } + catch (Exception ex) + { + FLog.severe(ex); + } + + defaults = tempDefaults; + } + + @Override + public void onStart() + { + + } + + @Override + public void onStop() + { + + } + + public void load() + { + try + { + YamlConfiguration config = new YamlConfiguration(); + + config.load(getConfigFile()); + + configuration = config; + + for (ConfigEntry entry : ConfigEntry.values()) + { + String path = entry.getConfigName(); + if (config.contains(path)) + { + Object value = config.get(path); + if (value == null || entry.getType().isAssignableFrom(value.getClass())) + { + entries.put(entry, value); + } + else + { + FLog.warning("Value for " + entry.getConfigName() + " is of type " + + value.getClass().getSimpleName() + ". Needs to be " + entry.getType().getSimpleName() + ". Using default value."); + } + } + else + { + FLog.warning("Missing configuration entry " + entry.getConfigName() + ". Using default value."); + } + } + } + catch (IOException | InvalidConfigurationException ex) + { + FLog.severe(ex); + } + } + + private File getConfigFile() + { + return new File(plugin.getDataFolder(), "config.yml"); + } + + public String getString(ConfigEntry entry) + { + try + { + return get(entry, String.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public void setString(ConfigEntry entry, String value) + { + try + { + set(entry, value, String.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + } + + public Double getDouble(ConfigEntry entry) + { + try + { + return get(entry, Double.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public void setDouble(ConfigEntry entry, Double value) + { + try + { + set(entry, value, Double.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + } + + public Boolean getBoolean(ConfigEntry entry) + { + try + { + return get(entry, Boolean.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public void setBoolean(ConfigEntry entry, Boolean value) + { + try + { + set(entry, value, Boolean.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + } + + public Integer getInteger(ConfigEntry entry) + { + try + { + return get(entry, Integer.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public void setInteger(ConfigEntry entry, Integer value) + { + try + { + set(entry, value, Integer.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + } + + public List getList(ConfigEntry entry) + { + try + { + return get(entry, List.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public T get(ConfigEntry entry, Class type) throws IllegalArgumentException + { + Object value = entries.get(entry); + try + { + return type.cast(value); + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException(entry.name() + " is not of type " + type.getSimpleName()); + } + } + + public void set(ConfigEntry entry, T value, Class type) throws IllegalArgumentException + { + if (!type.isAssignableFrom(entry.getType())) + { + throw new IllegalArgumentException(entry.name() + " is not of type " + type.getSimpleName()); + } + if (value != null && !type.isAssignableFrom(value.getClass())) + { + throw new IllegalArgumentException("Value is not of type " + type.getSimpleName()); + } + entries.put(entry, value); + } + + private void copyDefaultConfig(File targetFile) + { + if (targetFile.exists()) + { + return; + } + + FLog.info("Installing default configuration file template: " + targetFile.getPath()); + + try + { + try (InputStream defaultConfig = getDefaultConfig()) + { + FileUtils.copyInputStreamToFile(defaultConfig, targetFile); + } + } + catch (IOException ex) + { + FLog.severe(ex); + } + } + + private InputStream getDefaultConfig() + { + return plugin.getResource(TotalFreedomMod.CONFIG_FILENAME); + } + + public ConfigDefaults getDefaults() + { + return defaults; + } + + public static class ConfigDefaults + { + + private YamlConfiguration defaults = null; + + private ConfigDefaults(InputStream defaultConfig) + { + try + { + defaults = new YamlConfiguration(); + final InputStreamReader isr = new InputStreamReader(defaultConfig); + defaults.load(isr); + isr.close(); + } + catch (IOException | InvalidConfigurationException ex) + { + FLog.severe(ex); + } + } + + public Object get(String path) + { + return defaults.get(path); + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/YamlConfig.java b/src/main/java/me/totalfreedom/totalfreedommod/config/YamlConfig.java new file mode 100644 index 00000000..0ed4aa89 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/YamlConfig.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.config; + +import java.io.File; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import org.bukkit.configuration.file.YamlConfiguration; + +public class YamlConfig extends YamlConfiguration +{ + private final File file; + + public YamlConfig(TotalFreedomMod plugin, String name, boolean copyDefaults) + { + this.file = new File(plugin.getDataFolder(), name); + + if (copyDefaults) + { + options().copyDefaults(true); + } + + if (!file.exists()) + { + plugin.saveResource(name, false); + } + load(); + } + + public YamlConfig(TotalFreedomMod plugin, String name) + { + this(plugin, name, true); + } + + public void load() + { + try + { + super.load(file); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public void save() + { + try + { + super.save(file); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public void clear() + { + for (String key : super.getKeys(false)) + { + super.set(key, null); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java new file mode 100644 index 00000000..484d6016 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java @@ -0,0 +1,540 @@ +package me.totalfreedom.totalfreedommod.discord; + +import com.google.common.base.Strings; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.SplittableRandom; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.security.auth.login.LoginException; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.PrivateChannel; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.SelfUser; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.ReadyEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.ChunkingFilter; +import net.dv8tion.jda.api.utils.MemberCachePolicy; +import net.dv8tion.jda.internal.utils.concurrent.CountingThreadFactory; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; +import org.bukkit.GameRule; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.jetbrains.annotations.NotNull; + +public class Discord extends FreedomService +{ + + public static HashMap LINK_CODES = new HashMap<>(); + public static HashMap VERIFICATION_CODES = new HashMap<>(); + public static JDA bot = null; + public ScheduledThreadPoolExecutor RATELIMIT_EXECUTOR; + public List> sentMessages = new ArrayList<>(); + public Boolean enabled = false; + private final Pattern DISCORD_MENTION_PATTERN = Pattern.compile("(<@!?([0-9]{16,20})>)"); + + public static String getMD5(String string) + { + return DigestUtils.md5Hex(string); + } + + public static String getCode(PlayerData playerData) + { + for (String code : LINK_CODES.keySet()) + { + if (LINK_CODES.get(code).equals(playerData)) + { + return code; + } + } + return null; + } + + public static boolean syncRoles(Admin admin, String discordID) + { + if (discordID == null) + { + return false; + } + + Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + if (server == null) + { + FLog.severe("The Discord server ID specified is invalid, or the bot is not on the server."); + return false; + } + + Member member = server.getMemberById(discordID); + if (member == null) + { + return false; + } + + Role adminRole = server.getRoleById(ConfigEntry.DISCORD_NEW_ADMIN_ROLE_ID.getString()); + if (adminRole == null) + { + FLog.severe("The specified Admin role does not exist!"); + return false; + } + + Role senioradminRole = server.getRoleById(ConfigEntry.DISCORD_SENIOR_ADMIN_ROLE_ID.getString()); + if (senioradminRole == null) + { + FLog.severe("The specified Senior Admin role does not exist!"); + return false; + } + + if (!admin.isActive()) + { + if (member.getRoles().contains(adminRole)) + { + server.removeRoleFromMember(member, adminRole).complete(); + } + if (member.getRoles().contains(senioradminRole)) + { + server.removeRoleFromMember(member, senioradminRole).complete(); + } + return true; + } + + if (admin.getRank().equals(Rank.ADMIN)) + { + if (!member.getRoles().contains(adminRole)) + { + server.addRoleToMember(member, adminRole).complete(); + } + if (member.getRoles().contains(senioradminRole)) + { + server.removeRoleFromMember(member, senioradminRole).complete(); + } + return true; + } + else if (admin.getRank().equals(Rank.SENIOR_ADMIN)) + { + if (!member.getRoles().contains(senioradminRole)) + { + server.addRoleToMember(member, senioradminRole).complete(); + } + if (member.getRoles().contains(adminRole)) + { + server.removeRoleFromMember(member, adminRole).complete(); + } + return true; + } + return false; + } + + public void startBot() + { + boolean verificationEnabled = ConfigEntry.DISCORD_VERIFICATION.getBoolean(); + if (!verificationEnabled) + { + FLog.info("Discord Verification has been manually disabled."); + } + + enabled = !Strings.isNullOrEmpty(ConfigEntry.DISCORD_TOKEN.getString()); + if (!enabled) + { + return; + } + + if (bot != null) + { + RATELIMIT_EXECUTOR = new ScheduledThreadPoolExecutor(5, new CountingThreadFactory(this::poolIdentifier, "RateLimit")); + RATELIMIT_EXECUTOR.setRemoveOnCancelPolicy(true); + for (Object object : bot.getRegisteredListeners()) + { + bot.removeEventListener(object); + } + } + + try + { + bot = JDABuilder.createDefault(ConfigEntry.DISCORD_TOKEN.getString()) + .addEventListeners(new PrivateMessageListener(), + new DiscordToMinecraftListener(), + new DiscordToAdminChatListener(), + new ListenerAdapter() + { + @Override + public void onReady(@NotNull ReadyEvent event) + { + new StartEvent().start(); + } + }) + .setAutoReconnect(true) + .setRateLimitPool(RATELIMIT_EXECUTOR) + .setChunkingFilter(ChunkingFilter.ALL) + .setMemberCachePolicy(MemberCachePolicy.ALL) + .enableIntents(GatewayIntent.GUILD_MEMBERS) + .build(); + FLog.info("Discord verification bot has successfully enabled!"); + } + catch (LoginException e) + { + FLog.warning("An invalid token for the discord verification bot, the bot will not enable."); + } + catch (IllegalArgumentException e) + { + FLog.warning("Discord verification bot failed to start."); + } + catch (NoClassDefFoundError e) + { + FLog.warning("The JDA plugin is not installed, therefore the discord bot cannot start."); + FLog.warning("To resolve this error, please download the latest JDA from: https://github.com/TFPatches/Minecraft-JDA/releases"); + } + + } + + public String poolIdentifier() + { + return "JDA"; + } + + public void clearQueue() + { + for (CompletableFuture messages : sentMessages) + { + if (!messages.isDone()) + { + messages.cancel(true); + } + } + sentMessages.clear(); + messageChatChannel("**Message queue cleared**"); + } + + public void sendPteroInfo(PlayerData playerData, String username, String password) + { + User user = getUser(playerData.getDiscordID()); + String message = "The following are your Pterodactyl details:\n\nUsername: " + username + "\nPassword: " + password + "\n\nYou can connect to the panel at " + plugin.ptero.URL; + PrivateChannel privateChannel = user.openPrivateChannel().complete(); + privateChannel.sendMessage(message).complete(); + } + + public User getUser(String id) + { + Guild guild = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + if (guild == null) + { + FLog.severe("Either the bot is not in the Discord server or it doesn't exist. Check the server ID."); + return null; + } + + Member member = guild.getMemberById(id); + if (member == null) + { + return null; + } + + return member.getUser(); + } + + public boolean sendBackupCodes(PlayerData playerData) + { + List codes = generateBackupCodes(); + List encryptedCodes = generateEncryptedBackupCodes(codes); + User user = getUser(playerData.getDiscordID()); + File file = generateBackupCodesFile(playerData.getName(), codes); + if (file == null) + { + return false; + } + PrivateChannel privateChannel = user.openPrivateChannel().complete(); + privateChannel.sendMessage("Do not share these codes with anyone as they can be used to impose as you.").addFile(file).complete(); + playerData.setBackupCodes(encryptedCodes); + plugin.pl.save(playerData); + //noinspection ResultOfMethodCallIgnored + file.delete(); + return true; + } + + public List generateBackupCodes() + { + List codes = new ArrayList<>(); + for (int i = 0; i < 10; i++) + { + codes.add(FUtil.randomAlphanumericString(10)); + } + return codes; + } + + public String generateCode(int size) + { + StringBuilder code = new StringBuilder(); + SplittableRandom random = new SplittableRandom(); + for (int i = 0; i < size; i++) + { + code.append(random.nextInt(10)); + } + return code.toString(); + } + + public List generateEncryptedBackupCodes(List codes) + { + List encryptedCodes = new ArrayList<>(); + for (String code : codes) + { + encryptedCodes.add(getMD5(code)); + } + return encryptedCodes; + } + + public File generateBackupCodesFile(String name, List codes) + { + StringBuilder text = new StringBuilder(); + text.append("Below are your backup codes for use on TotalFreedom in the event you lose access to your discord account.\n") + .append("Simply pick a code, and run /verify on the server. Each code is one use, so be sure to cross it off once you use it.\n") + .append("To generate new codes, simply run /generatebackupcodes\n\n"); + + for (String code : codes) + { + text.append(code).append("\n"); + } + + String fileUrl = plugin.getDataFolder().getAbsolutePath() + "/TF-Backup-Codes-" + name + ".txt"; + try + { + FileWriter fileWriter = new FileWriter(fileUrl); + fileWriter.write(text.toString()); + fileWriter.close(); + } + catch (IOException e) + { + FLog.severe("Failed to generate backup codes file: " + e.toString()); + return null; + } + return new File(fileUrl); + } + + public void addVerificationCode(String code, PlayerData playerData) + { + VERIFICATION_CODES.put(code, playerData); + } + + public void removeVerificationCode(String code) + { + VERIFICATION_CODES.remove(code); + } + + public HashMap getVerificationCodes() + { + return VERIFICATION_CODES; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerDeath(PlayerDeathEvent event) + { + //Avoiding NPE Unboxing Warnings + Boolean b = event.getEntity().getWorld().getGameRuleValue(GameRule.SHOW_DEATH_MESSAGES); + if (b == null || !b) + { + return; + } + + if (event.getDeathMessage() != null) + { + messageChatChannel("**" + event.getDeathMessage() + "**"); + } + } + + @Override + public void onStart() + { + startBot(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) + { + if (!plugin.al.isVanished(event.getPlayer().getName())) + { + messageChatChannel("**" + event.getPlayer().getName() + " joined the server" + "**"); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerLeave(PlayerQuitEvent event) + { + if (!plugin.al.isVanished(event.getPlayer().getName())) + { + messageChatChannel("**" + event.getPlayer().getName() + " left the server" + "**"); + } + } + + public void messageChatChannel(String message) + { + String chat_channel_id = ConfigEntry.DISCORD_CHAT_CHANNEL_ID.getString(); + if (message.contains("@everyone") || message.contains("@here")) + { + message = StringUtils.remove(message, "@"); + } + + if (message.toLowerCase().contains("discord.gg")) + { + return; + } + + if (message.contains("§")) + { + message = StringUtils.remove(message, "§"); + } + + + Matcher DISCORD_MENTION_MATCHER = DISCORD_MENTION_PATTERN.matcher(message); + + while (DISCORD_MENTION_MATCHER.find()) + { + String mention = DISCORD_MENTION_MATCHER.group(1); + message = message.replace(mention, mention.replace("@","")); + } + + if (enabled && !chat_channel_id.isEmpty()) + { + CompletableFuture sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(deformat(message)).submit(true); + sentMessages.add(sentMessage); + } + } + + public void messageAdminChatChannel(String message) + { + String chat_channel_id = ConfigEntry.DISCORD_ADMINCHAT_CHANNEL_ID.getString(); + if (message.contains("@everyone") || message.contains("@here")) + { + message = StringUtils.remove(message, "@"); + } + + if (message.toLowerCase().contains("discord.gg")) + { + return; + } + + if (message.contains("§")) + { + message = StringUtils.remove(message, "§"); + } + + + Matcher DISCORD_MENTION_MATCHER = DISCORD_MENTION_PATTERN.matcher(message); + + while (DISCORD_MENTION_MATCHER.find()) + { + String mention = DISCORD_MENTION_MATCHER.group(1); + message = message.replace(mention, mention.replace("@","")); + } + + if (enabled && !chat_channel_id.isEmpty()) + { + CompletableFuture sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(deformat(message)).submit(true); + sentMessages.add(sentMessage); + } + } + + public String formatBotTag() + { + SelfUser user = bot.getSelfUser(); + return user.getName() + "#" + user.getDiscriminator(); + } + + @Override + public void onStop() + { + if (bot != null) + { + messageChatChannel("**Server has stopped**"); + } + + FLog.info("Discord verification bot has successfully shutdown."); + } + + public String deformat(String input) + { + return input.replace("_", "\\_"); + } + + public boolean sendReport(Player reporter, Player reported, String reason) + { + if (ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString().isEmpty()) + { + return false; + } + + if (ConfigEntry.DISCORD_SERVER_ID.getString().isEmpty()) + { + FLog.severe("No Discord server ID was specified in the config, but there is a report channel ID."); + return false; + } + + Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + if (server == null) + { + FLog.severe("The Discord server ID specified is invalid, or the bot is not on the server."); + return false; + } + + TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString()); + if (channel == null) + { + FLog.severe("The report channel ID specified in the config is invalid."); + return false; + } + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Report for " + reported.getName()); + embedBuilder.setDescription(reason); + embedBuilder.setFooter("Reported by " + reporter.getName(), "https://minotar.net/helm/" + reporter.getName() + ".png"); + embedBuilder.setTimestamp(Instant.from(ZonedDateTime.now())); + String location = "World: " + Objects.requireNonNull(reported.getLocation().getWorld()).getName() + ", X: " + reported.getLocation().getBlockX() + ", Y: " + reported.getLocation().getBlockY() + ", Z: " + reported.getLocation().getBlockZ(); + embedBuilder.addField("Location", location, true); + embedBuilder.addField("Game Mode", WordUtils.capitalizeFully(reported.getGameMode().name()), true); + com.earth2me.essentials.User user = plugin.esb.getEssentialsUser(reported.getName()); + embedBuilder.addField("God Mode", WordUtils.capitalizeFully(String.valueOf(user.isGodModeEnabled())), true); + if (user.getNickname() != null) + { + embedBuilder.addField("Nickname", user.getNickname(), true); + } + MessageEmbed embed = embedBuilder.build(); + channel.sendMessage(embed).complete(); + return true; + } + + // Do no ask why this is here. I spent two hours trying to make a simple thing work + public class StartEvent + { + public void start() + { + messageChatChannel("**Server has started**"); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToAdminChatListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToAdminChatListener.java new file mode 100644 index 00000000..4e0ff6fb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToAdminChatListener.java @@ -0,0 +1,148 @@ +package me.totalfreedom.totalfreedommod.discord; + +import com.google.common.base.Strings; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.rank.Title; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; + +public class DiscordToAdminChatListener extends ListenerAdapter +{ + + DiscordToMinecraftListener dtml = new DiscordToMinecraftListener(); + + public static net.md_5.bungee.api.ChatColor getColor(Displayable display) + { + return display.getColor(); + } + + public void onMessageReceived(MessageReceivedEvent event) + { + String chat_channel_id = ConfigEntry.DISCORD_ADMINCHAT_CHANNEL_ID.getString(); + if (event.getMember() != null && !chat_channel_id.isEmpty() && event.getChannel().getId().equals(chat_channel_id) && !event.getAuthor().getId().equals(event.getJDA().getSelfUser().getId())) + { + Member member = event.getMember(); + String tag = dtml.getDisplay(member); + Message msg = event.getMessage(); + String mediamessage = ChatColor.YELLOW + "[Media]"; + + StringBuilder logmessage = new StringBuilder(ChatColor.DARK_GRAY + "[" + ChatColor.DARK_AQUA + "Discord" + ChatColor.DARK_GRAY + "] " + ChatColor.RESET); + String lm = ChatColor.DARK_RED + member.getEffectiveName() + " " + + ChatColor.DARK_GRAY + tag + ChatColor.DARK_GRAY + + ChatColor.WHITE + ": " + ChatColor.GOLD + FUtil.colorize(msg.getContentDisplay()); + logmessage.append(lm); + + if (!msg.getAttachments().isEmpty()) + { + + logmessage.append(mediamessage); // Actually for logging... + + } + FLog.info(logmessage.toString()); + + Bukkit.getOnlinePlayers().stream().filter(player -> TotalFreedomMod.getPlugin().al.isAdmin(player)).forEach(player -> + { + StringBuilder message = new StringBuilder(ChatColor.DARK_GRAY + "[" + ChatColor.DARK_AQUA + "Discord" + ChatColor.DARK_GRAY + "] " + ChatColor.RESET); + + ComponentBuilder builder = new ComponentBuilder(message.toString()); + + Admin admin = TotalFreedomMod.getPlugin().al.getAdmin(player); + String format = admin.getAcFormat(); + if (!Strings.isNullOrEmpty(format)) + { + Displayable display = getDisplay(member); + net.md_5.bungee.api.ChatColor color = getColor(display); + String m = format.replace("%name%", member.getEffectiveName()) + .replace("%rank%", display.getAbbr()) + .replace("%rankcolor%", color.toString()) + .replace("%msg%", FUtil.colorize(msg.getContentDisplay())); + builder.append(FUtil.colorize(m)); + + } + else + { + String m = ChatColor.DARK_RED + member.getEffectiveName() + " " + + ChatColor.DARK_GRAY + tag + ChatColor.DARK_GRAY + + ChatColor.WHITE + ": " + ChatColor.GOLD + FUtil.colorize(msg.getContentDisplay()); + builder.append(m); + } + + if (!msg.getAttachments().isEmpty()) + { + for (Message.Attachment attachment : msg.getAttachments()) + { + TextComponent text = new TextComponent(mediamessage); + text.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, attachment.getUrl())); + if (!msg.getContentDisplay().isEmpty()) + { + builder.append(" "); + } + builder.append(text); + } + } + player.spigot().sendMessage(builder.create()); + + }); + } + } + + // Needed to display tags in custom AC messages + public Displayable getDisplay(Member member) + { + Guild server = Discord.bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + // Server Owner + if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_SERVER_OWNER_ROLE_ID.getString()))) + { + return Title.OWNER; + } + // Developers + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_DEVELOPER_ROLE_ID.getString()))) + { + return Title.DEVELOPER; + } + // Executives + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_EXECUTIVE_ROLE_ID.getString()))) + { + return Title.EXECUTIVE; + } + // Assistant Executives + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_ASSISTANT_EXECUTIVE_ROLE_ID.getString()))) + { + return Title.ASSTEXEC; + } + // Senior Admins + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_SENIOR_ADMIN_ROLE_ID.getString()))) + { + return Rank.SENIOR_ADMIN; + } + // Admins + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_NEW_ADMIN_ROLE_ID.getString()))) + { + return Rank.ADMIN; + } + // Master Builders + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_MASTER_BUILDER_ROLE_ID.getString()))) + { + return Title.MASTER_BUILDER; + } + // OP, returning null breaks? + else + { + return Rank.OP; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java new file mode 100644 index 00000000..e0f26bff --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java @@ -0,0 +1,113 @@ +package me.totalfreedom.totalfreedommod.discord; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.rank.Title; +import me.totalfreedom.totalfreedommod.util.FLog; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +public class DiscordToMinecraftListener extends ListenerAdapter +{ + public void onMessageReceived(MessageReceivedEvent event) + { + String chat_channel_id = ConfigEntry.DISCORD_CHAT_CHANNEL_ID.getString(); + if (event.getMember() != null && !chat_channel_id.isEmpty() && event.getChannel().getId().equals(chat_channel_id)) + { + if (!event.getAuthor().getId().equals(Discord.bot.getSelfUser().getId())) + { + Member member = event.getMember(); + String tag = getDisplay(member); + StringBuilder message = new StringBuilder(ChatColor.DARK_GRAY + "[" + ChatColor.DARK_AQUA + "Discord" + ChatColor.DARK_GRAY + "]"); + Message msg = event.getMessage(); + if (tag != null) + { + message.append(" ").append(tag); + } + message.append(" ").append(ChatColor.RED).append(ChatColor.stripColor(member.getEffectiveName())).append(ChatColor.DARK_GRAY).append(":").append(ChatColor.RESET); + ComponentBuilder builder = new ComponentBuilder(message.toString()); + if (!msg.getContentDisplay().isEmpty()) + { + builder.append(" ").append(ChatColor.stripColor(msg.getContentDisplay())); + message.append(" ").append(ChatColor.stripColor(msg.getContentDisplay())); // for logging + } + if (!msg.getAttachments().isEmpty()) + { + for (Message.Attachment attachment : msg.getAttachments()) + { + attachment.getUrl(); + builder.append(" "); + TextComponent text = new TextComponent(ChatColor.YELLOW + "[Media]"); + text.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, attachment.getUrl())); + builder.append(text); + message.append(" [Media]"); // for logging + } + } + for (Player player : Bukkit.getOnlinePlayers()) + { + if (TotalFreedomMod.getPlugin().pl.getData(player).doesDisplayDiscord()) + { + player.spigot().sendMessage(builder.create()); + } + } + FLog.info(message.toString()); + } + } + } + + public String getDisplay(Member member) + { + Guild server = Discord.bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + // Server Owner + assert server != null; + if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_SERVER_OWNER_ROLE_ID.getString()))) + { + return Title.OWNER.getColoredTag(); + } + // Developers + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_DEVELOPER_ROLE_ID.getString()))) + { + return Title.DEVELOPER.getColoredTag(); + } + // Executives + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_EXECUTIVE_ROLE_ID.getString()))) + { + return Title.EXECUTIVE.getColoredTag(); + } + // Assistant Executives + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_ASSISTANT_EXECUTIVE_ROLE_ID.getString()))) + { + return Title.ASSTEXEC.getColoredTag(); + } + // Senior Admins + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_SENIOR_ADMIN_ROLE_ID.getString()))) + { + return Rank.SENIOR_ADMIN.getColoredTag(); + } + // Admins + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_NEW_ADMIN_ROLE_ID.getString()))) + { + return Rank.ADMIN.getColoredTag(); + } + // Master Builders + else if (member.getRoles().contains(server.getRoleById(ConfigEntry.DISCORD_MASTER_BUILDER_ROLE_ID.getString()))) + { + return Title.MASTER_BUILDER.getColoredTag(); + } + // None + else + { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java new file mode 100644 index 00000000..aee47a51 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/PrivateMessageListener.java @@ -0,0 +1,45 @@ +package me.totalfreedom.totalfreedommod.discord; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +public class PrivateMessageListener extends ListenerAdapter +{ + public void onPrivateMessageReceived(PrivateMessageReceivedEvent event) + { + if (!event.getAuthor().getId().equals(event.getJDA().getSelfUser().getId())) + { + // Handle link code + if (event.getMessage().getContentRaw().matches("[0-9][0-9][0-9][0-9][0-9]")) + { + String code = event.getMessage().getContentRaw(); + String name; + if (Discord.LINK_CODES.get(code) != null) + { + PlayerData player = Discord.LINK_CODES.get(code); + name = player.getName(); + player.setDiscordID(event.getMessage().getAuthor().getId()); + player.setVerification(true); + + Admin admin = TotalFreedomMod.getPlugin().al.getEntryByName(name); + if (admin != null) + { + Discord.syncRoles(admin, player.getDiscordID()); + } + + TotalFreedomMod.getPlugin().pl.save(player); + Discord.LINK_CODES.remove(code); + } + else + { + return; + } + event.getChannel().sendMessage("Link successful. Now this Discord account is linked with your Minecraft account **" + name + "**.\n" + + "Now when you are an impostor on the server, you may use `/verify` to verify.").complete(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/freeze/FreezeData.java b/src/main/java/me/totalfreedom/totalfreedommod/freeze/FreezeData.java new file mode 100644 index 00000000..bb03caaf --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/freeze/FreezeData.java @@ -0,0 +1,78 @@ +package me.totalfreedom.totalfreedommod.freeze; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import static me.totalfreedom.totalfreedommod.TotalFreedomMod.plugin; +import static me.totalfreedom.totalfreedommod.player.FPlayer.AUTO_PURGE_TICKS; + +public class FreezeData +{ + + private final FPlayer fPlayer; + // + private Location location = null; + private BukkitTask unfreeze = null; + + public FreezeData(FPlayer fPlayer) + { + this.fPlayer = fPlayer; + } + + public boolean isFrozen() + { + return unfreeze != null; + } + + public void setFrozen(boolean freeze) + { + final Player player = fPlayer.getPlayer(); + if (player == null) + { + FLog.info("Could not freeze that player as they are not online!"); + return; + } + + FUtil.cancel(unfreeze); + unfreeze = null; + location = null; + + if (!freeze) + { + if (fPlayer.getPlayer().getGameMode() != GameMode.CREATIVE) + { + FUtil.setFlying(player, false); + } + + return; + } + + location = player.getLocation(); // Blockify location + FUtil.setFlying(player, true); // Avoid infinite falling + + unfreeze = new BukkitRunnable() + { + @Override + public void run() + { + if (!Objects.requireNonNull(plugin()).al.isAdminImpostor(player) && Objects.requireNonNull(plugin()).pl.isPlayerImpostor(player)) + { + FUtil.adminAction("TotalFreedom", "Unfreezing " + player.getName(), false); + setFrozen(false); + } + } + + }.runTaskLater(Objects.requireNonNull(plugin()), AUTO_PURGE_TICKS); + } + + public Location getLocation() + { + return location; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/freeze/Freezer.java b/src/main/java/me/totalfreedom/totalfreedommod/freeze/Freezer.java new file mode 100644 index 00000000..cb0a33f9 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/freeze/Freezer.java @@ -0,0 +1,69 @@ +package me.totalfreedom.totalfreedommod.freeze; + +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; + +public class Freezer extends FreedomService +{ + + private boolean globalFreeze = false; + + @Override + public void onStart() + { + globalFreeze = false; + } + + @Override + public void onStop() + { + } + + public void purge() + { + this.globalFreeze = false; + + for (Player player : server.getOnlinePlayers()) + { + plugin.pl.getPlayer(player).getFreezeData().setFrozen(false); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerMove(PlayerMoveEvent event) + { + final Player player = event.getPlayer(); + + if (plugin.al.isAdmin(player)) + { + return; + } + + final FreezeData fd = plugin.pl.getPlayer(player).getFreezeData(); + if (!fd.isFrozen() && !globalFreeze) + { + return; + } + + FUtil.setFlying(player, true); + + Location loc = player.getLocation(); + + event.setTo(loc); + } + + public boolean isGlobalFreeze() + { + return globalFreeze; + } + + public void setGlobalFreeze(boolean frozen) + { + this.globalFreeze = frozen; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/fun/ItemFun.java b/src/main/java/me/totalfreedom/totalfreedommod/fun/ItemFun.java new file mode 100644 index 00000000..55c4ec94 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/fun/ItemFun.java @@ -0,0 +1,452 @@ +package me.totalfreedom.totalfreedommod.fun; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SplittableRandom; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.block.Block; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.Firework; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +public class ItemFun extends FreedomService +{ + + private final SplittableRandom random = new SplittableRandom(); + private final Map> cooldownTracker = new HashMap<>(); + private final Map orientationTracker = new HashMap<>(); + private final List FIRE_BALL_UUIDS = new ArrayList<>(); + public List explosivePlayers = new ArrayList<>(); + + private void cooldown(Player player, ShopItem item, int seconds) + { + if (cooldownTracker.get(player.getName()) == null) + { + List featureList = new ArrayList<>(); + featureList.add(item.getDataName()); + cooldownTracker.put(player.getName(), featureList); + } + else + { + cooldownTracker.get(player.getName()).add(item.getDataName()); + } + new BukkitRunnable() + { + @Override + public void run() + { + cooldownTracker.get(player.getName()).remove(item.getDataName()); + } + }.runTaskLater(plugin, seconds * 20); + } + + public boolean onCooldown(Player player, ShopItem item) + { + if (cooldownTracker.get(player.getName()) == null) + { + return false; + } + return cooldownTracker.get(player.getName()).contains(item.getDataName()); + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler + public void onPlayerEntityInteract(PlayerInteractEntityEvent event) + { + + Player player = event.getPlayer(); + Entity entity = event.getRightClicked(); + FPlayer fPlayer = plugin.pl.getPlayer(player); + + if (player.getInventory().getItemInMainHand().getType().equals(Material.POTATO) || entity.getType().equals(EntityType.PLAYER)) + { + if (plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.STACKING_POTATO, player.getInventory(), plugin.sh.getStackingPotato())) + { + if (entity instanceof Player) + { + return; + } + player.addPassenger(entity); + player.sendMessage("Stacked " + entity.getName()); + } + } + + if (player.getInventory().getItemInMainHand().getType().equals(Material.BONE) || entity.getType().equals(EntityType.PLAYER)) + { + if (!fPlayer.mobThrowerEnabled()) + { + return; + } + + Location playerLoc = player.getLocation(); + Vector direction = playerLoc.getDirection().normalize(); + + LivingEntity livingEntity = (LivingEntity)event.getRightClicked(); + EntityType entityType = livingEntity.getType(); + if (!(entityType == fPlayer.mobThrowerCreature())) + { + return; + } + + livingEntity.setVelocity(direction.multiply(fPlayer.mobThrowerSpeed())); + fPlayer.enqueueMob(livingEntity); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEntityDamage(EntityDamageByEntityEvent event) + { + Entity entity = event.getEntity(); + + if (entity instanceof Player || !(event.getDamager() instanceof Player)) + { + return; + } + + Player player = (Player)event.getDamager(); + + if (!player.getInventory().getItemInMainHand().getType().equals(Material.POTATO)) + { + return; + } + + if (!plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.STACKING_POTATO, player.getInventory(), plugin.sh.getStackingPotato())) + { + return; + } + + event.setCancelled(true); + entity.addPassenger(player); + } + + @EventHandler + public void onPlayerInteractEvent(PlayerInteractEvent event) + { + if (event.getAction() == Action.LEFT_CLICK_AIR + || event.getAction() == Action.LEFT_CLICK_BLOCK) + { + return; + } + + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + + switch (event.getMaterial()) + { + case GUNPOWDER: + { + if (!fPlayer.isMP44Armed()) + { + break; + } + + event.setCancelled(true); + + if (fPlayer.toggleMP44Firing()) + { + fPlayer.startArrowShooter(plugin); + } + else + { + fPlayer.stopArrowShooter(); + } + break; + } + + case BLAZE_ROD: + { + if (!plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.LIGHTNING_ROD, player.getInventory(), plugin.sh.getLightningRod())) + { + break; + } + + if (onCooldown(player, ShopItem.LIGHTNING_ROD)) + { + player.sendMessage(ChatColor.RED + "You're currently on a cool-down for 10 seconds."); + break; + } + + event.setCancelled(true); + Block targetBlock = player.getTargetBlock(null, 20); + + for (int i = 0; i < 5; i++) + { + player.getWorld().strikeLightning(targetBlock.getLocation()); + } + cooldown(player, ShopItem.LIGHTNING_ROD, 10); + break; + } + + case FIRE_CHARGE: + { + if (!plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.FIRE_BALL, player.getInventory(), plugin.sh.getFireBall())) + { + break; + } + + if (onCooldown(player, ShopItem.FIRE_BALL)) + { + player.sendMessage(ChatColor.RED + "You're currently on a cool-down for 5 seconds."); + break; + } + + event.setCancelled(true); + Entity fireball = player.getWorld().spawnEntity(player.getLocation(), EntityType.FIREBALL); + FIRE_BALL_UUIDS.add(fireball.getUniqueId()); + fireball.setVelocity(player.getLocation().getDirection().multiply(2)); + cooldown(player, ShopItem.FIRE_BALL, 5); + break; + } + + case TROPICAL_FISH: + { + final int RADIUS_HIT = 5; + final int STRENGTH = 4; + + if (plugin.lp.CLOWNFISH_TOGGLE.contains(player.getName())) + { + player.sendMessage(ChatColor.GRAY + "An admin has disabled your ability to use clownfish."); + break; + } + + if (!plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.CLOWN_FISH, player.getInventory(), plugin.sh.getClownFish())) + { + break; + } + + event.setCancelled(true); + boolean didHit = false; + + final Location playerLoc = player.getLocation(); + final Vector playerLocVec = playerLoc.toVector(); + + final List players = player.getWorld().getPlayers(); + for (final Player target : players) + { + if (target == player) + { + continue; + } + + final Location targetPos = target.getLocation(); + final Vector targetPosVec = targetPos.toVector(); + + try + { + if (targetPosVec.distanceSquared(playerLocVec) < (RADIUS_HIT * RADIUS_HIT)) + { + FUtil.setFlying(player, false); + target.setVelocity(targetPosVec.subtract(playerLocVec).normalize().multiply(STRENGTH)); + didHit = true; + } + } + catch (IllegalArgumentException ignored) + { + } + } + + if (didHit) + { + final Sound[] sounds = Sound.values(); + for (Sound sound : sounds) + { + if (sound.toString().contains("HIT")) + { + Objects.requireNonNull(playerLoc.getWorld()).playSound(randomOffset(playerLoc), sound, 20f, randomDoubleRange(0.5, 2.0).floatValue()); + } + } + cooldown(player, ShopItem.CLOWN_FISH, 30); + } + break; + } + } + } + + @EventHandler + public void onProjectileLaunch(ProjectileLaunchEvent event) + { + Projectile entity = event.getEntity(); + if (entity instanceof EnderPearl && entity.getShooter() instanceof Player) + { + Player player = (Player)entity.getShooter(); + if (plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.RIDEABLE_PEARL, player.getInventory(), plugin.sh.getRideablePearl())) + { + entity.addPassenger(player); + } + } + } + + @EventHandler + public void onProjectileHit(ProjectileHitEvent event) + { + Entity entity = event.getEntity(); + Arrow arrow = null; + if (entity instanceof Arrow) + { + arrow = (Arrow)entity; + } + //Redundant Player cast is required to avoid suspicious method calls. + if (arrow != null + && (arrow.getShooter() instanceof Player) + && explosivePlayers.contains(arrow.getShooter())) + { + Objects.requireNonNull(arrow.getLocation().getWorld()).createExplosion(arrow.getLocation().getX(), arrow.getLocation().getY(), arrow.getLocation().getZ(), ConfigEntry.EXPLOSIVE_RADIUS.getDouble().floatValue(), false, ConfigEntry.ALLOW_EXPLOSIONS.getBoolean()); + arrow.remove(); + } + + if (entity instanceof Fireball) + { + if (FIRE_BALL_UUIDS.contains(entity.getUniqueId())) + { + FIRE_BALL_UUIDS.remove(entity.getUniqueId()); + Firework firework = (Firework)entity.getWorld().spawnEntity(entity.getLocation(), EntityType.FIREWORK); + firework.setSilent(true); + FireworkMeta meta = firework.getFireworkMeta(); + FireworkEffect explosionEffect = FireworkEffect.builder().withColor(Color.ORANGE).withFade(Color.YELLOW).with(FireworkEffect.Type.BALL_LARGE).trail(true).build(); + meta.addEffect(explosionEffect); + meta.setPower(0); + firework.setFireworkMeta(meta); + entity.remove(); + firework.detonate(); + entity.getWorld().playSound(firework.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, SoundCategory.PLAYERS, 10f, 1f); + } + } + } + + private Location randomOffset(Location a) + { + return a.clone().add(randomDoubleRange(-1.0, 1.0) * 5.0, randomDoubleRange(-1.0, 1.0) * 5.0, randomDoubleRange(-1.0, 1.0) * 5.0); + } + + private Double randomDoubleRange(double min, double max) + { + return min + (random.nextDouble() * ((max - min) + 1.0)); + } + + @EventHandler + public void onFish(PlayerFishEvent event) + { + Player player = event.getPlayer(); + PlayerData data = plugin.pl.getData(player); + PlayerInventory inv = event.getPlayer().getInventory(); + ItemStack rod = inv.getItemInMainHand(); + if (plugin.sh.isRealItem(plugin.pl.getData(player), ShopItem.GRAPPLING_HOOK, player.getInventory(), plugin.sh.getGrapplingHook())) + { + if (event.getState() == PlayerFishEvent.State.REEL_IN || event.getState() == PlayerFishEvent.State.IN_GROUND) + { + double orientation = player.getLocation().getYaw(); + if (orientationTracker.containsKey(player)) + { + orientation = orientationTracker.get(player); + } + if (orientation < 0.0) + { + orientation += 360; + } + int speed = 5; + if (player.getLocation().subtract(0, 1, 0).getBlock().getType() == Material.AIR) + { + speed = 15; + } + double xVel = 0; + double yVel = 1; + double zVel = 0; + if (orientation >= 0.0 && orientation < 22.5) + { + zVel = speed; + } + else if (orientation >= 22.5 && orientation < 67.5) + { + xVel = -(speed / 2.0); + zVel = speed / 2.0; + } + else if (orientation >= 67.5 && orientation < 112.5) + { + xVel = -speed; + } + else if (orientation >= 112.5 && orientation < 157.5) + { + xVel = -(speed / 2.0); + zVel = -(speed / 2.0); + } + else if (orientation >= 157.5 && orientation < 202.5) + { + zVel = -speed; + } + else if (orientation >= 202.5 && orientation < 247.5) + { + xVel = speed / 2.0; + zVel = -(speed / 2.0); + } + else if (orientation >= 247.5 && orientation < 292.5) + { + xVel = speed; + } + else if (orientation >= 292.5 && orientation < 337.5) + { + xVel = speed / 2.0; + zVel = speed / 2.0; + } + else if (orientation >= 337.5 && orientation < 360.0) + { + zVel = speed; + } + player.setVelocity(new Vector(xVel, yVel, zVel)); + } + + if (event.getState() == PlayerFishEvent.State.FISHING) + { + orientationTracker.put(player, player.getLocation().getYaw()); + } + else + { + orientationTracker.remove(player); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/fun/Jumppads.java b/src/main/java/me/totalfreedom/totalfreedommod/fun/Jumppads.java new file mode 100644 index 00000000..2341272b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/fun/Jumppads.java @@ -0,0 +1,159 @@ +package me.totalfreedom.totalfreedommod.fun; + +import com.google.common.collect.Maps; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; + +public class Jumppads extends FreedomService +{ + + public static final double DAMPING_COEFFICIENT = 0.8; + // + private final Map pushMap = Maps.newHashMap(); + // + private final double strength = 1 + 0.1F; + public HashMap players = new HashMap<>(); + + public static double getDampingCoefficient() + { + return DAMPING_COEFFICIENT; + } + + @Override + public void onStart() + { + + } + + @Override + public void onStop() + { + + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) + { + if (!players.containsKey(event.getPlayer())) + { + players.put(event.getPlayer(), JumpPadMode.OFF); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) + { + if (players.get(event.getPlayer()) == JumpPadMode.OFF) + { + return; + } + + final Player player = event.getPlayer(); + final Block block = Objects.requireNonNull(event.getTo()).getBlock(); + final Vector velocity = player.getVelocity().clone(); + + if (players.get(event.getPlayer()) == JumpPadMode.MADGEEK) + { + Boolean canPush = pushMap.get(player); + if (canPush == null) + { + canPush = true; + } + if (Groups.WOOL_COLORS.contains(block.getRelative(0, -1, 0).getType())) + { + if (canPush) + { + velocity.multiply(strength + 0.85).multiply(-1.0); + } + canPush = false; + } + else + { + canPush = true; + } + pushMap.put(player, canPush); + } + else + { + if (Groups.WOOL_COLORS.contains(block.getRelative(0, -1, 0).getType())) + { + velocity.add(new Vector(0.0, strength, 0.0)); + } + + if (players.get(event.getPlayer()) == JumpPadMode.NORMAL_AND_SIDEWAYS) + { + if (Groups.WOOL_COLORS.contains(block.getRelative(1, 0, 0).getType())) + { + velocity.add(new Vector(-DAMPING_COEFFICIENT * strength, 0.0, 0.0)); + } + + if (Groups.WOOL_COLORS.contains(block.getRelative(-1, 0, 0).getType())) + { + velocity.add(new Vector(DAMPING_COEFFICIENT * strength, 0.0, 0.0)); + } + + if (Groups.WOOL_COLORS.contains(block.getRelative(0, 0, 1).getType())) + { + velocity.add(new Vector(0.0, 0.0, -DAMPING_COEFFICIENT * strength)); + } + + if (Groups.WOOL_COLORS.contains(block.getRelative(0, 0, -1).getType())) + { + velocity.add(new Vector(0.0, 0.0, DAMPING_COEFFICIENT * strength)); + } + } + } + + if (!player.getVelocity().equals(velocity)) + { + player.setFallDistance(0.0f); + player.setVelocity(velocity); + } + } + + public Map getPushMap() + { + return pushMap; + } + + public double getStrength() + { + return strength; + } + + public HashMap getPlayers() + { + return players; + } + + public void setPlayers(HashMap players) + { + this.players = players; + } + + public enum JumpPadMode + { + + OFF(false), NORMAL_AND_SIDEWAYS(true), MADGEEK(true); + private final boolean on; + + JumpPadMode(boolean on) + { + this.on = on; + } + + public boolean isOn() + { + return on; + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/fun/Landminer.java b/src/main/java/me/totalfreedom/totalfreedommod/fun/Landminer.java new file mode 100644 index 00000000..bf9c8bde --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/fun/Landminer.java @@ -0,0 +1,135 @@ +package me.totalfreedom.totalfreedommod.fun; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; + +public class Landminer extends FreedomService +{ + private final List landmines = new ArrayList<>(); + + @Override + public void onStart() + { + landmines.clear(); + } + + @Override + public void onStop() + { + } + + public void add(Landmine landmine) + { + landmines.add(landmine); + } + + public void remove(Landmine landmine) + { + landmines.remove(landmine); + } + + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerMove(PlayerMoveEvent event) + { + if (!(ConfigEntry.LANDMINES_ENABLED.getBoolean() && ConfigEntry.ALLOW_EXPLOSIONS.getBoolean())) + { + return; + } + + final Player player = event.getPlayer(); + + final Iterator lit = landmines.iterator(); + while (lit.hasNext()) + { + final Landmine landmine = lit.next(); + + final Location location = landmine.location; + if (location.getBlock().getType() != Material.TNT) + { + lit.remove(); + continue; + } + + if (landmine.planter.equals(player)) + { + break; + } + + if (!player.getWorld().equals(location.getWorld())) + { + continue; + } + + if (!(player.getLocation().distanceSquared(location) <= (landmine.radius * landmine.radius))) + { + break; + } + + landmine.location.getBlock().setType(Material.AIR); + + final TNTPrimed tnt1 = location.getWorld().spawn(location, TNTPrimed.class); + tnt1.setFuseTicks(40); + tnt1.setPassenger(player); + tnt1.setVelocity(new Vector(0.0, 2.0, 0.0)); + + final TNTPrimed tnt2 = location.getWorld().spawn(player.getLocation(), TNTPrimed.class); + tnt2.setFuseTicks(1); + + player.setGameMode(GameMode.SURVIVAL); + lit.remove(); + } + } + + public List getLandmines() + { + return landmines; + } + + public static class Landmine + { + private final Location location; + private final Player planter; + private final double radius; + + public Landmine(Location location, Player player, double radius) + { + this.location = location; + this.planter = player; + this.radius = radius; + } + + @Override + public String toString() + { + return this.location.toString() + ", " + this.radius + ", " + this.planter.getName(); + } + + public Location getLocation() + { + return location; + } + + public Player getPlanter() + { + return planter; + } + + public double getRadius() + { + return radius; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/fun/MP44.java b/src/main/java/me/totalfreedom/totalfreedommod/fun/MP44.java new file mode 100644 index 00000000..33e631eb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/fun/MP44.java @@ -0,0 +1,25 @@ +package me.totalfreedom.totalfreedommod.fun; + +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerQuitEvent; + +public class MP44 extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) + { + plugin.pl.getPlayer(event.getPlayer()).disarmMP44(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/fun/Trailer.java b/src/main/java/me/totalfreedom/totalfreedommod/fun/Trailer.java new file mode 100644 index 00000000..012dc4d7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/fun/Trailer.java @@ -0,0 +1,94 @@ +package me.totalfreedom.totalfreedommod.fun; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.SplittableRandom; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.Groups; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerMoveEvent; + +public class Trailer extends FreedomService +{ + private final SplittableRandom random = new SplittableRandom(); + private final Set trailPlayers = new HashSet<>(); // player name + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerMove(PlayerMoveEvent event) + { + if (trailPlayers.isEmpty()) + { + return; + } + + if (!trailPlayers.contains(event.getPlayer().getName())) + { + return; + } + + if (event.getPlayer().getWorld().equals(plugin.wm.masterBuilderWorld.getWorld())) + { + return; + } + + Block fromBlock = event.getFrom().getBlock(); + if (!fromBlock.isEmpty()) + { + return; + } + + Block toBlock = Objects.requireNonNull(event.getTo()).getBlock(); + if (fromBlock.equals(toBlock)) + { + return; + } + + fromBlock.setType(Groups.WOOL_COLORS.get(random.nextInt(Groups.WOOL_COLORS.size()))); + BlockData data = fromBlock.getBlockData(); + Material material = Material.getMaterial(String.valueOf(fromBlock.getType())); + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + final Location trail_pos; + trail_pos = new Location(event.getPlayer().getWorld(), fromBlock.getX() + x, fromBlock.getY(), fromBlock.getZ() + z); + if (trailPlayers.contains(event.getPlayer().getName()) && plugin.cpb.isEnabled()) + { + plugin.cpb.getCoreProtectAPI().logPlacement(event.getPlayer().getName(), trail_pos, material, data); + } + } + } + } + + public void remove(Player player) + { + trailPlayers.remove(player.getName()); + } + + public void add(Player player) + { + trailPlayers.add(player.getName()); + } + + public boolean contains(Player player) + { + return trailPlayers.contains(player.getName()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTMLGenerationTools.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTMLGenerationTools.java new file mode 100644 index 00000000..b682e8cc --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTMLGenerationTools.java @@ -0,0 +1,50 @@ +package me.totalfreedom.totalfreedommod.httpd; + +import java.util.Collection; +import java.util.Map; +import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4; + +public class HTMLGenerationTools +{ + public static String paragraph(String data) + { + return "

" + escapeHtml4(data) + "

\r\n"; + } + + public static String heading(String data, int level) + { + return "" + escapeHtml4(data) + "\r\n"; + } + + public static String list(Map map) + { + StringBuilder output = new StringBuilder(); + + output.append("
    \r\n"); + + for (Map.Entry entry : map.entrySet()) + { + output.append("
  • ").append(escapeHtml4(entry.getKey().toString() + " = " + entry.getValue().toString())).append("
  • \r\n"); + } + + output.append("
\r\n"); + + return output.toString(); + } + + public static String list(Collection list) + { + StringBuilder output = new StringBuilder(); + + output.append("
    \r\n"); + + for (T entry : list) + { + output.append("
  • ").append(escapeHtml4(entry.toString())).append("
  • \r\n"); + } + + output.append("
\r\n"); + + return output.toString(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDPageBuilder.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDPageBuilder.java new file mode 100644 index 00000000..b4027a0d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDPageBuilder.java @@ -0,0 +1,70 @@ +package me.totalfreedom.totalfreedommod.httpd; + +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; + +public class HTTPDPageBuilder +{ + + private static final String TEMPLATE = "\r\n" + + "\r\n" + + "\r\n" + + "{$TITLE}\r\n" + + "\r\n" + + "{$STYLE}" + + "{$SCRIPT}" + + "\r\n" + + "\r\n{$BODY}\r\n" + + "\r\n"; + private static final String STYLE = "\r\n"; + private static final String SCRIPT = "\r\n" + + "\r\n" + + "\r\n"; + // + private String body; + private String title; + private String style; + private String script; + + public HTTPDPageBuilder(String body, String title, String style, String script) + { + this.body = body; + this.title = title; + this.style = style; + this.script = script; + } + + public void setBody(String body) + { + this.body = body; + } + + public void setTitle(String title) + { + this.title = title; + } + + public void setStyle(String style) + { + this.style = style; + } + + public void setScript(String script) + { + this.script = script; + } + + public Response getResponse() + { + return new Response(this.toString()); + } + + @Override + public String toString() + { + return TEMPLATE + .replace("{$BODY}", this.body == null ? "" : this.body) + .replace("{$TITLE}", this.title == null ? "" : this.title) + .replace("{$STYLE}", this.style == null ? "" : STYLE.replace("{$STYLE}", this.style)) + .replace("{$SCRIPT}", this.script == null ? "" : SCRIPT.replace("{$SCRIPT}", this.script)); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDaemon.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDaemon.java new file mode 100644 index 00000000..177df52a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/HTTPDaemon.java @@ -0,0 +1,177 @@ +package me.totalfreedom.totalfreedommod.httpd; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; +import me.totalfreedom.totalfreedommod.httpd.module.HTTPDModule; +import me.totalfreedom.totalfreedommod.httpd.module.Module_activitylog; +import me.totalfreedom.totalfreedommod.httpd.module.Module_admins; +import me.totalfreedom.totalfreedommod.httpd.module.Module_bans; +import me.totalfreedom.totalfreedommod.httpd.module.Module_file; +import me.totalfreedom.totalfreedommod.httpd.module.Module_help; +import me.totalfreedom.totalfreedommod.httpd.module.Module_indefbans; +import me.totalfreedom.totalfreedommod.httpd.module.Module_index; +import me.totalfreedom.totalfreedommod.httpd.module.Module_list; +import me.totalfreedom.totalfreedommod.httpd.module.Module_logfile; +import me.totalfreedom.totalfreedommod.httpd.module.Module_logs; +import me.totalfreedom.totalfreedommod.httpd.module.Module_players; +import me.totalfreedom.totalfreedommod.httpd.module.Module_punishments; +import me.totalfreedom.totalfreedommod.httpd.module.Module_schematic; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.exception.ExceptionUtils; + +public class HTTPDaemon extends FreedomService +{ + + private static final Pattern EXT_REGEX = Pattern.compile("\\.([^.\\s]+)$"); + public static String MIME_DEFAULT_BINARY = "application/octet-stream"; + // + public int port; + public Map modules = new HashMap<>(); + private HTTPD httpd; + + public static Response serveFileBasic(File file) + { + Response response = null; + + if (file != null && file.exists()) + { + try + { + String mimetype = null; + + Matcher matcher = EXT_REGEX.matcher(file.getCanonicalPath()); + if (matcher.find()) + { + mimetype = Module_file.MIME_TYPES.get(matcher.group(1)); + } + + if (mimetype == null || mimetype.trim().isEmpty()) + { + mimetype = MIME_DEFAULT_BINARY; + } + + // Some browsers like firefox download the file for text/yaml mime types + if (FilenameUtils.getExtension(file.getName()).equals("yml")) + { + mimetype = NanoHTTPD.MIME_PLAINTEXT; + } + + response = new Response(Response.Status.OK, mimetype, new FileInputStream(file)); + response.addHeader("Content-Length", "" + file.length()); + } + catch (IOException ex) + { + FLog.severe(ex); + } + } + return response; + } + + @Override + public void onStart() + { + if (!ConfigEntry.HTTPD_ENABLED.getBoolean()) + { + return; + } + + port = ConfigEntry.HTTPD_PORT.getInteger(); + httpd = new HTTPD(port); + + // Modules + modules.clear(); + module("activitylog", Module_activitylog.class, true); + module("admins", Module_admins.class, true); + module("bans", Module_bans.class, true); + module("help", Module_help.class, false); + module("list", Module_list.class, false); + module("logfile", Module_logfile.class, true); + module("logs", Module_logs.class, true); + module("indefbans", Module_indefbans.class, true); + module("players", Module_players.class, false); + module("punishments", Module_punishments.class, true); + module("schematic", Module_schematic.class, true); + module("index", Module_index.class, false); + + try + { + httpd.start(); + + if (httpd.isAlive()) + { + FLog.info("TFM HTTPd started. Listening on port: " + httpd.getListeningPort()); + } + else + { + FLog.info("Error starting TFM HTTPd."); + } + } + catch (IOException ex) + { + FLog.severe(ex); + } + } + + @Override + public void onStop() + { + if (!ConfigEntry.HTTPD_ENABLED.getBoolean()) + { + return; + } + + httpd.stop(); + + FLog.info("TFM HTTPd stopped."); + } + + private void module(String name, Class clazz, boolean async) + { + modules.put(name, ModuleExecutable.forClass(clazz, async)); + } + + private class HTTPD extends NanoHTTPD + { + private HTTPD(int port) + { + super(port); + } + + @Override + public Response serve(HTTPSession session) + { + final String[] args = StringUtils.split(session.getUri(), "/"); + + ModuleExecutable mex = modules.get("file"); + if (args.length >= 1) + { + mex = modules.get(args[0].toLowerCase()); + } + + if (mex == null) + { + return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Error 404: Not Found - The requested resource was not found on this server."); + } + + try + { + return mex.execute(session); + } + catch (Exception ex) + { + FLog.severe(ex); + return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Error 500: Internal Server Error\r\n" + ex.getMessage() + "\r\n" + ExceptionUtils.getStackTrace(ex)); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/ModuleExecutable.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/ModuleExecutable.java new file mode 100644 index 00000000..a345f0ac --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/ModuleExecutable.java @@ -0,0 +1,74 @@ +package me.totalfreedom.totalfreedommod.httpd; + +import java.lang.reflect.Constructor; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.httpd.module.HTTPDModule; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.Bukkit; + +public abstract class ModuleExecutable +{ + private final boolean async; + + public ModuleExecutable(boolean async) + { + this.async = async; + } + + public static ModuleExecutable forClass(Class clazz, boolean async) + { + final Constructor cons; + try + { + cons = clazz.getConstructor(NanoHTTPD.HTTPSession.class); + } + catch (Exception ex) + { + throw new IllegalArgumentException("Improperly defined module!"); + } + + return new ModuleExecutable(async) + { + @Override + public NanoHTTPD.Response getResponse(NanoHTTPD.HTTPSession session) + { + try + { + return cons.newInstance(session).getResponse(); + } + catch (Exception ex) + { + FLog.severe(ex); + return null; + } + } + }; + } + + public NanoHTTPD.Response execute(final NanoHTTPD.HTTPSession session) + { + try + { + if (async) + { + return getResponse(session); + } + + // Sync to server thread + return Bukkit.getScheduler().callSyncMethod(TotalFreedomMod.getPlugin(), () -> getResponse(session)).get(); + + } + catch (Exception ex) + { + FLog.severe(ex); + } + return null; + } + + public abstract NanoHTTPD.Response getResponse(NanoHTTPD.HTTPSession session); + + public boolean isAsync() + { + return async; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/NanoHTTPD.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/NanoHTTPD.java new file mode 100644 index 00000000..c0566cc1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/NanoHTTPD.java @@ -0,0 +1,1582 @@ +package me.totalfreedom.totalfreedommod.httpd; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.io.SequenceInputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.URLDecoder; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TimeZone; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.jetbrains.annotations.NotNull; + +/** + * A simple, tiny, nicely embeddable HTTP server in Java + *

+ *

+ * NanoHTTPD + *

+ *

Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias

+ *

+ *

+ * Features + limitations: + *

    + *

    + *

  • Only one Java file
  • + *
  • Java 5 compatible
  • + *
  • Released as open source, Modified BSD licence
  • + *
  • No fixed config files, logging, authorization etc. (Implement yourself if you need them.)
  • + *
  • Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)
  • + *
  • Supports both dynamic content and file serving
  • + *
  • Supports file upload (since version 1.2, 2010)
  • + *
  • Supports partial content (streaming)
  • + *
  • Supports ETags
  • + *
  • Never caches anything
  • + *
  • Doesn't limit bandwidth, request time or simultaneous connections
  • + *
  • Default code serves files and shows all HTTP parameters and headers
  • + *
  • File server supports directory listing, index.html and index.htm
  • + *
  • File server supports partial content (streaming)
  • + *
  • File server supports ETags
  • + *
  • File server does the 301 redirection trick for directories without '/'
  • + *
  • File server supports simple skipping for files (continue download)
  • + *
  • File server serves also very long files without memory overhead
  • + *
  • Contains a built-in list of most common mime types
  • + *
  • All header names are converted lowercase so they don't vary between browsers/clients
  • + *

    + *

+ *

+ *

+ * How to use: + *

    + *

    + *

  • Subclass and implement serve() and embed to your own program
  • + *

    + *

+ *

+ * See the separate "LICENSE.md" file for the distribution license (Modified BSD licence) + */ +//codebeat:disable +public abstract class NanoHTTPD +{ + + /** + * Common mime type for dynamic content: plain text + */ + public static final String MIME_PLAINTEXT = "text/plain"; + /** + * Common mime type for dynamic content: html + */ + public static final String MIME_HTML = "text/html"; + // TFM Start + /** + * Common mime type for dynamic content: json + */ + public static final String MIME_JSON = "application/json"; + // TFM End + /** + * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing. + */ + private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; + private final String hostname; + private final int myPort; + private ServerSocket myServerSocket; + private Thread myThread; + /** + * Pluggable strategy for asynchronously executing requests. + */ + private AsyncRunner asyncRunner; + /** + * Pluggable strategy for creating and cleaning up temporary files. + */ + private TempFileManagerFactory tempFileManagerFactory; + + /** + * Constructs an HTTP server on given port. + */ + public NanoHTTPD(int port) + { + this(null, port); + } + + /** + * Constructs an HTTP server on given hostname and port. + */ + public NanoHTTPD(String hostname, int port) + { + this.hostname = hostname; + this.myPort = port; + setTempFileManagerFactory(new DefaultTempFileManagerFactory()); + setAsyncRunner(new DefaultAsyncRunner()); + } + + private static void safeClose(ServerSocket serverSocket) + { + if (serverSocket != null) + { + try + { + serverSocket.close(); + } + catch (IOException ignored) + { + } + } + } + + private static void safeClose(Socket socket) + { + if (socket != null) + { + try + { + socket.close(); + } + catch (IOException ignored) + { + } + } + } + + private static void safeClose(Closeable closeable) + { + if (closeable != null) + { + try + { + closeable.close(); + } + catch (IOException ignored) + { + } + } + } + + /** + * Start the server. + * + * @throws IOException if the socket is in use. + */ + public void start() throws IOException + { + myServerSocket = new ServerSocket(); + myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); + + myThread = new Thread(() -> + { + do + { + try + { + final Socket finalAccept = myServerSocket.accept(); + final InputStream inputStream = finalAccept.getInputStream(); + if (inputStream == null) + { + safeClose(finalAccept); + } + else + { + asyncRunner.exec(() -> + { + OutputStream outputStream = null; + try + { + outputStream = finalAccept.getOutputStream(); + TempFileManager tempFileManager = tempFileManagerFactory.create(); + HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept); + while (!finalAccept.isClosed()) + { + session.execute(); + } + } + catch (Exception e) + { + // When the socket is closed by the client, we throw our own SocketException + // to break the "keep alive" loop above. + if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) + { + FLog.severe(e); + } + } + finally + { + safeClose(outputStream); + safeClose(inputStream); + safeClose(finalAccept); + } + }); + } + } + catch (IOException ignored) + { + } + } + while (!myServerSocket.isClosed()); + }); + myThread.setDaemon(true); + myThread.setName("NanoHttpd Main Listener"); + myThread.start(); + } + + /** + * Stop the server. + */ + public void stop() + { + try + { + safeClose(myServerSocket); + //This should prevent a null pointer :) + if (myThread != null) + { + myThread.join(); + } + } + catch (Exception e) + { + FLog.severe(e); + } + } + + public final int getListeningPort() + { + return myServerSocket == null ? -1 : myServerSocket.getLocalPort(); + } + + public final boolean wasStarted() + { + return myServerSocket != null && myThread != null; + } + + public final boolean isAlive() + { + return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive(); + } + + /** + * Override this to customize the server. + *

+ *

+ * (By default, this delegates to serveFile() and allows directory listing.) + * + * @param uri Percent-decoded URI without parameters, for example "/index.cgi" + * @param method "GET", "POST" etc. + * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data. + * @param headers Header entries, percent decoded + * @return HTTP response, see class Response for details + */ + @Deprecated + public Response serve(String uri, Method method, Map headers, Map parms, + Map files) + { + return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found"); + } + + /** + * Override this to customize the server. + *

+ *

+ * (By default, this delegates to serveFile() and allows directory listing.) + * + * @param session The HTTP session + * @return HTTP response, see class Response for details + */ + public Response serve(HTTPSession session) + { + Map files = new HashMap<>(); + Method method = session.getMethod(); + if (Method.PUT.equals(method) || Method.POST.equals(method)) + { + try + { + session.parseBody(files); + } + catch (IOException ioe) + { + return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); + } + catch (ResponseException re) + { + return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage()); + } + } + + return serve(session.getUri(), method, session.getHeaders(), session.getParms(), files); + } + + /** + * Decode percent encoded String values. + * + * @param str the percent encoded String + * @return expanded form of the input, for example "foo%20bar" becomes "foo bar" + */ + protected String decodePercent(String str) + { + String decoded = null; + try + { + decoded = URLDecoder.decode(str, "UTF8"); + } + catch (UnsupportedEncodingException ignored) + { + } + return decoded; + } + + /** + * Decode parameters from a URL, handing the case where a single parameter name might have been supplied several times, by return lists of values. In general these lists will contain a single + * element. + * + * @param parms original NanoHttpd parameters values, as passed to the serve() method. + * @return a map of String (parameter name) to List<String> (a list of the values supplied). + */ + protected Map> decodeParameters(Map parms) + { + return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER)); + } + + /** + * Decode parameters from a URL, handing the case where a single parameter name might have been supplied several times, by return lists of values. In general these lists will contain a single + * element. + * + * @param queryString a query string pulled from the URL. + * @return a map of String (parameter name) to List<String> (a list of the values supplied). + */ + protected Map> decodeParameters(String queryString) + { + Map> parms = new HashMap<>(); + if (queryString != null) + { + StringTokenizer st = new StringTokenizer(queryString, "&"); + while (st.hasMoreTokens()) + { + String e = st.nextToken(); + int sep = e.indexOf('='); + String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); + if (!parms.containsKey(propertyName)) + { + parms.put(propertyName, new ArrayList<>()); + } + String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null; + if (propertyValue != null) + { + parms.get(propertyName).add(propertyValue); + } + } + } + return parms; + } + + // ------------------------------------------------------------------------------- // + // + // Threading Strategy. + // + // ------------------------------------------------------------------------------- // + + /** + * Pluggable strategy for asynchronously executing requests. + * + * @param asyncRunner new strategy for handling threads. + */ + public void setAsyncRunner(AsyncRunner asyncRunner) + { + this.asyncRunner = asyncRunner; + } + + // ------------------------------------------------------------------------------- // + // + // Temp file handling strategy. + // + // ------------------------------------------------------------------------------- // + + /** + * Pluggable strategy for creating and cleaning up temporary files. + * + * @param tempFileManagerFactory new strategy for handling temp files. + */ + public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) + { + this.tempFileManagerFactory = tempFileManagerFactory; + } + + /** + * HTTP Request methods, with the ability to decode a String back to its enum value. + */ + public enum Method + { + + GET, PUT, POST, DELETE, HEAD; + + static Method lookup(String method) + { + for (Method m : Method.values()) + { + if (m.toString().equalsIgnoreCase(method)) + { + return m; + } + } + return null; + } + } + + /** + * Pluggable strategy for asynchronously executing requests. + */ + public interface AsyncRunner + { + + void exec(Runnable code); + } + + /** + * Factory to create temp file managers. + */ + public interface TempFileManagerFactory + { + + TempFileManager create(); + } + + // ------------------------------------------------------------------------------- // + + /** + * Temp file manager. + *

+ *

+ * Temp file managers are created 1-to-1 with incoming requests, to create and cleanup temporary files created as a result of handling the request.

+ */ + public interface TempFileManager + { + + TempFile createTempFile() throws Exception; + + void clear(); + } + + /** + * A temp file. + *

+ *

+ * Temp files are responsible for managing the actual temporary storage and cleaning themselves up when no longer needed.

+ */ + public interface TempFile + { + + OutputStream open() throws Exception; + + void delete() throws Exception; + + String getName(); + } + + /** + * Default threading strategy for NanoHttpd. + *

+ *

+ * By default, the server spawns a new Thread for every incoming request. These are set to + * daemon status, and named according to the request number. The name is useful when profiling the application.

+ */ + public static class DefaultAsyncRunner implements AsyncRunner + { + + private long requestCount; + + @Override + public void exec(Runnable code) + { + ++requestCount; + Thread t = new Thread(code); + t.setDaemon(true); + t.setName("NanoHttpd Request Processor (#" + requestCount + ")"); + t.start(); + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + *

+ *

+ *

This class stores its files in the standard location (that is, wherever java.io.tmpdir points to). Files are added to an internal list, and deleted when no longer needed (that + * is, when clear() is invoked at the end of processing a request).

+ */ + public static class DefaultTempFileManager implements TempFileManager + { + + private final String tmpdir; + private final List tempFiles; + + public DefaultTempFileManager() + { + tmpdir = System.getProperty("java.io.tmpdir"); + tempFiles = new ArrayList<>(); + } + + @Override + public TempFile createTempFile() throws Exception + { + DefaultTempFile tempFile = new DefaultTempFile(tmpdir); + tempFiles.add(tempFile); + return tempFile; + } + + @Override + public void clear() + { + for (TempFile file : tempFiles) + { + try + { + file.delete(); + } + catch (Exception ignored) + { + } + } + tempFiles.clear(); + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + *

+ *

+ *

By default, files are created by File.createTempFile() in the directory specified.

+ */ + public static class DefaultTempFile implements TempFile + { + + private final File file; + private final OutputStream fstream; + + public DefaultTempFile(String tempdir) throws IOException + { + file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); + fstream = new FileOutputStream(file); + } + + @Override + public OutputStream open() + { + return fstream; + } + + @Override + public void delete() throws Exception + { + safeClose(fstream); + file.delete(); + } + + @Override + public String getName() + { + return file.getAbsolutePath(); + } + } + + /** + * HTTP response. Return one of these from serve(). + */ + public static class Response + { + + /** + * Headers for the HTTP response. Use addHeader() to add lines. + */ + private final Map header = new HashMap<>(); + /** + * HTTP status code after processing, e.g. "200 OK", HTTP_OK + */ + private Status status; + /** + * MIME type of content, e.g. "text/html" + */ + private String mimeType; + /** + * Data of the response, may be null. + */ + private InputStream data; + /** + * The request method that spawned this response. + */ + private Method requestMethod; + /** + * Use chunkedTransfer + */ + private boolean chunkedTransfer; + + /** + * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message + */ + public Response(String msg) + { + this(Status.OK, MIME_HTML, msg); + } + + /** + * Basic constructor. + */ + public Response(Status status, String mimeType, InputStream data) + { + this.status = status; + this.mimeType = mimeType; + this.data = data; + } + + /** + * Convenience method that makes an InputStream out of given text. + */ + public Response(Status status, String mimeType, String txt) + { + this.status = status; + this.mimeType = mimeType; + this.data = txt != null ? new ByteArrayInputStream(txt.getBytes(StandardCharsets.UTF_8)) : null; + } + + /** + * Adds given line to the header. + */ + public void addHeader(String name, String value) + { + header.put(name, value); + } + + /** + * Sends given response to the socket. + */ + private void send(OutputStream outputStream) + { + String mime = mimeType; + SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); + gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); + + try + { + if (status == null) + { + throw new Error("sendResponse(): Status can't be null."); + } + PrintWriter pw = new PrintWriter(outputStream); + pw.print("HTTP/1.1 " + status.getDescription() + " \r\n"); + + if (mime != null) + { + pw.print("Content-Type: " + mime + "\r\n"); + } + + if (header.get("Date") == null) + { + pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); + } + + for (String key : header.keySet()) + { + String value = header.get(key); + pw.print(key + ": " + value + "\r\n"); + } + + pw.print("Connection: keep-alive\r\n"); + + if (requestMethod != Method.HEAD && chunkedTransfer) + { + sendAsChunked(outputStream, pw); + } + else + { + sendAsFixedLength(outputStream, pw); + } + outputStream.flush(); + safeClose(data); + } + catch (IOException ioe) + { + // Couldn't write? No can do. + } + } + + private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException + { + pw.print("Transfer-Encoding: chunked\r\n"); + pw.print("\r\n"); + pw.flush(); + int BUFFER_SIZE = 16 * 1024; + byte[] CRLF = "\r\n".getBytes(); + byte[] buff = new byte[BUFFER_SIZE]; + int read; + while ((read = data.read(buff)) > 0) + { + outputStream.write(String.format("%x\r\n", read).getBytes()); + outputStream.write(buff, 0, read); + outputStream.write(CRLF); + } + outputStream.write("0\r\n\r\n".getBytes()); + } + + private void sendAsFixedLength(OutputStream outputStream, PrintWriter pw) throws IOException + { + int pending = data != null ? data.available() : 0; // This is to support partial sends, see serveFile() + pw.print("Content-Length: " + pending + "\r\n"); + + pw.print("\r\n"); + pw.flush(); + + if (requestMethod != Method.HEAD && data != null) + { + int BUFFER_SIZE = 16 * 1024; + byte[] buff = new byte[BUFFER_SIZE]; + while (pending > 0) + { + int read = data.read(buff, 0, (Math.min(pending, BUFFER_SIZE))); + if (read <= 0) + { + break; + } + outputStream.write(buff, 0, read); + + pending -= read; + } + } + } + + public Status getStatus() + { + return status; + } + + public void setStatus(Status status) + { + this.status = status; + } + + public String getMimeType() + { + return mimeType; + } + + public void setMimeType(String mimeType) + { + this.mimeType = mimeType; + } + + public InputStream getData() + { + return data; + } + + public void setData(InputStream data) + { + this.data = data; + } + + public Method getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(Method requestMethod) + { + this.requestMethod = requestMethod; + } + + public void setChunkedTransfer(boolean chunkedTransfer) + { + this.chunkedTransfer = chunkedTransfer; + } + + /** + * Some HTTP response status codes + */ + public enum Status + { + + OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NO_CONTENT(204, "No Content"), PARTIAL_CONTENT(206, "Partial Content"), REDIRECT(301, + "Moved Permanently"), NOT_MODIFIED(304, "Not Modified"), BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, + "Unauthorized"), FORBIDDEN(403, "Forbidden"), NOT_FOUND(404, "Not Found"), RANGE_NOT_SATISFIABLE(416, + "Requested Range Not Satisfiable"), INTERNAL_ERROR(500, "Internal Server Error"); + private final int requestStatus; + private final String description; + + Status(int requestStatus, String description) + { + this.requestStatus = requestStatus; + this.description = description; + } + + public int getRequestStatus() + { + return this.requestStatus; + } + + public String getDescription() + { + return "" + this.requestStatus + " " + description; + } + } + + } + + public static final class ResponseException extends Exception + { + + private final Response.Status status; + + public ResponseException(Response.Status status, String message) + { + super(message); + this.status = status; + } + + public ResponseException(Response.Status status, String message, Exception e) + { + super(message, e); + this.status = status; + } + + public Response.Status getStatus() + { + return status; + } + } + + public static class Cookie + { + + private final String n; + private final String v; + private final String e; + + public Cookie(String name, String value, String expires) + { + n = name; + v = value; + e = expires; + } + + public Cookie(String name, String value) + { + this(name, value, 30); + } + + public Cookie(String name, String value, int numDays) + { + n = name; + v = value; + e = getHTTPTime(numDays); + } + + public static String getHTTPTime(int days) + { + Calendar calendar = Calendar.getInstance(); + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + calendar.add(Calendar.DAY_OF_MONTH, days); + return dateFormat.format(calendar.getTime()); + } + + public String getHTTPHeader() + { + String fmt = "%s=%s; expires=%s"; + return String.format(fmt, n, v, e); + } + } + + /** + * Default strategy for creating and cleaning up temporary files. + */ + private static class DefaultTempFileManagerFactory implements TempFileManagerFactory + { + + @Override + public TempFileManager create() + { + return new DefaultTempFileManager(); + } + } + + /** + * Provides rudimentary support for cookies. Doesn't support 'path', 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported features. + * + * @author LordFokas + */ + public static class CookieHandler implements Iterable + { + + private final HashMap cookies = new HashMap<>(); + private final ArrayList queue = new ArrayList<>(); + + public CookieHandler(Map httpHeaders) + { + String raw = httpHeaders.get("cookie"); + if (raw != null) + { + String[] tokens = raw.split(";"); + for (String token : tokens) + { + String[] data = token.trim().split("="); + if (data.length == 2) + { + cookies.put(data[0], data[1]); + } + } + } + } + + @Override + public @NotNull Iterator iterator() + { + return cookies.keySet().iterator(); + } + + /** + * Read a cookie from the HTTP Headers. + * + * @param name The cookie's name. + * @return The cookie's value if it exists, null otherwise. + */ + public String read(String name) + { + return cookies.get(name); + } + + /** + * Sets a cookie. + * + * @param name The cookie's name. + * @param value The cookie's value. + * @param expires How many days until the cookie expires. + */ + public void set(String name, String value, int expires) + { + queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); + } + + public void set(Cookie cookie) + { + queue.add(cookie); + } + + /** + * Set a cookie with an expiration date from a month ago, effectively deleting it on the client side. + * + * @param name The cookie name. + */ + public void delete(String name) + { + set(name, "-delete-", -30); + } + + /** + * Internally used by the webserver to add all queued cookies into the Response's HTTP Headers. + * + * @param response The Response object to which headers the queued cookies will be added. + */ + public void unloadQueue(Response response) + { + for (Cookie cookie : queue) + { + response.addHeader("Set-Cookie", cookie.getHTTPHeader()); + } + } + } + + /** + * Handles one session, i.e. parses the HTTP request and returns the response. + */ + public class HTTPSession // TFM - protected -> public + { + + public static final int BUFSIZE = 8192; + private final TempFileManager tempFileManager; + private final OutputStream outputStream; + private final Socket socket; + private InputStream inputStream; + private int splitbyte; + private int rlen; + private String uri; + private Method method; + private Map parms; + private Map headers; + private CookieHandler cookies; + + public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, Socket socket) + { + this.tempFileManager = tempFileManager; + this.inputStream = inputStream; + this.outputStream = outputStream; + this.socket = socket; + } + + public void execute() throws IOException + { + try + { + // Read the first 8192 bytes. + // The full header should fit in here. + // Apache's default header limit is 8KB. + // Do NOT assume that a single read will get the entire header at once! + byte[] buf = new byte[BUFSIZE]; + splitbyte = 0; + rlen = 0; + { + int read = inputStream.read(buf, 0, BUFSIZE); + if (read == -1) + { + // socket was been closed + throw new SocketException("NanoHttpd Shutdown"); + } + while (read > 0) + { + rlen += read; + splitbyte = findHeaderEnd(buf, rlen); + if (splitbyte > 0) + { + break; + } + read = inputStream.read(buf, rlen, BUFSIZE - rlen); + } + } + + if (splitbyte < rlen) + { + ByteArrayInputStream splitInputStream = new ByteArrayInputStream(buf, splitbyte, rlen - splitbyte); + inputStream = new SequenceInputStream(splitInputStream, inputStream); + } + + parms = new HashMap<>(); + headers = new HashMap<>(); + + // Create a BufferedReader for parsing the header. + BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen))); + + // Decode the header into parms and header java properties + Map pre = new HashMap<>(); + decodeHeader(hin, pre, parms, headers); + + method = Method.lookup(pre.get("method")); + if (method == null) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); + } + + uri = pre.get("uri"); + + cookies = new CookieHandler(headers); + + // Ok, now do the serve() + Response r = serve(this); + if (r == null) + { + throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); + } + else + { + cookies.unloadQueue(r); + r.setRequestMethod(method); + r.send(outputStream); + } + } + catch (SocketException e) + { + // throw it out to close socket object (finalAccept) + throw e; + } + catch (IOException ioe) + { + Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); + r.send(outputStream); + safeClose(outputStream); + } + catch (ResponseException re) + { + Response r = new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage()); + r.send(outputStream); + safeClose(outputStream); + } + finally + { + tempFileManager.clear(); + } + } + + public void parseBody(Map files) throws IOException, ResponseException // TFM - protected -> public + { + RandomAccessFile randomAccessFile = null; + BufferedReader in = null; + try + { + + randomAccessFile = getTmpBucket(); + + long size; + if (headers.containsKey("content-length")) + { + size = Integer.parseInt(headers.get("content-length")); + } + else if (splitbyte < rlen) + { + size = rlen - splitbyte; + } + else + { + size = 0; + } + + // Now read all the body and write it to f + byte[] buf = new byte[512]; + while (rlen >= 0 && size > 0) + { + rlen = inputStream.read(buf, 0, 512); + size -= rlen; + if (rlen > 0) + { + assert randomAccessFile != null; + randomAccessFile.write(buf, 0, rlen); + } + } + + // Get the raw body as a byte [] + assert randomAccessFile != null; + ByteBuffer fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); + randomAccessFile.seek(0); + + // Create a BufferedReader for easily reading it as string. + InputStream bin = new FileInputStream(randomAccessFile.getFD()); + in = new BufferedReader(new InputStreamReader(bin)); + + // If the method is POST, there may be parameters + // in data section, too, read it: + if (Method.POST.equals(method)) + { + String contentType = ""; + String contentTypeHeader = headers.get("content-type"); + + StringTokenizer st = null; + if (contentTypeHeader != null) + { + st = new StringTokenizer(contentTypeHeader, ",; "); + if (st.hasMoreTokens()) + { + contentType = st.nextToken(); + } + } + + if ("multipart/form-data".equalsIgnoreCase(contentType)) + { + // Handle multipart/form-data + if (!st.hasMoreTokens()) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); + } + + String boundaryStartString = "boundary="; + int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length(); + String boundary = contentTypeHeader.substring(boundaryContentStart); + if (boundary.startsWith("\"") && boundary.endsWith("\"")) + { + boundary = boundary.substring(1, boundary.length() - 1); + } + + decodeMultipartData(boundary, fbuf, in, parms, files); + } + else + { + // Handle application/x-www-form-urlencoded + StringBuilder postLine = new StringBuilder(); + char[] pbuf = new char[512]; + int read = in.read(pbuf); + while (read >= 0 && !postLine.toString().endsWith("\r\n")) + { + postLine.append(String.valueOf(pbuf, 0, read)); + read = in.read(pbuf); + } + postLine = new StringBuilder(postLine.toString().trim()); + decodeParms(postLine.toString(), parms); + } + } + else if (Method.PUT.equals(method)) + { + files.put("content", saveTmpFile(fbuf, 0, fbuf.limit())); + } + } + finally + { + safeClose(randomAccessFile); + safeClose(in); + } + } + + /** + * Decodes the sent headers and loads the data into Key/value pairs + */ + private void decodeHeader(BufferedReader in, Map pre, Map parms, Map headers) + throws ResponseException + { + try + { + // Read the request line + String inLine = in.readLine(); + if (inLine == null) + { + return; + } + + StringTokenizer st = new StringTokenizer(inLine); + if (!st.hasMoreTokens()) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); + } + + pre.put("method", st.nextToken()); + + if (!st.hasMoreTokens()) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); + } + + String uri = st.nextToken(); + + // Decode parameters from the URI + int qmi = uri.indexOf('?'); + if (qmi >= 0) + { + decodeParms(uri.substring(qmi + 1), parms); + uri = decodePercent(uri.substring(0, qmi)); + } + else + { + uri = decodePercent(uri); + } + + // If there's another token, it's protocol version, + // followed by HTTP headers. Ignore version but parse headers. + // NOTE: this now forces header names lowercase since they are + // case insensitive and vary by client. + if (st.hasMoreTokens()) + { + String line = in.readLine(); + while (line != null && line.trim().length() > 0) + { + int p = line.indexOf(':'); + if (p >= 0) + { + headers.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim()); + } + line = in.readLine(); + } + } + + pre.put("uri", uri); + } + catch (IOException ioe) + { + throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); + } + } + + /** + * Decodes the Multipart Body data and put it into Key/Value pairs. + */ + private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map parms, + Map files) throws ResponseException + { + try + { + int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes()); + int boundarycount = 1; + String mpline = in.readLine(); + while (mpline != null) + { + if (!mpline.contains(boundary)) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html"); + } + boundarycount++; + Map item = new HashMap<>(); + mpline = in.readLine(); + while (mpline != null && mpline.trim().length() > 0) + { + int p = mpline.indexOf(':'); + if (p != -1) + { + item.put(mpline.substring(0, p).trim().toLowerCase(), mpline.substring(p + 1).trim()); + } + mpline = in.readLine(); + } + if (mpline != null) + { + String contentDisposition = item.get("content-disposition"); + if (contentDisposition == null) + { + throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html"); + } + StringTokenizer st = new StringTokenizer(contentDisposition, "; "); + Map disposition = new HashMap<>(); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + int p = token.indexOf('='); + if (p != -1) + { + disposition.put(token.substring(0, p).trim().toLowerCase(), token.substring(p + 1).trim()); + } + } + String pname = disposition.get("name"); + pname = pname.substring(1, pname.length() - 1); + + StringBuilder value = new StringBuilder(); + if (item.get("content-type") == null) + { + while (mpline != null && !mpline.contains(boundary)) + { + mpline = in.readLine(); + if (mpline != null) + { + int d = mpline.indexOf(boundary); + if (d == -1) + { + value.append(mpline); + } + else + { + value.append(mpline, 0, d - 2); + } + } + } + } + else + { + if (boundarycount > bpositions.length) + { + throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request"); + } + int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]); + String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4); + files.put(pname, path); + value = new StringBuilder(disposition.get("filename")); + value = new StringBuilder(value.substring(1, value.length() - 1)); + do + { + mpline = in.readLine(); + } + while (mpline != null && !mpline.contains(boundary)); + } + parms.put(pname, value.toString()); + } + } + } + catch (IOException ioe) + { + throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); + } + } + + /** + * Find byte index separating header from body. It must be the last byte of the first two sequential new lines. + */ + private int findHeaderEnd(final byte[] buf, int rlen) + { + int splitbyte = 0; + while (splitbyte + 3 < rlen) + { + if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') + { + return splitbyte + 4; + } + splitbyte++; + } + return 0; + } + + /** + * Find the byte positions where multipart boundaries start. + */ + private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) + { + int matchcount = 0; + int matchbyte = -1; + List matchbytes = new ArrayList<>(); + for (int i = 0; i < b.limit(); i++) + { + if (b.get(i) == boundary[matchcount]) + { + if (matchcount == 0) + { + matchbyte = i; + } + matchcount++; + if (matchcount == boundary.length) + { + matchbytes.add(matchbyte); + matchcount = 0; + matchbyte = -1; + } + } + else + { + i -= matchcount; + matchcount = 0; + matchbyte = -1; + } + } + int[] ret = new int[matchbytes.size()]; + for (int i = 0; i < ret.length; i++) + { + ret[i] = matchbytes.get(i); + } + return ret; + } + + /** + * Retrieves the content of a sent file and saves it to a temporary file. The full path to the saved file is returned. + */ + private String saveTmpFile(ByteBuffer b, int offset, int len) + { + String path = ""; + if (len > 0) + { + FileOutputStream fileOutputStream = null; + try + { + TempFile tempFile = tempFileManager.createTempFile(); + ByteBuffer src = b.duplicate(); + fileOutputStream = new FileOutputStream(tempFile.getName()); + FileChannel dest = fileOutputStream.getChannel(); + src.position(offset).limit(offset + len); + dest.write(src.slice()); + path = tempFile.getName(); + } + catch (Exception e) + { // Catch exception if any + FLog.severe(e); + } + finally + { + safeClose(fileOutputStream); + } + } + return path; + } + + private RandomAccessFile getTmpBucket() + { + try + { + TempFile tempFile = tempFileManager.createTempFile(); + return new RandomAccessFile(tempFile.getName(), "rw"); + } + catch (Exception e) + { + FLog.severe(e); + } + return null; + } + + /** + * It returns the offset separating multipart file headers from the file's data. + */ + private int stripMultipartHeaders(ByteBuffer b, int offset) + { + int i; + for (i = offset; i < b.limit(); i++) + { + if (b.get(i) == '\r' && b.get(++i) == '\n' && b.get(++i) == '\r' && b.get(++i) == '\n') + { + break; + } + } + return i + 1; + } + + /** + * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given Map. NOTE: this doesn't support multiple identical keys due to the + * simplicity of Map. + */ + private void decodeParms(String parms, Map p) + { + if (parms == null) + { + p.put(QUERY_STRING_PARAMETER, ""); + return; + } + + p.put(QUERY_STRING_PARAMETER, parms); + StringTokenizer st = new StringTokenizer(parms, "&"); + while (st.hasMoreTokens()) + { + String e = st.nextToken(); + int sep = e.indexOf('='); + if (sep >= 0) + { + p.put(decodePercent(e.substring(0, sep)).trim(), + decodePercent(e.substring(sep + 1))); + } + else + { + p.put(decodePercent(e).trim(), ""); + } + } + } + + public final Map getParms() + { + return parms; + } + + public final Map getHeaders() + { + return headers; + } + + public final String getUri() + { + return uri; + } + + public final Method getMethod() + { + return method; + } + + public final InputStream getInputStream() + { + return inputStream; + } + + public CookieHandler getCookies() + { + return cookies; + } + + public Socket getSocket() + { + return socket; + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/HTTPDModule.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/HTTPDModule.java new file mode 100644 index 00000000..4ea99cef --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/HTTPDModule.java @@ -0,0 +1,83 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.httpd.HTTPDPageBuilder; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.HTTPSession; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Method; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; +import me.totalfreedom.totalfreedommod.util.FLog; + +public abstract class HTTPDModule extends FreedomService +{ + + protected final String uri; + protected final Method method; + protected final Map headers; + protected final Map params; + protected final Socket socket; + protected final HTTPSession session; + + public HTTPDModule(HTTPSession session) + { + this.uri = session.getUri(); + this.method = session.getMethod(); + this.headers = session.getHeaders(); + this.params = session.getParms(); + this.socket = session.getSocket(); + this.session = session; + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public String getBody() + { + return null; + } + + public String getTitle() + { + return null; + } + + public String getStyle() + { + return null; + } + + public String getScript() + { + return null; + } + + public Response getResponse() + { + return new HTTPDPageBuilder(getBody(), getTitle(), getStyle(), getScript()).getResponse(); + } + + protected final Map getFiles() + { + Map files = new HashMap<>(); + + try + { + session.parseBody(files); + } + catch (Exception ex) + { + FLog.severe(ex); + } + + return files; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_activitylog.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_activitylog.java new file mode 100644 index 00000000..2e3b8ab6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_activitylog.java @@ -0,0 +1,43 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import me.totalfreedom.totalfreedommod.admin.ActivityLog; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; + +public class Module_activitylog extends HTTPDModule +{ + public Module_activitylog(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + final String remoteAddress = socket.getRemoteSocketAddress().toString(); + + if (!isAuthorized(remoteAddress)) + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "You may not view the activity log. Your IP, " + remoteAddress + ", is not registered to an admin on the server."); + } + File activityLogFile = new File(plugin.getDataFolder(), ActivityLog.FILENAME); + if (activityLogFile.exists()) + { + return HTTPDaemon.serveFileBasic(new File(plugin.getDataFolder(), ActivityLog.FILENAME)); + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - The requested resource was not found on this server."); + } + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry != null && entry.isActive(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_admins.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_admins.java new file mode 100644 index 00000000..f03da6bc --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_admins.java @@ -0,0 +1,47 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; + +public class Module_admins extends HTTPDModule +{ + + public Module_admins(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - i have to re-work this"); + /*File adminFile = new File(plugin.getDataFolder(), Admin.CONFIG_FILENAME); + if (adminFile.exists()) + { + final String remoteAddress = socket.getInetAddress().getHostAddress(); + if (!isAuthorized(remoteAddress)) + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "You may not view the admin list. Your IP, " + remoteAddress + ", is not registered to an admin on the server."); + } + else + { + return HTTPDaemon.serveFileBasic(new File(plugin.getDataFolder(), Admin.CONFIG_FILENAME)); + } + + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - The requested resource was not found on this server."); + }*/ + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry != null && entry.isActive(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_bans.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_bans.java new file mode 100644 index 00000000..182fb5a7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_bans.java @@ -0,0 +1,46 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; + +public class Module_bans extends HTTPDModule +{ + + public Module_bans(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - i have to re-work this"); + /*File banFile = new File(plugin.getDataFolder(), BanManager.CONFIG_FILENAME); + if (banFile.exists()) + { + final String remoteAddress = socket.getInetAddress().getHostAddress(); + if (!isAuthorized(remoteAddress)) + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "You may not view the ban list. Your IP, " + remoteAddress + ", is not registered to an admin on the server."); + } + else + { + return HTTPDaemon.serveFileBasic(new File(plugin.getDataFolder(), BanManager.CONFIG_FILENAME)); + } + + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - The requested resource was not found on this server."); + }*/ + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry != null && entry.isActive(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_file.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_file.java new file mode 100644 index 00000000..da43ca2c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_file.java @@ -0,0 +1,364 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; +import org.apache.commons.lang3.StringUtils; + +/* + * This class was adapted from https://github.com/NanoHttpd/nanohttpd/blob/master/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java + */ +public class Module_file extends HTTPDModule +{ + + public static final Map MIME_TYPES = new HashMap<>(); + + static + { + MIME_TYPES.put("css", "text/css"); + MIME_TYPES.put("htm", "text/html"); + MIME_TYPES.put("html", "text/html"); + MIME_TYPES.put("xml", "text/xml"); + MIME_TYPES.put("java", "text/x-java-source, text/java"); + MIME_TYPES.put("txt", "text/plain"); + MIME_TYPES.put("asc", "text/plain"); + MIME_TYPES.put("yml", "text/yaml"); + MIME_TYPES.put("gif", "image/gif"); + MIME_TYPES.put("jpg", "image/jpeg"); + MIME_TYPES.put("jpeg", "image/jpeg"); + MIME_TYPES.put("png", "image/png"); + MIME_TYPES.put("mp3", "audio/mpeg"); + MIME_TYPES.put("m3u", "audio/mpeg-url"); + MIME_TYPES.put("mp4", "video/mp4"); + MIME_TYPES.put("ogv", "video/ogg"); + MIME_TYPES.put("flv", "video/x-flv"); + MIME_TYPES.put("mov", "video/quicktime"); + MIME_TYPES.put("swf", "application/x-shockwave-flash"); + MIME_TYPES.put("js", "application/javascript"); + MIME_TYPES.put("pdf", "application/pdf"); + MIME_TYPES.put("doc", "application/msword"); + MIME_TYPES.put("ogg", "application/x-ogg"); + MIME_TYPES.put("zip", "application/octet-stream"); + MIME_TYPES.put("exe", "application/octet-stream"); + MIME_TYPES.put("class", "application/octet-stream"); + } + + private final File rootDir = new File(ConfigEntry.HTTPD_PUBLIC_FOLDER.getString()); + + public Module_file(NanoHTTPD.HTTPSession session) + { + super(session); + } + + private File getRootDir() + { + return rootDir; + } + + private String encodeUri(String uri) + { + StringBuilder newUri = new StringBuilder(); + StringTokenizer st = new StringTokenizer(uri, "/ ", true); + while (st.hasMoreTokens()) + { + String tok = st.nextToken(); + if (tok.equals("/")) + { + newUri.append("/"); + } + else if (tok.equals(" ")) + { + newUri.append("%20"); + } + else + { + try + { + newUri.append(URLEncoder.encode(tok, "UTF-8")); + } + catch (UnsupportedEncodingException ignored) + { + } + } + } + return newUri.toString(); + } + + public Response serveFile(String uri, Map params, File homeDir) + { + Response res = null; + + // Make sure we won't die of an exception later + if (!homeDir.isDirectory()) + { + res = new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); + } + + if (res == null) + { + // Remove URL arguments + uri = uri.trim().replace(File.separatorChar, '/'); + if (uri.indexOf('?') >= 0) + { + uri = uri.substring(0, uri.indexOf('?')); + } + + // Prohibit getting out of current directory + if (uri.startsWith("src/main") || uri.endsWith("src/main") || uri.contains("../")) + { + res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); + } + } + + File f = new File(homeDir, uri); + if (res == null && !f.exists()) + { + res = new Response(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found."); + } + + // List the directory, if necessary + if (res == null && f.isDirectory()) + { + // Browsers get confused without '/' after the + // directory, send a redirect. + if (!uri.endsWith("/")) + { + uri += "/"; + res = new Response(Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "Redirected: " + uri + + ""); + res.addHeader("Location", uri); + } + + if (res == null) + { + // First try index.html and index.htm + if (new File(f, "index.html").exists()) + { + f = new File(homeDir, uri + "/index.html"); + } + else if (new File(f, "index.htm").exists()) + { + f = new File(homeDir, uri + "/index.htm"); + } + else if (f.canRead()) + { + // No index file, list the directory if it is readable + res = new Response(listDirectory(uri, f)); + } + else + { + res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: No directory listing."); + } + } + } + + try + { + if (res == null) + { + // Get MIME type from file name extension, if possible + String mime = null; + int dot = f.getCanonicalPath().lastIndexOf('.'); + if (dot >= 0) + { + mime = MIME_TYPES.get(f.getCanonicalPath().substring(dot + 1).toLowerCase()); + } + if (mime == null) + { + mime = HTTPDaemon.MIME_DEFAULT_BINARY; + } + + // Calculate etag + String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode()); + + final long fileLen = f.length(); + + long startFrom = 0; + long endAt = -1; + final String range = params.get("range"); + if (range != null) + { + final String[] rangeParams = StringUtils.split(range, "="); + if (rangeParams.length >= 2) + { + if ("bytes".equalsIgnoreCase(rangeParams[0])) + { + try + { + int minus = rangeParams[1].indexOf('-'); + if (minus > 0) + { + startFrom = Long.parseLong(rangeParams[1].substring(0, minus)); + endAt = Long.parseLong(rangeParams[1].substring(minus + 1)); + } + } + catch (NumberFormatException ignored) + { + } + } + else if ("tail".equalsIgnoreCase(rangeParams[0])) + { + try + { + final long tailLen = Long.parseLong(rangeParams[1]); + if (tailLen < fileLen) + { + startFrom = fileLen - tailLen - 2; + if (startFrom < 0) + { + startFrom = 0; + } + } + } + catch (NumberFormatException ignored) + { + } + } + } + } + + // Change return code and add Content-Range header when skipping is requested + if (range != null && startFrom >= 0) + { + if (startFrom >= fileLen) + { + res = new Response(Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, ""); + res.addHeader("Content-Range", "bytes 0-0/" + fileLen); + } + else + { + if (endAt < 0) + { + endAt = fileLen - 1; + } + long newLen = endAt - startFrom + 1; + if (newLen < 0) + { + newLen = 0; + } + + final long dataLen = newLen; + FileInputStream fis = new FileInputStream(f) + { + @Override + public int available() + { + return (int)dataLen; + } + }; + //noinspection ResultOfMethodCallIgnored + fis.skip(startFrom); + + res = new Response(Response.Status.PARTIAL_CONTENT, mime, fis); + res.addHeader("Content-Length", "" + dataLen); + res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen); + } + } + else + { + res = new Response(Response.Status.OK, mime, new FileInputStream(f)); + res.addHeader("Content-Length", "" + fileLen); + } + res.addHeader("ETag", etag); + } + } + catch (IOException ioe) + { + res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); + } + + res.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes + return res; + } + + private String listDirectory(String uri, File f) + { + String heading = "Directory " + uri; + StringBuilder msg = new StringBuilder("" + heading + "" + + "

" + heading + "

"); + + String up = null; + if (uri.length() > 1) + { + String u = uri.substring(0, uri.length() - 1); + int slash = u.lastIndexOf('/'); + if (slash >= 0 && slash < u.length()) + { + up = uri.substring(0, slash + 1); + } + } + + List files = Arrays.asList(f.list((dir, name) -> new File(dir, name).isFile())); + Collections.sort(files); + List directories = Arrays.asList(f.list((dir, name) -> new File(dir, name).isDirectory())); + Collections.sort(directories); + if (up != null || directories.size() + files.size() > 0) + { + msg.append("
    "); + if (up != null || directories.size() > 0) + { + msg.append("
    "); + if (up != null) + { + msg.append("
  • ..
  • "); + } + for (String directory : directories) + { + String dir = directory + "/"; + msg.append("
  • ").append(dir).append("
  • "); + } + msg.append("
    "); + } + if (files.size() > 0) + { + msg.append("
    "); + for (String file : files) + { + msg.append("
  • ").append(file).append(""); + File curFile = new File(f, file); + long len = curFile.length(); + msg.append(" ("); + if (len < 1024) + { + msg.append(len).append(" bytes"); + } + else if (len < 1024 * 1024) + { + msg.append(len / 1024).append(".").append(len % 1024 / 10 % 100).append(" KB"); + } + else + { + msg.append(len / (1024 * 1024)).append(".").append(len % (1024 * 1024) / 10 % 100).append(" MB"); + } + msg.append(")
  • "); + } + msg.append("
    "); + } + msg.append("
"); + } + msg.append(""); + return msg.toString(); + } + + @Override + public Response getResponse() + { + return serveFile(uri, params, getRootDir()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_help.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_help.java new file mode 100644 index 00000000..d19849be --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_help.java @@ -0,0 +1,151 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.command.FreedomCommand; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import org.apache.commons.lang.StringUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.command.SimpleCommandMap; +import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.heading; +import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.paragraph; +import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4; + +public class Module_help extends HTTPDModule +{ + + public Module_help(NanoHTTPD.HTTPSession session) + { + super(session); + } + + private static String buildDescription(Command command) + { + StringBuilder sb = new StringBuilder(); + + sb.append( + "
  • {$CMD_NAME} - Usage: {$CMD_USAGE}" + .replace("{$CMD_NAME}", escapeHtml4(command.getName().trim())) + .replace("{$CMD_USAGE}", escapeHtml4(command.getUsage().trim()))); + + if (!command.getAliases().isEmpty()) + { + sb.append( + " - Aliases: {$CMD_ALIASES}" + .replace("{$CMD_ALIASES}", escapeHtml4(StringUtils.join(command.getAliases(), ", ")))); + } + + sb.append( + "
    {$CMD_DESC}
  • \r\n" + .replace("{$CMD_DESC}", escapeHtml4(command.getDescription().trim()))); + + return sb.toString(); + } + + @Override + public String getBody() + { + final CommandMap map = FreedomCommand.getCommandMap(); + if (!(map instanceof SimpleCommandMap)) + { + return paragraph("Error loading commands."); + } + + final StringBuilder responseBody = new StringBuilder() + .append(heading("Command Help", 1)) + .append(paragraph( + "This page is an automatically generated listing of all plugin commands that are currently live on the server. " + + "Please note that it does not include vanilla server commands.")); + + final Collection knownCommands = ((SimpleCommandMap)map).getCommands(); + final Map> commandsByPlugin = new HashMap<>(); + + for (Command command : knownCommands) + { + String pluginName = "Bukkit"; + if (command instanceof PluginIdentifiableCommand) + { + pluginName = ((PluginIdentifiableCommand)command).getPlugin().getName(); + } + + List pluginCommands = commandsByPlugin.computeIfAbsent(pluginName, k -> Lists.newArrayList()); + + pluginCommands.add(command); + } + + for (Map.Entry> entry : commandsByPlugin.entrySet()) + { + final String pluginName = entry.getKey(); + final List commands = entry.getValue(); + + commands.sort(new CommandComparator()); + + responseBody.append(heading(pluginName, 2)).append("
      \r\n"); + + Displayable lastTfmCommandLevel = null; + for (Command command : commands) + { + if (!TotalFreedomMod.pluginName.equals(pluginName)) + { + responseBody.append(buildDescription(command)); + continue; + } + + Displayable tfmCommandLevel = Objects.requireNonNull(FreedomCommand.getFrom(command)).getPerms().level(); + if (lastTfmCommandLevel == null || lastTfmCommandLevel != tfmCommandLevel) + { + responseBody.append("
    \r\n").append(heading(tfmCommandLevel.getName(), 3)).append("
      \r\n"); + } + lastTfmCommandLevel = tfmCommandLevel; + responseBody.append(buildDescription(command)); + } + + responseBody.append("
    \r\n"); + } + + return responseBody.toString(); + } + + @Override + public String getTitle() + { + return "TotalFreedomMod :: Command Help"; + } + + @Override + public String getStyle() + { + return ".commandName{font-weight:bold;}.commandDescription{padding-left:15px;}li{margin:.15em;padding:.15em;}"; + } + + public static class CommandComparator implements Comparator + { + + @Override + public int compare(Command a, Command b) + { + FreedomCommand ca = FreedomCommand.getFrom(a); + FreedomCommand cb = FreedomCommand.getFrom(b); + + if (ca == null + || cb == null + || ca.getPerms() == null + || cb.getPerms() == null) + { + return a.getName().compareTo(b.getName()); + } + + return ca.getPerms().level().getName().compareTo(cb.getPerms().level().getName()); + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_indefbans.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_indefbans.java new file mode 100644 index 00000000..a94e666a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_indefbans.java @@ -0,0 +1,43 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.banning.IndefiniteBanList; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; + +public class Module_indefbans extends HTTPDModule +{ + public Module_indefbans(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + File indefbanFile = new File(plugin.getDataFolder(), IndefiniteBanList.CONFIG_FILENAME); + + final String remoteAddress = socket.getRemoteSocketAddress().toString(); + if (!isAuthorized(remoteAddress)) + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "You may not view the indefinite ban list. Your IP, " + remoteAddress + ", is not registered to an admin on the server."); + } + if (indefbanFile.exists()) + { + return HTTPDaemon.serveFileBasic(new File(plugin.getDataFolder(), IndefiniteBanList.CONFIG_FILENAME)); + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - The requested resource was not found on this server."); + } + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry != null && entry.isActive(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_index.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_index.java new file mode 100644 index 00000000..eb98099e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_index.java @@ -0,0 +1,63 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.util.Set; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools; +import me.totalfreedom.totalfreedommod.httpd.HTTPDPageBuilder; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import org.reflections.Reflections; + +public class Module_index extends HTTPDModule +{ + + public Module_index(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + return new HTTPDPageBuilder(body(), title(), null, null).getResponse(); + } + + public String body() + { + final StringBuilder sb = new StringBuilder(); + + sb.append(HTMLGenerationTools.heading("TotalFreedom HTTPd Module List", 1)); + + Reflections r = new Reflections("me.totalfreedom.totalfreedommod.httpd.module"); + + Set> moduleClasses = r.getSubTypesOf(HTTPDModule.class); + + for (Class c : moduleClasses) + { + String name = c.getSimpleName().replace("Module_", ""); + + if (name.equals("file")) + { + continue; + } + + // index + sb.append(""); + } + return sb.toString(); + } + + public String title() + { + return "TotalFreedom :: HTTPd Modules"; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_list.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_list.java new file mode 100644 index 00000000..8ee00487 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_list.java @@ -0,0 +1,150 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.admin.AdminList; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.Collection; + +public class Module_list extends HTTPDModule +{ + + public Module_list(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + if (params.get("json") != null && params.get("json").equals("true")) + { + final JSONObject responseObject = new JSONObject(); + + final JSONArray owners = new JSONArray(); + final JSONArray executives = new JSONArray(); + final JSONArray developers = new JSONArray(); + final JSONArray senioradmins = new JSONArray(); + final JSONArray admins = new JSONArray(); + final JSONArray masterbuilders = new JSONArray(); + final JSONArray operators = new JSONArray(); + final JSONArray imposters = new JSONArray(); + + for (Player player : Bukkit.getOnlinePlayers()) + { + if (plugin.al.isVanished(player.getName())) + { + continue; + } + + if (isImposter(player)) + { + imposters.add(player.getName()); + } + + if (plugin.pl.getData(player).isMasterBuilder()) + { + masterbuilders.add(player.getName()); + } + + if (FUtil.DEVELOPER_NAMES.contains(player.getName())) + { + developers.add(player.getName()); + } + + if (ConfigEntry.SERVER_EXECUTIVES.getList().contains(player.getName()) && !FUtil.DEVELOPERS.contains(player.getName())) + { + executives.add(player.getName()); + } + + if (ConfigEntry.SERVER_OWNERS.getList().contains(player.getName())) + { + owners.add(player.getName()); + } + + if (!plugin.al.isAdmin(player) && hasSpecialTitle(player)) + { + operators.add(player.getName()); + } + + if (hasSpecialTitle(player) && plugin.al.isAdmin(player) && !plugin.al.isVanished(player.getName())) + { + Admin admin = plugin.al.getAdmin(player); + switch (admin.getRank()) + { + case ADMIN: + admins.add(player.getName()); + break; + case SENIOR_ADMIN: + senioradmins.add(player.getName()); + break; + } + } + } + + // for future refernce - any multi-worded ranks are to be delimited by underscores in the json; eg. senior_admins + responseObject.put("owners", owners); + responseObject.put("executives", executives); + responseObject.put("developers", developers); + responseObject.put("senior_admins", senioradmins); + responseObject.put("admins", admins); + responseObject.put("master_builders", masterbuilders); + responseObject.put("operators", operators); + responseObject.put("imposters", imposters); + responseObject.put("online", FUtil.getFakePlayerCount()); + responseObject.put("max", server.getMaxPlayers()); + + final NanoHTTPD.Response response = new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_JSON, responseObject.toString()); + response.addHeader("Access-Control-Allow-Origin", "*"); + return response; + } + else + { + final StringBuilder body = new StringBuilder(); + + final Collection onlinePlayers = Bukkit.getOnlinePlayers(); + + int count = onlinePlayers.size() - AdminList.vanished.size(); + body.append("

    There are ").append(count < 0 ? 0 : count).append("/") + .append(Bukkit.getMaxPlayers()).append(" players online:

    \r\n"); + + body.append("
      \r\n"); + + for (Player player : onlinePlayers) + { + if (plugin.al.isVanished(player.getName())) + { + continue; + } + String tag = plugin.rm.getDisplay(player).getTag(); + body.append("
    • ").append(tag).append(player.getName()).append("
    • \r\n"); + } + + body.append("
    \r\n"); + + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_HTML, body.toString()); + } + } + + public boolean isImposter(Player player) + { + return plugin.al.isAdminImpostor(player) || plugin.pl.isPlayerImpostor(player); + } + + public boolean hasSpecialTitle(Player player) + { + return !FUtil.DEVELOPERS.contains(player.getUniqueId().toString()) && !ConfigEntry.SERVER_EXECUTIVES.getList().contains(player.getName()) && !ConfigEntry.SERVER_OWNERS.getList().contains(player.getName()); + } + + @Override + public String getTitle() + { + return "TotalFreedom - Online Players"; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logfile.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logfile.java new file mode 100644 index 00000000..e2d7f55e --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logfile.java @@ -0,0 +1,219 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools; +import me.totalfreedom.totalfreedommod.httpd.HTTPDPageBuilder; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; + +public class Module_logfile extends HTTPDModule +{ + + private static final File LOG_FOLDER = new File("./logs/"); + private static final String[] LOG_FILTER = new String[] + { + "log", + "gz" + }; + + public Module_logfile(NanoHTTPD.HTTPSession session) + { + super(session); + } + + private static String getArg(String[] args) + { + String out = (args.length == 1 + 1 ? args[1] : null); + return (out == null ? null : (out.trim().isEmpty() ? null : out.trim())); + } + + @Override + public Response getResponse() + { + try + { + return new HTTPDPageBuilder(body(), title(), null, null).getResponse(); + } + catch (ResponseOverrideException ex) + { + return ex.getResponse(); + } + } + + public String title() + { + return "TotalFreedomMod :: Log Files"; + } + + public String body() throws ResponseOverrideException + { + if (!LOG_FOLDER.exists()) + { + FLog.warning("The logfile module failed to find the logs folder."); + return HTMLGenerationTools.paragraph("Can't find the logs folder."); + } + + final StringBuilder out = new StringBuilder(); + final String remoteAddress = socket.getRemoteSocketAddress().toString(); + final String[] args = StringUtils.split(uri, "/"); + final ModuleMode mode = ModuleMode.getMode(getArg(args)); + + switch (mode) + { + case LIST -> { + if (isAuthorized(remoteAddress)) + { + + out.append(HTMLGenerationTools.paragraph("Log files access denied: Your IP, " + remoteAddress + ", is not registered to an admin on this server.")); + FLog.info("An unregistered IP (" + remoteAddress + ") has tried to access the log files"); + } + else + { + Collection LogFiles = FileUtils.listFiles(LOG_FOLDER, LOG_FILTER, false); + + final List LogFilesFormatted = new ArrayList<>(); + for (File logfile : LogFiles) + { + String filename = StringEscapeUtils.escapeHtml4(logfile.getName()); + + LogFilesFormatted.add("
  • " + filename + "
  • "); + + } + + LogFilesFormatted.sort(Comparator.comparing(String::toLowerCase)); + + out + .append(HTMLGenerationTools.heading("Logfiles:", 1)) + .append("
      ") + .append(StringUtils.join(LogFilesFormatted, "\r\n")) + .append("
    "); + } + } + case DOWNLOAD -> { + if (isAuthorized(remoteAddress)) + { + out.append(HTMLGenerationTools.paragraph("Log files access denied: Your IP, " + remoteAddress + ", is not registered to an admin on this server.")); + FLog.info("An unregistered IP (" + remoteAddress + ") has tried to download a log file"); + } + else + { + try + { + FLog.info("The IP \"" + remoteAddress + "\" is downloading log file: " + params.get("logFileName")); + throw new ResponseOverrideException(downloadLogFile(params.get("logFileName"))); + } + catch (LogFileTransferException ex) + { + out.append(HTMLGenerationTools.paragraph("Error downloading logfile: " + ex.getMessage())); + } + } + } + default -> { + out.append(HTMLGenerationTools.heading("Logfile Submodules", 1)); + out.append(""); + } + } + return out.toString(); + } + + private Response downloadLogFile(String LogFilesName) throws LogFileTransferException + { + final File targetFile = new File(LOG_FOLDER.getPath(), LogFilesName); + if (!targetFile.exists()) + { + throw new LogFileTransferException("Logfile not found: " + LogFilesName); + } + + Response response = HTTPDaemon.serveFileBasic(targetFile); + + response.addHeader("Content-Disposition", "attachment; filename=" + targetFile.getName() + ";"); + + return response; + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry == null || !entry.isActive(); + } + + private enum ModuleMode + { + + LIST("list"), + DOWNLOAD("download"), + INVALID(null); + // + private final String modeName; + + ModuleMode(String modeName) + { + this.modeName = modeName; + } + + public static ModuleMode getMode(String needle) + { + for (ModuleMode mode : values()) + { + final String haystack = mode.toString(); + if (haystack != null && haystack.equalsIgnoreCase(needle)) + { + return mode; + } + } + return INVALID; + } + + @Override + public String toString() + { + return this.modeName; + } + } + + private static class LogFileTransferException extends Exception + { + public LogFileTransferException(String string) + { + super(string); + } + } + + private static class ResponseOverrideException extends Exception + { + private final Response response; + + public ResponseOverrideException(Response response) + { + this.response = response; + } + + public Response getResponse() + { + return response; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logs.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logs.java new file mode 100644 index 00000000..8939be9b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_logs.java @@ -0,0 +1,28 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.util.FLog; + +public class Module_logs extends Module_file +{ + public Module_logs(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + if (ConfigEntry.LOGS_SECRET.getString().equals(params.get("password")) && !ConfigEntry.LOGS_SECRET.getString().isEmpty()) + { + FLog.info(session.getSocket().getInetAddress() + " is downloading latest.log."); + return serveFile("latest.log", params, new File("./logs")); + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "Incorrect password."); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_players.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_players.java new file mode 100644 index 00000000..7b73b32f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_players.java @@ -0,0 +1,80 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +public class Module_players extends HTTPDModule +{ + public Module_players(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + @SuppressWarnings("unchecked") + public NanoHTTPD.Response getResponse() + { + final JSONObject responseObject = new JSONObject(); + + final JSONArray players = new JSONArray(); + final JSONArray onlineadmins = new JSONArray(); // updated, never queried. + final JSONArray masterbuilders = new JSONArray(); + final JSONArray admins = new JSONArray(); + final JSONArray senioradmins = new JSONArray(); + final JSONArray developers = new JSONArray(); + final JSONArray executives = new JSONArray(); + + // All online players + for (Player player : Bukkit.getOnlinePlayers()) + { + if (!plugin.al.isVanished(player.getName())) + { + players.add(player.getName()); + if (plugin.al.isAdmin(player) && !plugin.al.isAdminImpostor(player)) + { + onlineadmins.add(player.getName()); + } + } + } + + // Admins + for (Admin admin : plugin.al.getActiveAdmins()) + { + final String username = admin.getName(); + switch (admin.getRank()) + { + case ADMIN: + admins.add(username); + break; + case SENIOR_ADMIN: + senioradmins.add(username); + break; + } + } + + masterbuilders.addAll(plugin.pl.getMasterBuilderNames()); + + // Developers + developers.addAll(FUtil.DEVELOPER_NAMES); + + // Executives + executives.addAll(ConfigEntry.SERVER_EXECUTIVES.getList()); + + responseObject.put("players", players); + responseObject.put("masterbuilders", masterbuilders); + responseObject.put("admins", admins); + responseObject.put("senioradmins", senioradmins); + responseObject.put("developers", developers); + responseObject.put("executives", executives); + + final NanoHTTPD.Response response = new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_JSON, responseObject.toString()); + response.addHeader("Access-Control-Allow-Origin", "*"); + return response; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_punishments.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_punishments.java new file mode 100644 index 00000000..9bfe124c --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_punishments.java @@ -0,0 +1,47 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import java.io.File; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.punishments.PunishmentList; + +public class Module_punishments extends HTTPDModule +{ + + public Module_punishments(NanoHTTPD.HTTPSession session) + { + super(session); + } + + @Override + public NanoHTTPD.Response getResponse() + { + File punishmentFile = new File(plugin.getDataFolder(), PunishmentList.CONFIG_FILENAME); + if (punishmentFile.exists()) + { + final String remoteAddress = socket.getRemoteSocketAddress().toString(); + if (!isAuthorized(remoteAddress)) + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "You may not view the punishment list. Your IP, " + remoteAddress + ", is not registered to an admin on the server."); + } + else + { + return HTTPDaemon.serveFileBasic(new File(plugin.getDataFolder(), PunishmentList.CONFIG_FILENAME)); + } + + } + else + { + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, + "Error 404: Not Found - The requested resource was not found on this server."); + } + } + + private boolean isAuthorized(String remoteAddress) + { + Admin entry = plugin.al.getEntryByIp(remoteAddress); + return entry != null && entry.isActive(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_schematic.java b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_schematic.java new file mode 100644 index 00000000..2459de14 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/httpd/module/Module_schematic.java @@ -0,0 +1,358 @@ +package me.totalfreedom.totalfreedommod.httpd.module; + +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools; +import me.totalfreedom.totalfreedommod.httpd.HTTPDPageBuilder; +import me.totalfreedom.totalfreedommod.httpd.HTTPDaemon; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Method; +import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; + +public class Module_schematic extends HTTPDModule +{ + + private static final File SCHEMATIC_FOLDER = new File("./plugins/WorldEdit/schematics/"); + private static final String REQUEST_FORM_FILE_ELEMENT_NAME = "schematicFile"; + private static final Pattern SCHEMATIC_FILENAME_LC = Pattern.compile("^[a-z0-9_'!,\\-]*\\.(schem|schematic)$"); + private static final String[] SCHEMATIC_FILTER = new String[] + { + "schematic", + "schem" + }; + private static final String UPLOAD_FORM = "
    \n" + + "

    Select a schematic file to upload. Filenames must be alphanumeric, between 1 and 30 characters long (inclusive), and have a .schematic extension.

    \n" + + "\n" + + "
    \n" + + "\n" + + "
    "; + + public Module_schematic(NanoHTTPD.HTTPSession session) + { + super(session); + } + + private static String getArg(String[] args) + { + String out = (args.length == 1 + 1 ? args[1] : null); + return (out == null ? null : (out.trim().isEmpty() ? null : out.trim())); + } + + @Override + public Response getResponse() + { + try + { + return new HTTPDPageBuilder(body(), title(), null, null).getResponse(); + } + catch (ResponseOverrideException ex) + { + return ex.getResponse(); + } + } + + public String title() + { + return "TotalFreedomMod :: Schematic Manager"; + } + + public String body() throws ResponseOverrideException + { + if (!SCHEMATIC_FOLDER.exists()) + { + return HTMLGenerationTools.paragraph("Can't find the WorldEdit schematic folder."); + } + + final StringBuilder out = new StringBuilder(); + + final String[] args = StringUtils.split(uri, "/"); + final ModuleMode mode = ModuleMode.getMode(getArg(args)); + + switch (mode) + { + case LIST: + { + Collection schematics = FileUtils.listFiles(SCHEMATIC_FOLDER, SCHEMATIC_FILTER, false); + + final List schematicsFormatted = new ArrayList<>(); + for (File schematic : schematics) + { + String filename = StringEscapeUtils.escapeHtml4(schematic.getName()); + + if (SCHEMATIC_FILENAME_LC.matcher(filename.trim().toLowerCase()).find()) + { + schematicsFormatted.add("
  • " + filename + "
  • "); + } + else if (filename.length() > 254) + { + schematicsFormatted.add("
  • " + filename + " - (Filename too long, can't download)
  • "); + } + else + { + schematicsFormatted.add("
  • " + filename + " - (Illegal filename, can't download)
  • "); + } + } + + schematicsFormatted.sort(Comparator.comparing(String::toLowerCase)); + + out.append(HTMLGenerationTools.heading("Schematics:", 1)) + .append("
      ") + .append(StringUtils.join(schematicsFormatted, "\r\n")) + .append("
    "); + break; + } + case DOWNLOAD: + { + try + { + throw new ResponseOverrideException(downloadSchematic(params.get("schematicName"))); + } + catch (SchematicTransferException ex) + { + out.append(HTMLGenerationTools.paragraph("Error downloading schematic: " + ex.getMessage())); + } + break; + } + case UPLOAD: + { + final String remoteAddress = socket.getRemoteSocketAddress().toString(); + if (!isAuthorized(remoteAddress)) + { + out.append(HTMLGenerationTools.paragraph("Schematic upload access denied: Your IP, " + remoteAddress + ", is not registered to an admin on this server.")); + } + else + { + if (method == Method.POST) + { + try + { + uploadSchematic(remoteAddress); + + out.append(HTMLGenerationTools.paragraph("Schematic uploaded successfully.")); + } + catch (SchematicTransferException ex) + { + out.append(HTMLGenerationTools.paragraph("Error uploading schematic: " + ex.getMessage())); + } + } + else + { + out.append(UPLOAD_FORM); + } + } + break; + } + default: + { + out.append(HTMLGenerationTools.heading("Schematic Submodules", 1)); + out.append(""); + break; + } + } + return out.toString(); + } + + //Reduced NPath Complexity. + private void uploadSchematic(String remoteAddress) throws SchematicTransferException + { + Map files = getFiles(); + + final String tempFileName = files.get(REQUEST_FORM_FILE_ELEMENT_NAME); + if (tempFileName == null) + { + throw new SchematicTransferException("No file transmitted to server."); + } + + final File tempFile = new File(tempFileName); + if (!tempFile.exists()) + { + throw new SchematicTransferException(); + } + + String origFileName = params.get(REQUEST_FORM_FILE_ELEMENT_NAME); + if (origFileName == null || (origFileName = origFileName.trim()).isEmpty()) + { + throw new SchematicTransferException("Can't resolve original file name."); + } + + func1(tempFile, origFileName); + + final File targetFile = new File(SCHEMATIC_FOLDER.getPath(), origFileName); + + if (targetFile.exists()) + { + throw new SchematicTransferException("Schematic already exists on the server."); + } + + func2(tempFile, targetFile, remoteAddress); + + } + + private void func1(File tempFile, String origFileName) throws SchematicTransferException + { + if (tempFile.length() > FileUtils.ONE_MB) + { + throw new SchematicTransferException("Schematic is too big (1mb max)."); + } + + if (plugin.web.getWorldEditPlugin() == null) + { + throw new SchematicTransferException("WorldEdit is not on the server."); + } + + if (!SCHEMATIC_FILENAME_LC.matcher(origFileName.toLowerCase()).find()) + { + throw new SchematicTransferException("File name must be alphanumeric, between 1 and 30 characters long (inclusive), and have a \".schematic\" extension."); + } + } + + private void func2(File tempFile, File targetFile, String remoteAddress) throws SchematicTransferException + { + try + { + FileUtils.copyFile(tempFile, targetFile); + ClipboardFormat format = ClipboardFormats.findByFile(targetFile); + if (format == null) + { + FileUtils.deleteQuietly(targetFile); + throw new SchematicTransferException("Schematic is not a valid schematic."); + } + try + { + format.getReader(new FileInputStream(targetFile)); + } + catch (IOException e) + { + FileUtils.deleteQuietly(targetFile); + throw new SchematicTransferException("Schematic is not a valid schematic."); + } + + FLog.info(remoteAddress + " uploaded schematic: " + targetFile.getName()); + + } + catch (IOException ex) + { + FLog.severe(ex); + throw new SchematicTransferException(); + } + } + + private Response downloadSchematic(String schematicName) throws SchematicTransferException + { + if (schematicName == null || !SCHEMATIC_FILENAME_LC.matcher((schematicName = schematicName.trim()).toLowerCase()).find()) + { + throw new SchematicTransferException("Invalid schematic name requested: " + schematicName); + } + + final File targetFile = new File(SCHEMATIC_FOLDER.getPath(), schematicName); + if (!targetFile.exists()) + { + throw new SchematicTransferException("Schematic not found: " + schematicName); + } + + Response response = HTTPDaemon.serveFileBasic(targetFile); + + response.addHeader("Content-Disposition", "attachment; filename=" + targetFile.getName() + ";"); + + return response; + } + + private boolean isAuthorized(String remoteAddress) + { + Admin adminEntry = plugin.al.getEntryByIp(remoteAddress); + PlayerData data = plugin.pl.getDataByIp(remoteAddress); + return ((adminEntry != null && adminEntry.isActive()) || data != null && data.isMasterBuilder()); + } + + private enum ModuleMode + { + + LIST("list"), + UPLOAD("upload"), + DOWNLOAD("download"), + INVALID(null); + // + private final String modeName; + + ModuleMode(String modeName) + { + this.modeName = modeName; + } + + public static ModuleMode getMode(String needle) + { + for (ModuleMode mode : values()) + { + final String haystack = mode.toString(); + if (haystack != null && haystack.equalsIgnoreCase(needle)) + { + return mode; + } + } + return INVALID; + } + + @Override + public String toString() + { + return this.modeName; + } + } + + private static class SchematicTransferException extends Exception + { + + public SchematicTransferException() + { + } + + public SchematicTransferException(String string) + { + super(string); + } + } + + private static class ResponseOverrideException extends Exception + { + + private final Response response; + + public ResponseOverrideException(Response response) + { + this.response = response; + } + + public Response getResponse() + { + return response; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionConfig.java b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionConfig.java new file mode 100644 index 00000000..c49faa66 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionConfig.java @@ -0,0 +1,185 @@ +package me.totalfreedom.totalfreedommod.permissions; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.EnumMap; +import java.util.List; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FileUtils; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class PermissionConfig extends FreedomService +{ + public static final String PERMISSIONS_FILENAME = "permissions.yml"; + // + private final EnumMap entries; + public YamlConfiguration configuration; + + public PermissionConfig() + { + entries = new EnumMap<>(PermissionEntry.class); + + PermissionDefaults tempDefaults; + try + { + try + { + try (InputStream defaultConfig = getDefaultConfig()) + { + tempDefaults = new PermissionDefaults(defaultConfig); + for (PermissionEntry entry : PermissionEntry.values()) + { + entries.put(entry, tempDefaults.get(entry.getConfigName())); + } + } + } + catch (IOException ex) + { + FLog.severe(ex); + } + + copyDefaultConfig(getConfigFile()); + + load(); + } + catch (Exception ex) + { + FLog.severe(ex); + } + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public void load() + { + try + { + YamlConfiguration config = new YamlConfiguration(); + + config.load(getConfigFile()); + + configuration = config; + + for (PermissionEntry entry : PermissionEntry.values()) + { + String path = entry.getConfigName(); + if (config.contains(path)) + { + Object value = config.get(path); + if (value != null) + { + entries.put(entry, value); + } + } + else + { + FLog.warning("Missing permission entry " + entry.getConfigName() + ". Using default value."); + } + } + } + catch (IOException | InvalidConfigurationException ex) + { + FLog.severe(ex); + } + } + + private File getConfigFile() + { + return new File(plugin.getDataFolder(), PERMISSIONS_FILENAME); + } + + public List getList(PermissionEntry entry) + { + try + { + return get(entry, List.class); + } + catch (IllegalArgumentException ex) + { + FLog.severe(ex); + } + return null; + } + + public T get(PermissionEntry entry, Class type) throws IllegalArgumentException + { + Object value = entries.get(entry); + try + { + return type.cast(value); + } + catch (ClassCastException ex) + { + throw new IllegalArgumentException(entry.name() + " is not of type " + type.getSimpleName()); + } + } + + public void set(PermissionEntry entry, T value) + { + entries.put(entry, value); + } + + private void copyDefaultConfig(File targetFile) + { + if (targetFile.exists()) + { + return; + } + + FLog.info("Installing default permission file template: " + targetFile.getPath()); + + try + { + try (InputStream defaultConfig = getDefaultConfig()) + { + FileUtils.copyInputStreamToFile(defaultConfig, targetFile); + } + } + catch (IOException ex) + { + FLog.severe(ex); + } + } + + private InputStream getDefaultConfig() + { + return plugin.getResource(PERMISSIONS_FILENAME); + } + + public static class PermissionDefaults + { + private YamlConfiguration defaults = null; + + private PermissionDefaults(InputStream defaultConfig) + { + try + { + defaults = new YamlConfiguration(); + final InputStreamReader isr = new InputStreamReader(defaultConfig); + defaults.load(isr); + isr.close(); + } + catch (IOException | InvalidConfigurationException ex) + { + FLog.severe(ex); + } + } + + public Object get(String path) + { + return defaults.get(path); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionEntry.java new file mode 100644 index 00000000..08b16351 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionEntry.java @@ -0,0 +1,41 @@ +package me.totalfreedom.totalfreedommod.permissions; + +import java.util.List; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; + +public enum PermissionEntry +{ + REMOVE("remove"), + OPERATORS("operators"), + MASTER_BUILDERS("master_builders"), + ADMINS("admins"), + SENIOR_ADMINS("senior_admins"); + + private final String configName; + + PermissionEntry(String configName) + { + this.configName = configName; + } + + public String getConfigName() + { + return configName; + } + + public List getList() + { + return getConfig().getList(this); + } + + @SuppressWarnings("unchecked") + public List getEntry() + { + return (List)getList(); + } + + private PermissionConfig getConfig() + { + return TotalFreedomMod.getPlugin().permissions; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionManager.java b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionManager.java new file mode 100644 index 00000000..31a32655 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/permissions/PermissionManager.java @@ -0,0 +1,120 @@ +package me.totalfreedom.totalfreedommod.permissions; + +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.rank.Title; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; + +public class PermissionManager extends FreedomService +{ + public Map> permissions = Maps.newHashMap(); + + public Map attachments = Maps.newHashMap(); + + @Override + public void onStart() + { + loadPermissionNodes(); + } + + @Override + public void onStop() + { + } + + public void loadPermissionNodes() + { + FLog.info("Loading permission nodes..."); + + permissions.clear(); + + List operatorPermissions; + List masterBuilderPermissions; + List adminPermissions; + List senioradminPermissions; + + operatorPermissions = PermissionEntry.OPERATORS.getEntry(); + permissions.put(Rank.OP, operatorPermissions); + + masterBuilderPermissions = PermissionEntry.MASTER_BUILDERS.getEntry(); + masterBuilderPermissions.addAll(operatorPermissions); + permissions.put(Title.MASTER_BUILDER, masterBuilderPermissions); + + adminPermissions = PermissionEntry.ADMINS.getEntry(); + adminPermissions.addAll(masterBuilderPermissions); + permissions.put(Rank.ADMIN, adminPermissions); + + senioradminPermissions = PermissionEntry.SENIOR_ADMINS.getEntry(); + senioradminPermissions.addAll(adminPermissions); + permissions.put(Rank.SENIOR_ADMIN, senioradminPermissions); + + int count = PermissionEntry.OPERATORS.getEntry().size() + PermissionEntry.MASTER_BUILDERS.getEntry().size() + PermissionEntry.ADMINS.getEntry().size() + PermissionEntry.SENIOR_ADMINS.getEntry().size(); + + FLog.info("Loaded " + count + " permission nodes"); + } + + public void setPermissions(Player player) + { + PermissionAttachment attachment = attachments.get(player); + + if (attachment != null) + { + player.removeAttachment(attachment); + } + + attachment = player.addAttachment(plugin); + + for (PermissionAttachmentInfo attachmentInfo : player.getEffectivePermissions()) + { + for (String rootNode : PermissionEntry.REMOVE.getEntry()) + { + String permission = attachmentInfo.getPermission(); + if (permission.startsWith(rootNode)) + { + attachment.setPermission(attachmentInfo.getPermission(), false); + } + } + } + List nodes = permissions.get(plugin.rm.getRank(player)); + if (nodes != null) + { + for (String node : nodes) + { + attachment.setPermission(node, true); + } + } + + if (plugin.pl.getData(player).isMasterBuilder() && !plugin.al.isAdmin(player)) + { + if (nodes != null) + { + for (String node : permissions.get(Title.MASTER_BUILDER)) + { + attachment.setPermission(node, true); + } + } + } + + attachments.put(player, attachment); + + player.recalculatePermissions(); + } + + public void updatePlayers() + { + for (Player player : server.getOnlinePlayers()) + { + setPermissions(player); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/player/FPlayer.java b/src/main/java/me/totalfreedom/totalfreedommod/player/FPlayer.java new file mode 100644 index 00000000..4e1febc4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/player/FPlayer.java @@ -0,0 +1,711 @@ +package me.totalfreedom.totalfreedommod.player; + +import java.util.ArrayList; +import java.util.List; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.caging.CageData; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.freeze.FreezeData; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class FPlayer +{ + public static final long AUTO_PURGE_TICKS = 5L * 60L * 20L; + + + private final TotalFreedomMod plugin; + + private final String name; + + private final String ip; + // + private final FreezeData freezeData = new FreezeData(this); + private final CageData cageData = new CageData(this); + private final List mobThrowerQueue = new ArrayList<>(); + private Player player; + // + private BukkitTask unmuteTask; + private double fuckoffRadius = 0; + private int messageCount = 0; + private int totalBlockDestroy = 0; + private int totalBlockPlace = 0; + private int freecamDestroyCount = 0; + private int freecamPlaceCount = 0; + private boolean isOrbiting = false; + private double orbitStrength = 10.0; + private boolean mobThrowerEnabled = false; + private EntityType mobThrowerEntity = EntityType.PIG; + private double mobThrowerSpeed = 4.0; + private BukkitTask mp44ScheduleTask = null; + private boolean mp44Armed = false; + private boolean mp44Firing = false; + private BukkitTask lockupScheduleTask = null; + private boolean lockedUp = false; + private String lastMessage = ""; + private boolean inAdminchat = false; + private boolean allCommandsBlocked = false; + + + private boolean superadminIdVerified = false; + private String lastCommand = ""; + private boolean cmdspyEnabled = false; + private String tag = null; + private int warningCount = 0; + + + private boolean editBlocked = false; + + + private boolean pvpBlocked = false; + + + private boolean invSee = false; + + public FPlayer(TotalFreedomMod plugin, Player player) + { + this(plugin, player.getName(), FUtil.getIp(player)); + } + + private FPlayer(TotalFreedomMod plugin, String name, String ip) + { + this.plugin = plugin; + this.name = name; + this.ip = ip; + } + + public static long getAutoPurgeTicks() + { + return AUTO_PURGE_TICKS; + } + + public Player getPlayer() + { + if (player != null && !player.isOnline()) + { + player = null; + } + + if (player == null) + { + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) + { + if (FUtil.getIp(onlinePlayer).equals(ip)) + { + player = onlinePlayer; + break; + } + } + } + + return player; + } + + public void setPlayer(Player player) + { + this.player = player; + } + + public boolean isOrbiting() + { + return isOrbiting; + } + + public void setOrbiting(boolean orbiting) + { + isOrbiting = orbiting; + } + + public void startOrbiting(double strength) + { + this.isOrbiting = true; + this.orbitStrength = strength; + } + + public void stopOrbiting() + { + this.isOrbiting = false; + } + + public double orbitStrength() + { + return orbitStrength; + } + + public boolean isFuckOff() + { + return fuckoffRadius > 0; + } + + public void setFuckoff(double radius) + { + this.fuckoffRadius = radius; + } + + public void disableFuckoff() + { + this.fuckoffRadius = 0; + } + + public void resetMsgCount() + { + this.messageCount = 0; + } + + public int incrementAndGetMsgCount() + { + return this.messageCount++; + } + + public int incrementAndGetBlockDestroyCount() + { + return this.totalBlockDestroy++; + } + + public void resetBlockDestroyCount() + { + this.totalBlockDestroy = 0; + } + + public int incrementAndGetBlockPlaceCount() + { + return this.totalBlockPlace++; + } + + public void resetBlockPlaceCount() + { + this.totalBlockPlace = 0; + } + + public int incrementAndGetFreecamDestroyCount() + { + return this.freecamDestroyCount++; + } + + public void resetFreecamDestroyCount() + { + this.freecamDestroyCount = 0; + } + + public int incrementAndGetFreecamPlaceCount() + { + return this.freecamPlaceCount++; + } + + public void resetFreecamPlaceCount() + { + this.freecamPlaceCount = 0; + } + + public void enableMobThrower(EntityType mobThrowerCreature, double mobThrowerSpeed) + { + this.mobThrowerEnabled = true; + this.mobThrowerEntity = mobThrowerCreature; + this.mobThrowerSpeed = mobThrowerSpeed; + } + + public void disableMobThrower() + { + this.mobThrowerEnabled = false; + } + + public EntityType mobThrowerCreature() + { + return this.mobThrowerEntity; + } + + public double mobThrowerSpeed() + { + return this.mobThrowerSpeed; + } + + public boolean mobThrowerEnabled() + { + return this.mobThrowerEnabled; + } + + public void enqueueMob(LivingEntity mob) + { + mobThrowerQueue.add(mob); + if (mobThrowerQueue.size() > 4) + { + LivingEntity oldmob = mobThrowerQueue.remove(0); + if (oldmob != null) + { + oldmob.damage(500.0); + } + } + } + + public void startArrowShooter(TotalFreedomMod plugin) + { + this.stopArrowShooter(); + this.mp44ScheduleTask = new ArrowShooter(this.player).runTaskTimer(plugin, 1L, 1L); + this.mp44Firing = true; + } + + public void stopArrowShooter() + { + if (this.mp44ScheduleTask != null) + { + this.mp44ScheduleTask.cancel(); + this.mp44ScheduleTask = null; + } + this.mp44Firing = false; + } + + public void armMP44() + { + this.mp44Armed = true; + this.stopArrowShooter(); + } + + public void disarmMP44() + { + this.mp44Armed = false; + this.stopArrowShooter(); + } + + public boolean isMP44Armed() + { + return this.mp44Armed; + } + + public boolean toggleMP44Firing() + { + this.mp44Firing = !this.mp44Firing; + return mp44Firing; + } + + public boolean isMuted() + { + return unmuteTask != null; + } + + public void setMuted(boolean muted, int minutes) + { + FUtil.cancel(unmuteTask); + plugin.mu.MUTED_PLAYERS.remove(getPlayer().getName()); + unmuteTask = null; + + if (!muted) + { + return; + } + + if (getPlayer() == null) + { + return; + } + + plugin.mu.MUTED_PLAYERS.add(getPlayer().getName()); + + // TODO: Simplify this into a Consumer lambda? + unmuteTask = new BukkitRunnable() + { + @Override + public void run() + { + if (getPlayer() != null) + { + FUtil.adminAction(ConfigEntry.SERVER_NAME.getString(), "Unmuting " + getPlayer().getName(), false); + setMuted(false); + } + else + { + FUtil.adminAction(ConfigEntry.SERVER_NAME.getString(), "Unmuting " + getName(), false); + plugin.mu.MUTED_PLAYERS.remove(getName()); + } + } + }.runTaskLater(plugin, minutes * (60L * 20L)); + } + + public void setMuted(boolean muted) + { + setMuted(muted, 5); + } + + public BukkitTask getLockupScheduleID() + { + return this.lockupScheduleTask; + } + + public void setLockupScheduleId(BukkitTask id) + { + this.lockupScheduleTask = id; + } + + public boolean isLockedUp() + { + return this.lockedUp; + } + + public void setLockedUp(boolean lockedUp) + { + this.lockedUp = lockedUp; + } + + public String getLastMessage() + { + return lastMessage; + } + + public void setLastMessage(String message) + { + this.lastMessage = message; + } + + public void setAdminChat(boolean inAdminchat) + { + this.inAdminchat = inAdminchat; + } + + public boolean inAdminChat() + { + return this.inAdminchat; + } + + public boolean allCommandsBlocked() + { + return this.allCommandsBlocked; + } + + public void setCommandsBlocked(boolean commandsBlocked) + { + this.allCommandsBlocked = commandsBlocked; + } + + public String getLastCommand() + { + return lastCommand; + } + + public void setLastCommand(String lastCommand) + { + this.lastCommand = lastCommand; + } + + public void setCommandSpy(boolean enabled) + { + this.cmdspyEnabled = enabled; + } + + public boolean cmdspyEnabled() + { + return cmdspyEnabled; + } + + public String getTag() + { + return this.tag; + } + + public void setTag(String tag) + { + if (tag == null) + { + this.tag = null; + } + else + { + this.tag = FUtil.colorize(tag) + ChatColor.WHITE; + } + } + + public int getWarningCount() + { + return this.warningCount; + } + + public void setWarningCount(int warningCount) + { + this.warningCount = warningCount; + } + + public void incrementWarnings() + { + this.warningCount++; + + if (this.warningCount % 2 == 0) + { + Player p = getPlayer(); + p.getWorld().strikeLightning(p.getLocation()); + FUtil.playerMsg(p, ChatColor.RED + "You have been warned at least twice now, make sure to read the rules at " + ConfigEntry.SERVER_BAN_URL.getString()); + } + } + + public TotalFreedomMod getPlugin() + { + return plugin; + } + + public String getName() + { + return name; + } + + public String getIp() + { + return ip; + } + + public BukkitTask getUnmuteTask() + { + return unmuteTask; + } + + public void setUnmuteTask(BukkitTask unmuteTask) + { + this.unmuteTask = unmuteTask; + } + + public FreezeData getFreezeData() + { + return freezeData; + } + + public double getFuckoffRadius() + { + return fuckoffRadius; + } + + public void setFuckoffRadius(double fuckoffRadius) + { + this.fuckoffRadius = fuckoffRadius; + } + + public int getMessageCount() + { + return messageCount; + } + + public void setMessageCount(int messageCount) + { + this.messageCount = messageCount; + } + + public int getTotalBlockDestroy() + { + return totalBlockDestroy; + } + + public void setTotalBlockDestroy(int totalBlockDestroy) + { + this.totalBlockDestroy = totalBlockDestroy; + } + + public int getTotalBlockPlace() + { + return totalBlockPlace; + } + + public void setTotalBlockPlace(int totalBlockPlace) + { + this.totalBlockPlace = totalBlockPlace; + } + + public int getFreecamDestroyCount() + { + return freecamDestroyCount; + } + + public void setFreecamDestroyCount(int freecamDestroyCount) + { + this.freecamDestroyCount = freecamDestroyCount; + } + + public int getFreecamPlaceCount() + { + return freecamPlaceCount; + } + + public void setFreecamPlaceCount(int freecamPlaceCount) + { + this.freecamPlaceCount = freecamPlaceCount; + } + + public CageData getCageData() + { + return cageData; + } + + public double getOrbitStrength() + { + return orbitStrength; + } + + public void setOrbitStrength(double orbitStrength) + { + this.orbitStrength = orbitStrength; + } + + public boolean isMobThrowerEnabled() + { + return mobThrowerEnabled; + } + + public void setMobThrowerEnabled(boolean mobThrowerEnabled) + { + this.mobThrowerEnabled = mobThrowerEnabled; + } + + public EntityType getMobThrowerEntity() + { + return mobThrowerEntity; + } + + public void setMobThrowerEntity(EntityType mobThrowerEntity) + { + this.mobThrowerEntity = mobThrowerEntity; + } + + public double getMobThrowerSpeed() + { + return mobThrowerSpeed; + } + + public void setMobThrowerSpeed(double mobThrowerSpeed) + { + this.mobThrowerSpeed = mobThrowerSpeed; + } + + public List getMobThrowerQueue() + { + return mobThrowerQueue; + } + + public BukkitTask getMp44ScheduleTask() + { + return mp44ScheduleTask; + } + + public void setMp44ScheduleTask(BukkitTask mp44ScheduleTask) + { + this.mp44ScheduleTask = mp44ScheduleTask; + } + + public boolean isMp44Armed() + { + return mp44Armed; + } + + public void setMp44Armed(boolean mp44Armed) + { + this.mp44Armed = mp44Armed; + } + + public boolean isMp44Firing() + { + return mp44Firing; + } + + public void setMp44Firing(boolean mp44Firing) + { + this.mp44Firing = mp44Firing; + } + + public BukkitTask getLockupScheduleTask() + { + return lockupScheduleTask; + } + + public void setLockupScheduleTask(BukkitTask lockupScheduleTask) + { + this.lockupScheduleTask = lockupScheduleTask; + } + + public boolean isInAdminchat() + { + return inAdminchat; + } + + public void setInAdminchat(boolean inAdminchat) + { + this.inAdminchat = inAdminchat; + } + + public boolean isAllCommandsBlocked() + { + return allCommandsBlocked; + } + + public void setAllCommandsBlocked(boolean allCommandsBlocked) + { + this.allCommandsBlocked = allCommandsBlocked; + } + + public boolean isSuperadminIdVerified() + { + return superadminIdVerified; + } + + public void setSuperadminIdVerified(boolean superadminIdVerified) + { + this.superadminIdVerified = superadminIdVerified; + } + + public boolean isCmdspyEnabled() + { + return cmdspyEnabled; + } + + public void setCmdspyEnabled(boolean cmdspyEnabled) + { + this.cmdspyEnabled = cmdspyEnabled; + } + + public boolean isEditBlocked() + { + return editBlocked; + } + + public void setEditBlocked(boolean editBlocked) + { + this.editBlocked = editBlocked; + } + + public boolean isPvpBlocked() + { + return pvpBlocked; + } + + public void setPvpBlocked(boolean pvpBlocked) + { + this.pvpBlocked = pvpBlocked; + } + + public boolean isInvSee() + { + return invSee; + } + + public void setInvSee(boolean invSee) + { + this.invSee = invSee; + } + + private static class ArrowShooter extends BukkitRunnable + { + + private final Player player; + + private ArrowShooter(Player player) + { + this.player = player; + } + + @Override + public void run() + { + Arrow shot = player.launchProjectile(Arrow.class); + shot.setVelocity(shot.getVelocity().multiply(2.0)); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java new file mode 100644 index 00000000..942f9db4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerData.java @@ -0,0 +1,367 @@ +package me.totalfreedom.totalfreedommod.player; + +import com.google.common.collect.Lists; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.shop.ShopItem; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +public class PlayerData +{ + private final List ips = Lists.newArrayList(); + private final List notes = Lists.newArrayList(); + private final List backupCodes = Lists.newArrayList(); + private String name; + private String tag = null; + private String discordID = null; + private Boolean masterBuilder = false; + + private Boolean verification = false; + + + private String rideMode = "ask"; + + + private int coins; + private List items = Lists.newArrayList(); + + + private int totalVotes; + + private boolean displayDiscord = true; + + + private String loginMessage; + + private Boolean inspect = false; + + public PlayerData(ResultSet resultSet) + { + try + { + name = resultSet.getString("username"); + ips.clear(); + ips.addAll(FUtil.stringToList(resultSet.getString("ips"))); + notes.clear(); + notes.addAll(FUtil.stringToList(resultSet.getString("notes"))); + tag = resultSet.getString("tag"); + discordID = resultSet.getString("discord_id"); + backupCodes.clear(); + backupCodes.addAll(FUtil.stringToList(resultSet.getString("backup_codes"))); + masterBuilder = resultSet.getBoolean("master_builder"); + verification = resultSet.getBoolean("verification"); + rideMode = resultSet.getString("ride_mode"); + coins = resultSet.getInt("coins"); + items.clear(); + items.addAll(FUtil.stringToList(resultSet.getString("items"))); + totalVotes = resultSet.getInt("total_votes"); + displayDiscord = resultSet.getBoolean("display_discord"); + loginMessage = resultSet.getString("login_message"); + inspect = resultSet.getBoolean("inspect"); + } + catch (SQLException e) + { + FLog.severe("Failed to load player: " + e.getMessage()); + } + + // Force verification for Master Builders + if (masterBuilder && !verification) + { + verification = true; + TotalFreedomMod.getPlugin().pl.save(this); + } + else if (!masterBuilder && discordID == null && verification) + { + this.verification = false; + TotalFreedomMod.getPlugin().pl.save(this); + } + } + + public PlayerData(Player player) + { + this.name = player.getName(); + } + + @Override + public String toString() + { + + return "Player: " + name + "\n" + + "- IPs: " + StringUtils.join(ips, ", ") + "\n" + + "- Discord ID: " + discordID + "\n" + + "- Master Builder: " + masterBuilder + "\n" + + "- Has Verification: " + verification + "\n" + + "- Coins: " + coins + "\n" + + "- Total Votes: " + totalVotes + "\n" + + "- Display Discord: " + displayDiscord + "\n" + + "- Tag: " + FUtil.colorize(tag) + ChatColor.GRAY + "\n" + + "- Ride Mode: " + rideMode + "\n" + + "- Backup Codes: " + backupCodes.size() + "/10" + "\n" + + "- Login Message: " + loginMessage; + } + + public List getIps() + { + return Collections.unmodifiableList(ips); + } + + public boolean hasLoginMessage() + { + return loginMessage != null && !loginMessage.isEmpty(); + } + + public boolean addIp(String ip) + { + if (ips.contains(ip)) + { + return false; + } + ips.add(ip); + return true; + } + + public void removeIp(String ip) + { + ips.remove(ip); + } + + public void clearIps() + { + ips.clear(); + } + + public void addIps(List ips) + { + this.ips.addAll(ips); + } + + public List getNotes() + { + return Collections.unmodifiableList(notes); + } + + public void clearNotes() + { + notes.clear(); + } + + public List getBackupCodes() + { + return Collections.unmodifiableList(backupCodes); + } + + public void setBackupCodes(List codes) + { + backupCodes.clear(); + backupCodes.addAll(codes); + } + + public void removeBackupCode(String code) + { + backupCodes.remove(code); + } + + public void addNote(String note) + { + notes.add(note); + } + + public boolean removeNote(int id) throws IndexOutOfBoundsException + { + try + { + notes.remove(id); + } + catch (IndexOutOfBoundsException e) + { + return false; + } + return true; + } + + public void giveItem(ShopItem item) + { + items.add(item.getDataName()); + } + + public List getItems() + { + return Collections.unmodifiableList(items); + } + + public void setItems(List items) + { + this.items = items; + } + + public boolean hasItem(ShopItem item) + { + return items.contains(item.getDataName()); + } + + public void removeItem(ShopItem item) + { + items.remove(item.getDataName()); + } + + public boolean hasVerification() + { + return verification; + } + + public boolean isMasterBuilder() + { + return masterBuilder; + } + + public boolean hasInspection() + { + return inspect; + } + + public Map toSQLStorable() + { + return new HashMap() + {{ + put("username", name); + put("ips", FUtil.listToString(ips)); + put("notes", FUtil.listToString(notes)); + put("tag", tag); + put("discord_id", discordID); + put("backup_codes", FUtil.listToString(backupCodes)); + put("master_builder", masterBuilder); + put("verification", verification); + put("ride_mode", rideMode); + put("coins", coins); + put("items", FUtil.listToString(items)); + put("total_votes", totalVotes); + put("display_discord", displayDiscord); + put("login_message", loginMessage); + put("inspect", inspect); + }}; + } + + public boolean doesDisplayDiscord() + { + return displayDiscord; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getTag() + { + return tag; + } + + public void setTag(String tag) + { + this.tag = tag; + } + + public String getDiscordID() + { + return discordID; + } + + public void setDiscordID(String discordID) + { + this.discordID = discordID; + } + + public Boolean getMasterBuilder() + { + return masterBuilder; + } + + public void setMasterBuilder(Boolean masterBuilder) + { + this.masterBuilder = masterBuilder; + } + + public Boolean getVerification() + { + return verification; + } + + public void setVerification(Boolean verification) + { + this.verification = verification; + } + + public String getRideMode() + { + return rideMode; + } + + public void setRideMode(String rideMode) + { + this.rideMode = rideMode; + } + + public int getCoins() + { + return coins; + } + + public void setCoins(int coins) + { + this.coins = coins; + } + + public int getTotalVotes() + { + return totalVotes; + } + + public void setTotalVotes(int totalVotes) + { + this.totalVotes = totalVotes; + } + + public boolean isDisplayDiscord() + { + return displayDiscord; + } + + public void setDisplayDiscord(boolean displayDiscord) + { + this.displayDiscord = displayDiscord; + } + + public String getLoginMessage() + { + return loginMessage; + } + + public void setLoginMessage(String loginMessage) + { + this.loginMessage = loginMessage; + } + + public Boolean getInspect() + { + return inspect; + } + + public void setInspect(Boolean inspect) + { + this.inspect = inspect; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerList.java b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerList.java new file mode 100644 index 00000000..222e54d2 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/player/PlayerList.java @@ -0,0 +1,312 @@ +package me.totalfreedom.totalfreedommod.player; + +import com.google.common.collect.Maps; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.rank.Rank; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public class PlayerList extends FreedomService +{ + + public final Map playerMap = Maps.newHashMap(); // ip,dataMap + public final Map dataMap = Maps.newHashMap(); // username, data + + @Override + public void onStart() + { + dataMap.clear(); + loadMasterBuilders(); + } + + @Override + public void onStop() + { + } + + public FPlayer getPlayerSync(Player player) + { + synchronized (playerMap) + { + return getPlayer(player); + } + } + + public void loadMasterBuilders() + { + ResultSet resultSet = plugin.sql.getMasterBuilders(); + + if (resultSet == null) + { + return; + } + + try + { + while (resultSet.next()) + { + PlayerData playerData = load(resultSet); + dataMap.put(playerData.getName(), playerData); + } + } + catch (SQLException e) + { + FLog.severe("Failed to parse master builders: " + e.getMessage()); + } + } + + public String getIp(OfflinePlayer player) + { + if (player.isOnline()) + { + return FUtil.getIp(Objects.requireNonNull(player.getPlayer())); + } + + final PlayerData entry = getData(player.getName()); + + return (entry == null ? null : entry.getIps().iterator().next()); + } + + public List getMasterBuilderNames() + { + List masterBuilders = new ArrayList<>(); + for (PlayerData playerData : plugin.pl.dataMap.values()) + { + if (playerData.isMasterBuilder()) + { + masterBuilders.add(playerData.getName()); + } + } + return masterBuilders; + } + + public boolean canManageMasterBuilders(String name) + { + PlayerData data = getData(name); + + return (!ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase()) && data != null && !ConfigEntry.SERVER_OWNERS.getStringList().contains(data.getName())) + && !ConfigEntry.SERVER_EXECUTIVES.getStringList().contains(data.getName()) + && !isTelnetMasterBuilder(data) + && !ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase()); + } + + public boolean isTelnetMasterBuilder(PlayerData playerData) + { + Admin admin = plugin.al.getEntryByName(playerData.getName()); + return admin != null && admin.getRank().isAtLeast(Rank.ADMIN) && playerData.isMasterBuilder(); + } + + // May not return null + public FPlayer getPlayer(Player player) + { + FPlayer tPlayer = playerMap.get(FUtil.getIp(player)); + if (tPlayer != null) + { + return tPlayer; + } + + tPlayer = new FPlayer(plugin, player); + playerMap.put(FUtil.getIp(player), tPlayer); + + return tPlayer; + } + + public PlayerData loadByName(String name) + { + return load(plugin.sql.getPlayerByName(name)); + } + + public PlayerData loadByIp(String ip) + { + return load(plugin.sql.getPlayerByIp(ip)); + } + + public PlayerData load(ResultSet resultSet) + { + if (resultSet == null) + { + return null; + } + return new PlayerData(resultSet); + } + + public Boolean isPlayerImpostor(Player player) + { + PlayerData playerData = getData(player); + return plugin.dc.enabled + && !plugin.al.isAdmin(player) + && (playerData.hasVerification()) + && !playerData.getIps().contains(FUtil.getIp(player)); + } + + public boolean IsImpostor(Player player) + { + return isPlayerImpostor(player) || plugin.al.isAdminImpostor(player); + } + + public void verify(Player player, String backupCode) + { + PlayerData playerData = getData(player); + if (backupCode != null) + { + playerData.removeBackupCode(backupCode); + } + + playerData.addIp(FUtil.getIp(player)); + save(playerData); + + if (plugin.al.isAdminImpostor(player)) + { + Admin admin = plugin.al.getEntryByName(player.getName()); + admin.setLastLogin(new Date()); + admin.addIp(FUtil.getIp(player)); + plugin.al.updateTables(); + plugin.al.save(admin); + } + + plugin.rm.updateDisplay(player); + } + + public void syncIps(Admin admin) + { + PlayerData playerData = getData(admin.getName()); + playerData.clearIps(); + playerData.addIps(admin.getIps()); + plugin.pl.save(playerData); + } + + public void syncIps(PlayerData playerData) + { + Admin admin = plugin.al.getEntryByName(playerData.getName()); + + if (admin != null && admin.isActive()) + { + admin.clearIPs(); + admin.addIps(playerData.getIps()); + plugin.al.updateTables(); + plugin.al.save(admin); + } + } + + + public void save(PlayerData player) + { + try + { + ResultSet currentSave = plugin.sql.getPlayerByName(player.getName()); + for (Map.Entry entry : player.toSQLStorable().entrySet()) + { + Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue()); + if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null) + { + plugin.sql.setPlayerValue(player, entry.getKey(), entry.getValue()); + } + } + } + catch (SQLException e) + { + FLog.severe("Failed to save player: " + e.getMessage()); + } + } + + public PlayerData getData(Player player) + { + // Check for existing data + PlayerData playerData = dataMap.get(player.getName()); + if (playerData != null) + { + return playerData; + } + + // Load data + playerData = loadByName(player.getName()); + + if (playerData == null) + { + playerData = loadByIp(FUtil.getIp(player)); + if (playerData != null) + { + plugin.sql.updatePlayerName(playerData.getName(), player.getName()); + playerData.setName(player.getName()); + dataMap.put(player.getName(), playerData); + return playerData; + } + } + else + { + dataMap.put(player.getName(), playerData); + return playerData; + } + + // Create new data if nonexistent + FLog.info("Creating new player verification entry for " + player.getName()); + + // Create new player + playerData = new PlayerData(player); + playerData.addIp(FUtil.getIp(player)); + + // Store player + dataMap.put(player.getName(), playerData); + + // Save player + plugin.sql.addPlayer(playerData); + return playerData; + + } + + public PlayerData getData(String username) + { + // Check for existing data + PlayerData playerData = dataMap.get(username); + if (playerData != null) + { + return playerData; + } + + playerData = loadByName(username); + + if (playerData != null) + { + dataMap.put(username, playerData); + } + else + { + return null; + } + + return playerData; + } + + public PlayerData getDataByIp(String ip) + { + PlayerData player = loadByIp(ip); + + if (player != null) + { + dataMap.put(player.getName(), player); + } + + return player; + } + + public Map getPlayerMap() + { + return playerMap; + } + + public Map getDataMap() + { + return dataMap; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/punishments/Punishment.java b/src/main/java/me/totalfreedom/totalfreedommod/punishments/Punishment.java new file mode 100644 index 00000000..3e532872 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/punishments/Punishment.java @@ -0,0 +1,145 @@ +package me.totalfreedom.totalfreedommod.punishments; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.config.IConfig; +import org.bukkit.configuration.ConfigurationSection; + +public class Punishment implements IConfig +{ + + public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); + + + private String username = null; + + private String ip = null; + + + private String by = null; + + + private PunishmentType type = null; + + + private String reason = null; + + + private Date issued_on = null; + + public Punishment() + { + } + + public Punishment(String username, String ip, String by, PunishmentType type, String reason) + { + this.username = username; + this.ip = ip; + this.by = by; + this.type = type; + this.reason = reason; + this.issued_on = new Date(); + } + + public static SimpleDateFormat getDateFormat() + { + return DATE_FORMAT; + } + + @Override + public void loadFrom(ConfigurationSection cs) + { + this.username = cs.getString("username", null); + this.ip = cs.getString("ip", null); + this.by = cs.getString("by", null); + this.type = PunishmentType.valueOf(Objects.requireNonNull(cs.getString("type", null)).toUpperCase()); + this.reason = cs.getString("reason", null); + try + { + this.issued_on = DATE_FORMAT.parse(cs.getString("issued_on", null)); + } + catch (ParseException e) + { + this.issued_on = null; + } + } + + @Override + public void saveTo(ConfigurationSection cs) + { + cs.set("username", username); + cs.set("ip", ip); + cs.set("by", by); + cs.set("type", type.name().toLowerCase()); + cs.set("reason", reason); + cs.set("issued_on", DATE_FORMAT.format(issued_on)); + } + + @Override + public boolean isValid() + { + return username != null || ip != null; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getIp() + { + return ip; + } + + public void setIp(String ip) + { + this.ip = ip; + } + + public String getBy() + { + return by; + } + + public void setBy(String by) + { + this.by = by; + } + + public PunishmentType getType() + { + return type; + } + + public void setType(PunishmentType type) + { + this.type = type; + } + + public String getReason() + { + return reason; + } + + public void setReason(String reason) + { + this.reason = reason; + } + + public Date getIssuedOn() + { + return issued_on; + } + + public void setIssuedOn(Date issued_on) + { + this.issued_on = issued_on; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentList.java b/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentList.java new file mode 100644 index 00000000..b18952cb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentList.java @@ -0,0 +1,125 @@ +package me.totalfreedom.totalfreedommod.punishments; + +import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.YamlConfig; +import me.totalfreedom.totalfreedommod.util.FLog; + +public class PunishmentList extends FreedomService +{ + + public static final String CONFIG_FILENAME = "punishments.yml"; + private final Set punishments = Sets.newHashSet(); + // + private final YamlConfig config; + + public PunishmentList() + { + this.config = new YamlConfig(plugin, CONFIG_FILENAME); + } + + @Override + public void onStart() + { + config.load(); + + punishments.clear(); + for (String id : config.getKeys(false)) + { + if (!config.isConfigurationSection(id)) + { + FLog.warning("Failed to load punishment number " + id + "!"); + continue; + } + + Punishment punishment = new Punishment(); + punishment.loadFrom(Objects.requireNonNull(config.getConfigurationSection(id))); + + if (!punishment.isValid()) + { + FLog.warning("Not adding punishment number " + id + ". Missing information."); + continue; + } + + punishments.add(punishment); + } + + FLog.info("Loaded " + punishments.size() + " punishments."); + } + + @Override + public void onStop() + { + saveAll(); + FLog.info("Saved " + punishments.size() + " player bans"); + } + + public void saveAll() + { + config.clear(); + + for (Punishment punishment : punishments) + { + punishment.saveTo(config.createSection(String.valueOf(punishment.hashCode()))); + } + + // Save config + config.save(); + } + + public int clear() + { + int removed = punishments.size(); + punishments.clear(); + saveAll(); + + return removed; + } + + public int clear(String username) + { + List removed = new ArrayList<>(); + + for (Punishment punishment : punishments) + { + if (punishment.getUsername().equalsIgnoreCase(username)) + { + removed.add(punishment); + } + } + + if (removed.size() != 0) + { + punishments.removeAll(removed); + saveAll(); + } + + return removed.size(); + } + + public int getLastPunishmentID() + { + int size = punishments.size(); + + if (size == 0) + { + return 1; + } + + return size; + } + + public void logPunishment(Punishment punishment) + { + if (punishments.add(punishment)) + { + saveAll(); + } + + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentType.java b/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentType.java new file mode 100644 index 00000000..705fd928 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/punishments/PunishmentType.java @@ -0,0 +1,11 @@ +package me.totalfreedom.totalfreedommod.punishments; + +public enum PunishmentType +{ + MUTE, + SMITE, + KICK, + TEMPBAN, + BAN, + DOOM +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/rank/Displayable.java b/src/main/java/me/totalfreedom/totalfreedommod/rank/Displayable.java new file mode 100644 index 00000000..55cc2fc1 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/rank/Displayable.java @@ -0,0 +1,30 @@ +package me.totalfreedom.totalfreedommod.rank; + +import net.md_5.bungee.api.ChatColor; + +public interface Displayable +{ + + String getArticle(); + + String getName(); + + String getTag(); + + String getAbbr(); + + ChatColor getColor(); + + org.bukkit.ChatColor getTeamColor(); + + String getColoredName(); + + String getColoredTag(); + + String getColoredLoginMessage(); + + boolean hasTeam(); + + boolean hasDefaultLoginMessage(); + +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/rank/Rank.java b/src/main/java/me/totalfreedom/totalfreedommod/rank/Rank.java new file mode 100644 index 00000000..b7ebb707 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/rank/Rank.java @@ -0,0 +1,206 @@ +package me.totalfreedom.totalfreedommod.rank; + +import net.md_5.bungee.api.ChatColor; + +public enum Rank implements Displayable +{ + IMPOSTOR("an", "Impostor", Type.PLAYER, "Imp", ChatColor.YELLOW, null, false, false), + NON_OP("a", "Non-Op", Type.PLAYER, "", ChatColor.WHITE, null, false, false), + OP("an", "Operator", Type.PLAYER, "OP", ChatColor.GREEN, null, false, false), + ADMIN("an", "Admin", Type.ADMIN, "Admin", ChatColor.DARK_GREEN, org.bukkit.ChatColor.DARK_GREEN, true, true), + SENIOR_ADMIN("a", "Senior Admin", Type.ADMIN, "SrA", ChatColor.GOLD, org.bukkit.ChatColor.GOLD, true, true), + ADMIN_CONSOLE("the", "Console", Type.ADMIN_CONSOLE, "Console", ChatColor.DARK_PURPLE, null, false, false), + SENIOR_CONSOLE("the", "Console", Type.ADMIN_CONSOLE, "Console", ChatColor.DARK_PURPLE, null, false, false); + + private final Type type; + + private final String name; + + private final String abbr; + + private final String article; + + private final String tag; + + private final String coloredTag; + + private final ChatColor color; + + private final org.bukkit.ChatColor teamColor; + + private final boolean hasTeam; + + private final boolean hasDefaultLoginMessage; + + Rank(String article, String name, Type type, String abbr, ChatColor color, org.bukkit.ChatColor teamColor, Boolean hasTeam, Boolean hasDefaultLoginMessage) + { + this.type = type; + this.name = name; + this.abbr = abbr; + this.article = article; + this.tag = abbr.isEmpty() ? "" : "[" + abbr + "]"; + this.coloredTag = abbr.isEmpty() ? "" : ChatColor.DARK_GRAY + "[" + color + abbr + ChatColor.DARK_GRAY + "]" + color; + this.color = color; + this.teamColor = teamColor; + this.hasTeam = hasTeam; + this.hasDefaultLoginMessage = hasDefaultLoginMessage; + } + + public static Rank findRank(String string) + { + try + { + return Rank.valueOf(string.toUpperCase()); + } + catch (Exception ignored) + { + } + + return Rank.NON_OP; + } + + @Override + public String getColoredName() + { + return color + name; + } + + @Override + public String getColoredLoginMessage() + { + return article + " " + color + name; + } + + @Override + public boolean hasTeam() + { + return hasTeam; + } + + @Override + public boolean hasDefaultLoginMessage() + { + return hasDefaultLoginMessage; + } + + @Override + public String getAbbr() + { + return abbr; + } + + public boolean isConsole() + { + return getType() == Type.ADMIN_CONSOLE; + } + + public int getLevel() + { + return ordinal(); + } + + public boolean isAtLeast(Rank rank) + { + if (getLevel() < rank.getLevel()) + { + return false; + } + + if (!hasConsoleVariant() || !rank.hasConsoleVariant()) + { + return true; + } + + assert getConsoleVariant() != null; + assert rank.getConsoleVariant() != null; + return getConsoleVariant().getLevel() >= rank.getConsoleVariant().getLevel(); + } + + public boolean isAdmin() + { + return getType() == Type.ADMIN || getType() == Type.ADMIN_CONSOLE; + } + + public boolean hasConsoleVariant() + { + return getConsoleVariant() != null; + } + + public Rank getConsoleVariant() + { + switch (this) + { + case ADMIN: + case ADMIN_CONSOLE: + return ADMIN_CONSOLE; + case SENIOR_ADMIN: + case SENIOR_CONSOLE: + return SENIOR_CONSOLE; + default: + return null; + } + } + + public Type getType() + { + return type; + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getArticle() + { + return article; + } + + @Override + public String getTag() + { + return tag; + } + + @Override + public String getColoredTag() + { + return coloredTag; + } + + @Override + public ChatColor getColor() + { + return color; + } + + @Override + public org.bukkit.ChatColor getTeamColor() + { + return teamColor; + } + + public boolean isHasTeam() + { + return hasTeam; + } + + public boolean isHasDefaultLoginMessage() + { + return hasDefaultLoginMessage; + } + + public enum Type + { + PLAYER, + ADMIN, + ADMIN_CONSOLE; + + public boolean isAdmin() + { + return this != PLAYER; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/rank/RankManager.java b/src/main/java/me/totalfreedom/totalfreedommod/rank/RankManager.java new file mode 100644 index 00000000..d246b19f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/rank/RankManager.java @@ -0,0 +1,321 @@ +package me.totalfreedom.totalfreedommod.rank; + +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.FPlayer; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +public class RankManager extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public Displayable getDisplay(CommandSender sender) + { + if (!(sender instanceof Player)) + { + return getRank(sender); // Consoles don't have display ranks + } + + final Player player = (Player)sender; + + // Display impostors + if (plugin.al.isAdminImpostor(player)) + { + return Rank.IMPOSTOR; + } + + // If the player's an owner, display that + if (ConfigEntry.SERVER_OWNERS.getList().contains(player.getName())) + { + return Title.OWNER; + } + + // Developers always show up + if (FUtil.isDeveloper(player)) + { + return Title.DEVELOPER; + } + + if (ConfigEntry.SERVER_EXECUTIVES.getList().contains(player.getName()) && plugin.al.isAdmin(player)) + { + return Title.EXECUTIVE; + } + + if (ConfigEntry.SERVER_ASSISTANT_EXECUTIVES.getList().contains(player.getName()) && plugin.al.isAdmin(player)) + { + return Title.ASSTEXEC; + } + + if (plugin.al.isVerifiedAdmin(player)) + { + return Title.VERIFIED_ADMIN; + } + + // Master builders show up if they are not an admin + if (plugin.pl.getData(player).isMasterBuilder() && !plugin.al.isAdmin(player)) + { + return Title.MASTER_BUILDER; + } + + return getRank(player); + } + + public Displayable getDisplay(Admin admin) + { + // If the player's an owner, display that + if (ConfigEntry.SERVER_OWNERS.getList().contains(admin.getName())) + { + return Title.OWNER; + } + + // Developers always show up + if (FUtil.isDeveloper((Player)admin)) + { + return Title.DEVELOPER; + } + + if (ConfigEntry.SERVER_EXECUTIVES.getList().contains(admin.getName())) + { + return Title.EXECUTIVE; + } + + return admin.getRank(); + } + + public Rank getRank(CommandSender sender) + { + if (sender instanceof Player) + { + return getRank((Player)sender); + } + + // CONSOLE? + if (sender.getName().equals("CONSOLE")) + { + return ConfigEntry.ADMINLIST_CONSOLE_IS_ADMIN.getBoolean() ? Rank.SENIOR_CONSOLE : Rank.ADMIN_CONSOLE; + } + + // Console admin, get by name + Admin admin = plugin.al.getEntryByName(sender.getName()); + + // Unknown console: RCON? + if (admin == null) + { + return Rank.SENIOR_CONSOLE; + } + + Rank rank = admin.getRank(); + + // Get console + if (rank.hasConsoleVariant()) + { + rank = rank.getConsoleVariant(); + } + return rank; + } + + public Rank getRank(Player player) + { + if (plugin.al.isAdminImpostor(player) || plugin.pl.isPlayerImpostor(player)) + { + return Rank.IMPOSTOR; + } + + final Admin entry = plugin.al.getAdmin(player); + if (entry != null) + { + return entry.getRank(); + } + + return player.isOp() ? Rank.OP : Rank.NON_OP; + } + + public String getTag(Player player, String defaultTag) + { + String tag = defaultTag; + + PlayerData playerData = plugin.pl.getData(player); + String t = playerData.getTag(); + if (t != null && !t.isEmpty()) + { + tag = t; + } + + return tag; + } + + public void updateDisplay(Player player) + { + if (!player.isOnline()) + { + return; + } + FPlayer fPlayer = plugin.pl.getPlayer(player); + PlayerData data = plugin.pl.getData(player); + Displayable display = getDisplay(player); + if (plugin.al.isAdmin(player) || data.isMasterBuilder() || FUtil.isDeveloper(player)) + { + String displayName = display.getColor() + player.getName(); + player.setPlayerListName(displayName); + } + else + { + fPlayer.setTag(null); + player.setPlayerListName(null); + } + fPlayer.setTag(getTag(player, display.getColoredTag())); + updatePlayerTeam(player); + plugin.pem.setPermissions(player); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + final FPlayer fPlayer = plugin.pl.getPlayer(player); + PlayerData target = plugin.pl.getData(player); + + // Unban admins + boolean isAdmin = plugin.al.isAdmin(player); + if (isAdmin) + { + // Verify strict IP match + if (!plugin.al.isIdentityMatched(player)) + { + FUtil.bcastMsg("Warning: " + player.getName() + " is an admin, but is using an account not registered to one of their ip-list.", ChatColor.RED); + fPlayer.setSuperadminIdVerified(false); + } + else + { + fPlayer.setSuperadminIdVerified(true); + plugin.al.updateLastLogin(player); + } + } + + if (plugin.al.isVerifiedAdmin(player)) + { + FUtil.bcastMsg("Warning: " + player.getName() + " is an admin, but does not have any admin permissions.", ChatColor.RED); + } + + // Handle impostors + boolean isImpostor = plugin.al.isAdminImpostor(player) || plugin.pl.isPlayerImpostor(player); + if (isImpostor) + { + FUtil.bcastMsg(ChatColor.AQUA + player.getName() + " is " + Rank.IMPOSTOR.getColoredLoginMessage()); + if (plugin.al.isAdminImpostor(player)) + { + FUtil.bcastMsg("Warning: " + player.getName() + " has been flagged as an admin impostor and has been frozen!", ChatColor.RED); + } + else if (plugin.pl.isPlayerImpostor(player)) + { + FUtil.bcastMsg("Warning: " + player.getName() + " has been flagged as a player impostor and has been frozen!", ChatColor.RED); + } + String displayName = Rank.IMPOSTOR.getColor() + player.getName(); + player.setPlayerListName(StringUtils.substring(displayName, 0, 16)); + player.getInventory().clear(); + player.setOp(false); + player.setGameMode(GameMode.SURVIVAL); + plugin.pl.getPlayer(player).getFreezeData().setFrozen(true); + player.sendMessage(ChatColor.RED + "You are marked as an impostor, please verify yourself!"); + return; + } + + // Broadcast login message + if (isAdmin || FUtil.isDeveloper(player) || plugin.pl.getData(player).isMasterBuilder() || plugin.pl.getData(player).hasLoginMessage()) + { + if (!plugin.al.isVanished(player.getName())) + { + FUtil.bcastMsg(craftLoginMessage(player, null)); + } + } + + // Set display + updateDisplay(player); + + if (!plugin.pl.isPlayerImpostor(player) && target.hasVerification()) + { + if (target.getTag() != null) + { + plugin.pl.getData(player).setTag(FUtil.colorize(target.getTag())); + } + } + } + + public String craftLoginMessage(Player player, String message) + { + Displayable display = plugin.rm.getDisplay(player); + PlayerData playerData = plugin.pl.getData(player); + if (message == null) + { + if (playerData.hasLoginMessage()) + { + message = playerData.getLoginMessage(); + } + else + { + if (display.hasDefaultLoginMessage()) + { + message = "%name% is %art% %coloredrank%"; + } + } + } + if (message != null) + { + return FUtil.colorize(ChatColor.AQUA + (message.contains("%name%") ? "" : player.getName() + " is ") + + FUtil.colorize(message).replace("%name%", player.getName()) + .replace("%rank%", display.getName()) + .replace("%coloredrank%", display.getColoredName()) + .replace("%art%", display.getArticle())); + } + + return null; + } + + @SuppressWarnings("deprecation") + public void updatePlayerTeam(Player player) + { + Displayable display = getDisplay(player); + Scoreboard scoreboard = Objects.requireNonNull(server.getScoreboardManager()).getMainScoreboard(); + Team team = scoreboard.getPlayerTeam(player); + if (!display.hasTeam()) + { + if (team != null) + { + team.removePlayer(player); + } + return; + } + String name = StringUtils.substring(display.toString(), 0, 16); + team = scoreboard.getTeam(name); + if (team == null) + { + team = scoreboard.registerNewTeam(name); + team.setColor(display.getTeamColor()); + } + if (!team.hasPlayer(player)) + { + team.addPlayer(player); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/rank/Title.java b/src/main/java/me/totalfreedom/totalfreedommod/rank/Title.java new file mode 100644 index 00000000..93bd0339 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/rank/Title.java @@ -0,0 +1,111 @@ +package me.totalfreedom.totalfreedommod.rank; + +import net.md_5.bungee.api.ChatColor; + +public enum Title implements Displayable +{ + + MASTER_BUILDER("a", "Master Builder", ChatColor.DARK_AQUA, org.bukkit.ChatColor.DARK_AQUA, "MB", true, true), + VERIFIED_ADMIN("a", "Verified Admin", ChatColor.LIGHT_PURPLE, org.bukkit.ChatColor.LIGHT_PURPLE, "VA", false, true), + EXECUTIVE("an", "Executive", ChatColor.RED, org.bukkit.ChatColor.RED, "Exec", true, true), + ASSTEXEC("an", "Assistant Executive", ChatColor.RED, org.bukkit.ChatColor.RED, "Asst Exec", true, true), + DEVELOPER("a", "Developer", ChatColor.DARK_PURPLE, org.bukkit.ChatColor.DARK_PURPLE, "Dev", true, true), + OWNER("the", "Owner", ChatColor.DARK_RED, org.bukkit.ChatColor.DARK_RED, "Owner", true, true); + + + private final String article; + + private final String name; + + private final String abbr; + + private final String tag; + + private final String coloredTag; + + private final ChatColor color; + + private final org.bukkit.ChatColor teamColor; + + private final boolean hasTeam; + private final boolean hasDefaultLoginMessage; + + Title(String article, String name, ChatColor color, org.bukkit.ChatColor teamColor, String tag, Boolean hasTeam, Boolean hasDefaultLoginMessage) + { + this.article = article; + this.name = name; + this.coloredTag = ChatColor.DARK_GRAY + "[" + color + tag + ChatColor.DARK_GRAY + "]" + color; + this.abbr = tag; + this.tag = "[" + tag + "]"; + this.color = color; + this.teamColor = teamColor; + this.hasTeam = hasTeam; + this.hasDefaultLoginMessage = hasDefaultLoginMessage; + } + + @Override + public String getColoredName() + { + return color + name; + } + + @Override + public boolean hasTeam() + { + return hasTeam; + } + + @Override + public boolean hasDefaultLoginMessage() + { + return hasDefaultLoginMessage; + } + + @Override + public String getColoredLoginMessage() + { + return article + " " + color + name; + } + + @Override + public String getArticle() + { + return article; + } + + @Override + public String getName() + { + return name; + } + + @Override + public String getAbbr() + { + return abbr; + } + + @Override + public String getTag() + { + return tag; + } + + @Override + public String getColoredTag() + { + return coloredTag; + } + + @Override + public ChatColor getColor() + { + return color; + } + + @Override + public org.bukkit.ChatColor getTeamColor() + { + return teamColor; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/shop/Shop.java b/src/main/java/me/totalfreedom/totalfreedommod/shop/Shop.java new file mode 100644 index 00000000..5eebbb2d --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/shop/Shop.java @@ -0,0 +1,425 @@ +package me.totalfreedom.totalfreedommod.shop; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +public class Shop extends FreedomService +{ + public final int coinsPerReactionWin = ConfigEntry.SHOP_REACTIONS_COINS_PER_WIN.getInteger(); + public final String prefix = ChatColor.DARK_GRAY + "[" + ChatColor.YELLOW + "Reaction" + ChatColor.DARK_GRAY + "] "; + private final String LOGIN_MESSAGE_GUI_TITLE = ChatColor.DARK_GREEN + ChatColor.BOLD.toString() + "Login Messages"; + public String reactionString = ""; + public Date reactionStartTime; + public BukkitTask countdownTask; + private BukkitTask reactions; + private BossBar countdownBar = null; + + @Override + public void onStart() + { + if (ConfigEntry.SHOP_REACTIONS_ENABLED.getBoolean()) + { + startReactionTimer(); + } + } + + public void startReactionTimer() + { + long interval = ConfigEntry.SHOP_REACTIONS_INTERVAL.getInteger() * 20L; + + reactions = new BukkitRunnable() + { + + @Override + public void run() + { + startReaction(); + } + }.runTaskLater(plugin, interval); + } + + public void forceStartReaction() + { + reactions.cancel(); + startReaction(); + } + + public void startReaction() + { + if (!ConfigEntry.SHOP_ENABLED.getBoolean()) + { + FLog.debug("The shop is not enabled, therefore a reaction did not start."); + return; + } + + reactionString = FUtil.randomAlphanumericString(ConfigEntry.SHOP_REACTIONS_STRING_LENGTH.getInteger()); + + FUtil.bcastMsg(prefix + ChatColor.AQUA + "Enter the code above to win " + ChatColor.GOLD + coinsPerReactionWin + ChatColor.AQUA + " coins!", false); + + reactionStartTime = new Date(); + + countdownBar = server.createBossBar(reactionString, BarColor.GREEN, BarStyle.SOLID); + for (Player player : server.getOnlinePlayers()) + { + countdownBar.addPlayer(player); + } + countdownBar.setVisible(true); + countdownTask = new BukkitRunnable() + { + double seconds = 30; + final double max = seconds; + + @Override + public void run() + { + if ((seconds -= 1) == 0) + { + endReaction(null); + } + else + { + countdownBar.setProgress(seconds / max); + if (!countdownBar.getColor().equals(BarColor.YELLOW) && seconds / max <= 0.25) + { + countdownBar.setColor(BarColor.YELLOW); + } + } + } + }.runTaskTimer(plugin, 0, 20); + } + + public void endReaction(String winner) + { + countdownTask.cancel(); + countdownBar.removeAll(); + countdownBar = null; + reactionString = ""; + + if (winner != null) + { + Date currentTime = new Date(); + long seconds = (currentTime.getTime() - reactionStartTime.getTime()) / 1000; + FUtil.bcastMsg(prefix + ChatColor.GREEN + winner + ChatColor.AQUA + " won in " + seconds + " seconds!", false); + startReactionTimer(); + return; + } + + FUtil.bcastMsg(prefix + ChatColor.RED + "No one reacted fast enough", false); + startReactionTimer(); + } + + @Override + public void onStop() + { + if (ConfigEntry.SHOP_REACTIONS_ENABLED.getBoolean()) + { + reactions.cancel(); + } + } + + public String getShopPrefix() + { + return FUtil.colorize(ConfigEntry.SHOP_PREFIX.getString()); + } + + public String getShopTitle() + { + return FUtil.colorize(ConfigEntry.SHOP_TITLE.getString()); + } + + public Inventory generateShopGUI(PlayerData playerData) + { + Inventory gui = server.createInventory(null, 36, getShopTitle()); + for (int slot = 0; slot < 36; slot++) + { + ItemStack blank = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); + ItemMeta meta = blank.getItemMeta(); + assert meta != null; + meta.setDisplayName(" "); + blank.setItemMeta(meta); + gui.setItem(slot, blank); + } + for (ShopItem shopItem : ShopItem.values()) + { + ItemStack item = shopGUIItem(shopItem, playerData); + gui.setItem(shopItem.getSlot(), item); + } + // Coins + ItemStack coins = new ItemStack(Material.GOLD_NUGGET); + ItemMeta meta = coins.getItemMeta(); + assert meta != null; + meta.setDisplayName(FUtil.colorize("&c&lYou have &e&l" + playerData.getCoins() + "&c&l coins")); + coins.setItemMeta(meta); + gui.setItem(35, coins); + return gui; + } + + public Inventory generateLoginMessageGUI(Player player) + { + Inventory gui = server.createInventory(null, 36, LOGIN_MESSAGE_GUI_TITLE); + int slot = 0; + for (String loginMessage : ConfigEntry.SHOP_LOGIN_MESSAGES.getStringList()) + { + ItemStack icon = new ItemStack(Material.NAME_TAG); + ItemMeta meta = icon.getItemMeta(); + assert meta != null; + meta.setDisplayName(FUtil.colorize(plugin.rm.craftLoginMessage(player, loginMessage))); + icon.setItemMeta(meta); + gui.setItem(slot, icon); + slot++; + } + ItemStack clear = new ItemStack(Material.BARRIER); + ItemMeta meta = clear.getItemMeta(); + assert meta != null; + meta.setDisplayName(ChatColor.RED + "Clear login message"); + clear.setItemMeta(meta); + gui.setItem(35, clear); + return gui; + } + + public boolean isRealItem(PlayerData data, ShopItem shopItem, PlayerInventory inventory, ItemStack realItem) + { + return isRealItem(data, shopItem, inventory.getItemInMainHand(), realItem) || isRealItem(data, shopItem, inventory.getItemInOffHand(), realItem); + } + + public boolean isRealItem(PlayerData data, ShopItem shopItem, ItemStack givenItem, ItemStack realItem) + { + if (!data.hasItem(shopItem) || !givenItem.getType().equals(realItem.getType())) + { + return false; + } + + ItemMeta givenMeta = givenItem.getItemMeta(); + ItemMeta realMeta = realItem.getItemMeta(); + + assert givenMeta != null; + assert realMeta != null; + return givenMeta.getDisplayName().equals(realMeta.getDisplayName()) && Objects.equals(givenMeta.getLore(), realMeta.getLore()); + } + + public ItemStack getLightningRod() + { + ItemStack itemStack = new ItemStack(Material.BLAZE_ROD); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(FUtil.colorize("&bL&3i&bg&3h&bt&3n&bi&3n&bg &3R&bo&3d")); + itemMeta.setLore(Arrays.asList(ChatColor.AQUA + "Strike others down with the power of lightning.", ChatColor.RED + ChatColor.ITALIC.toString() + "The classic way to exterminate annoyances.")); + itemMeta.addEnchant(Enchantment.CHANNELING, 1, false); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemStack getGrapplingHook() + { + ItemStack itemStack = new ItemStack(Material.FISHING_ROD); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.YELLOW + "Grappling Hook"); + itemMeta.setLore(Collections.singletonList(ChatColor.GREEN + "be spider-man but ghetto")); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemStack getFireBall() + { + ItemStack itemStack = new ItemStack(Material.FIRE_CHARGE); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.RED + "Fire Ball"); + itemMeta.setLore(Collections.singletonList(ChatColor.GOLD + "Yeet this at people")); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemStack getRideablePearl() + { + ItemStack itemStack = new ItemStack(Material.ENDER_PEARL); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.DARK_PURPLE + "Rideable Ender Pearl"); + itemMeta.setLore(Arrays.asList(ChatColor.LIGHT_PURPLE + "What the title says.", "", ChatColor.WHITE + ChatColor.ITALIC.toString() + "TotalFreedom is not responsible for any injuries", ChatColor.WHITE + ChatColor.ITALIC.toString() + "sustained while using this item.")); + itemMeta.addEnchant(Enchantment.BINDING_CURSE, 1, false); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemStack getStackingPotato() + { + ItemStack itemStack = new ItemStack(Material.POTATO); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.YELLOW + "Stacking Potato"); + itemMeta.setLore(Collections.singletonList(ChatColor.GREEN + "Left click to ride a mob, right click to put a mob on your head.")); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemStack getClownFish() + { + ItemStack itemStack = new ItemStack(Material.TROPICAL_FISH); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(ChatColor.GOLD + "Clown Fish"); + itemMeta.setLore(Collections.singletonList(ChatColor.AQUA + ":clown:")); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public boolean canAfford(int price, int coins) + { + return coins >= price; + } + + public int amountNeeded(int price, int coins) + { + return price - coins; + } + + public ItemStack shopGUIItem(ShopItem item, PlayerData data) + { + ItemStack itemStack = new ItemStack(item.getIcon()); + ItemMeta itemMeta = itemStack.getItemMeta(); + assert itemMeta != null; + itemMeta.setDisplayName(item.getColoredName()); + int price = item.getCost(); + int coins = data.getCoins(); + boolean canAfford = canAfford(price, coins); + List lore = new ArrayList<>(); + if (!data.hasItem(item)) + { + lore.add(ChatColor.GOLD + "Price: " + (canAfford ? ChatColor.DARK_GREEN : ChatColor.RED) + price); + if (!canAfford) + { + lore.add(ChatColor.RED + "You can not afford this item!"); + lore.add(ChatColor.RED + "You need " + amountNeeded(price, coins) + " more coins to buy this item."); + } + } + else + { + lore.add(ChatColor.RED + "You already purchased this item."); + } + itemMeta.setLore(lore); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + @EventHandler(priority = EventPriority.HIGH) + public void onShopGUIClick(InventoryClickEvent event) + { + if (!(event.getWhoClicked() instanceof Player)) + { + return; + } + + Inventory inventory = event.getInventory(); + if (inventory.getSize() != 36 || !event.getView().getTitle().equals(getShopTitle())) + { + return; + } + event.setCancelled(true); + + ShopItem shopItem = getShopItem(event.getSlot()); + if (shopItem == null) + { + return; + } + + Player player = (Player)event.getWhoClicked(); + PlayerData playerData = plugin.pl.getData(player); + int price = shopItem.getCost(); + int coins = playerData.getCoins(); + + if (playerData.hasItem(shopItem) || !canAfford(price, coins)) + { + return; + } + + playerData.giveItem(shopItem); + playerData.setCoins(coins - price); + plugin.pl.save(playerData); + + player.closeInventory(); + + player.sendMessage(getShopPrefix() + " " + ChatColor.GREEN + "Successfully purchased the \"" + shopItem.getColoredName() + ChatColor.GREEN + "\" for " + ChatColor.GOLD + price + ChatColor.GREEN + "!"); + + if (shopItem.getCommand() != null) + { + player.sendMessage(ChatColor.GREEN + "Run " + shopItem.getCommand() + " to get one!"); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onLoginMessageGUIClick(InventoryClickEvent event) + { + if (!(event.getWhoClicked() instanceof Player)) + { + return; + } + + Inventory inventory = event.getInventory(); + if (inventory.getSize() != 36 || !event.getView().getTitle().equals(LOGIN_MESSAGE_GUI_TITLE)) + { + return; + } + event.setCancelled(true); + + int slot = event.getSlot(); + + Player player = (Player)event.getWhoClicked(); + PlayerData data = plugin.pl.getData(player); + + if (slot == 35) + { + data.setLoginMessage(null); + plugin.pl.save(data); + player.sendMessage(ChatColor.GREEN + "Removed your login message"); + } + else + { + String message = ConfigEntry.SHOP_LOGIN_MESSAGES.getStringList().get(slot); + data.setLoginMessage(message); + plugin.pl.save(data); + player.sendMessage(ChatColor.GREEN + "Your login message is now the following:\n" + plugin.rm.craftLoginMessage(player, message)); + } + + player.closeInventory(); + + } + + public ShopItem getShopItem(int slot) + { + for (ShopItem shopItem : ShopItem.values()) + { + if (shopItem.getSlot() == slot) + { + return shopItem; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/shop/ShopItem.java b/src/main/java/me/totalfreedom/totalfreedommod/shop/ShopItem.java new file mode 100644 index 00000000..4ce94dd5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/shop/ShopItem.java @@ -0,0 +1,106 @@ +package me.totalfreedom.totalfreedommod.shop; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.bukkit.ChatColor; +import org.bukkit.Material; + +public enum ShopItem +{ + GRAPPLING_HOOK("Grappling Hook", Material.FISHING_ROD, 10, ConfigEntry.SHOP_PRICES_GRAPPLING_HOOK, ChatColor.GREEN, "grapplingHook", "/grapplinghook"), + LIGHTNING_ROD("Lightning Rod", Material.BLAZE_ROD, 12, ConfigEntry.SHOP_PRICES_LIGHTNING_ROD, ChatColor.LIGHT_PURPLE, "lightningRod", "/lightningrod"), + FIRE_BALL("Fire Ball", Material.FIRE_CHARGE, 14, ConfigEntry.SHOP_PRICES_FIRE_BALL, ChatColor.RED, "fireBall", "/fireball"), + RIDEABLE_PEARL("Rideable Ender Pearl", Material.ENDER_PEARL, 16, ConfigEntry.SHOP_PRICES_RIDEABLE_PEARL, ChatColor.DARK_PURPLE, "rideablePearl", "/rideablepearl"), + STACKING_POTATO("Stacking Potato", Material.POTATO, 20, ConfigEntry.SHOP_PRICES_STACKING_POTATO, ChatColor.YELLOW, "stackingPotato", "/stackingpotato"), + CLOWN_FISH("Clown Fish", Material.TROPICAL_FISH, 22, ConfigEntry.SHOP_PRICES_CLOWN_FISH, ChatColor.GOLD, "clownFish", "/clownfish"), + LOGIN_MESSAGES("Login Messages", Material.NAME_TAG, 24, ConfigEntry.SHOP_PRICES_LOGIN_MESSAGES, ChatColor.DARK_GREEN, "loginMessages", "/loginmessage"); + + /* + Shop GUI Layout: + + Dimensions: 9x4 = 36 + Key: g = Grappling Hook, l = Lightning Rod, f = Fire Ball, r = Rideable Ender Pearl, s = Stacking Potato, c = Clown Fish, x = Login Messages $ = Coins} + + --------- + -g-l-f-r- + --s-c-x-- + --------$ + */ + + + private final String name; + + private final Material icon; + + private final int slot; + private final ConfigEntry cost; + + private final ChatColor color; + + private final String dataName; + + private final String command; + + ShopItem(String name, Material icon, int slot, ConfigEntry cost, ChatColor color, String dataName, String command) + { + this.name = name; + this.icon = icon; + this.slot = slot; + this.cost = cost; + this.color = color; + this.dataName = dataName; + this.command = command; + } + + public static ShopItem findItem(String string) + { + try + { + return ShopItem.valueOf(string.toUpperCase()); + } + catch (Exception ignored) + { + } + + return null; + } + + public String getColoredName() + { + return color + name; + } + + public int getCost() + { + return cost.getInteger(); + } + + public String getName() + { + return name; + } + + public Material getIcon() + { + return icon; + } + + public int getSlot() + { + return slot; + } + + public ChatColor getColor() + { + return color; + } + + public String getDataName() + { + return dataName; + } + + public String getCommand() + { + return command; + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/shop/Votifier.java b/src/main/java/me/totalfreedom/totalfreedommod/shop/Votifier.java new file mode 100644 index 00000000..c559d061 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/shop/Votifier.java @@ -0,0 +1,56 @@ +package me.totalfreedom.totalfreedommod.shop; + +import com.vexsoftware.votifier.model.Vote; +import com.vexsoftware.votifier.model.VotifierEvent; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; + +public class Votifier extends FreedomService +{ + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerVote(VotifierEvent event) + { + Vote vote = event.getVote(); + String name = vote.getUsername(); + int coinsPerVote = ConfigEntry.SHOP_COINS_PER_VOTE.getInteger(); + Player player = server.getPlayer(name); + PlayerData data; + if (player != null) + { + data = plugin.pl.getData(player); + } + else + { + data = plugin.pl.getData(name); + } + + if (data != null) + { + data.setCoins(data.getCoins() + coinsPerVote); + data.setTotalVotes(data.getTotalVotes() + 1); + plugin.pl.save(data); + FUtil.bcastMsg(ChatColor.GREEN + name + ChatColor.AQUA + " has voted for us on " + ChatColor.GREEN + vote.getServiceName() + ChatColor.AQUA + "!"); + } + + if (player != null) + { + player.sendMessage(ChatColor.GREEN + "Thank you for voting for us! Here are " + coinsPerVote + " coins!"); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java b/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java new file mode 100644 index 00000000..09551e02 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/sql/SQLite.java @@ -0,0 +1,428 @@ +package me.totalfreedom.totalfreedommod.sql; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.MessageFormat; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.admin.Admin; +import me.totalfreedom.totalfreedommod.banning.Ban; +import me.totalfreedom.totalfreedommod.player.PlayerData; +import me.totalfreedom.totalfreedommod.util.FLog; +import me.totalfreedom.totalfreedommod.util.FUtil; + +public class SQLite extends FreedomService +{ + private final String FILE_NAME = "database.db"; + + private Connection connection; + + @Override + public void onStart() + { + connect(); + checkTables(); + } + + @Override + public void onStop() + { + disconnect(); + } + + public void connect() + { + try + { + connection = DriverManager.getConnection("jdbc:sqlite:" + plugin.getDataFolder() + "/" + FILE_NAME); + FLog.info("Successfully connected to the database."); + } + catch (SQLException e) + { + FLog.severe("Failed to connect to the database: " + e.getMessage()); + } + } + + public void disconnect() + { + try + { + if (connection != null) + { + connection.close(); + } + } + catch (SQLException e) + { + FLog.severe("Failed to disconnect from the database: " + e.getMessage()); + } + } + + public void checkTables() + { + try + { + DatabaseMetaData meta = connection.getMetaData(); + if (tableExists(meta, "bans")) + { + try + { + connection.createStatement().execute("CREATE TABLE `bans` (`name` VARCHAR, `uuid` VARCHAR, `ips` VARCHAR, `by` VARCHAR NOT NULL, `at` LONG NOT NULL, `expires` LONG, `reason` VARCHAR);"); + } + catch (SQLException e) + { + FLog.severe("Failed to create the bans table: " + e.getMessage()); + } + } + + if (tableExists(meta, "admins")) + { + try + { + connection.createStatement().execute("CREATE TABLE `admins` (`username` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `rank` VARCHAR NOT NULL, `active` BOOLEAN NOT NULL, `last_login` LONG NOT NULL, `command_spy` BOOLEAN NOT NULL, `potion_spy` BOOLEAN NOT NULL, `ac_format` VARCHAR, `ptero_id` VARCHAR);"); + } + catch (SQLException e) + { + FLog.severe("Failed to create the admins table: " + e.getMessage()); + } + } + if (tableExists(meta, "players")) + { + try + { + connection.createStatement().execute("CREATE TABLE `players` (`username` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `notes` VARCHAR, `tag` VARCHAR, `discord_id` VARCHAR, `backup_codes` VARCHAR, `master_builder` BOOLEAN NOT NULL,`verification` BOOLEAN NOT NULL, `ride_mode` VARCHAR NOT NULL, `coins` INT, `items` VARCHAR, `total_votes` INT NOT NULL, `display_discord` BOOLEAN NOT NULL, `login_message` VARCHAR, `inspect` BOOLEAN NOT NULL);"); + } + catch (SQLException e) + { + FLog.severe("Failed to create the players table: " + e.getMessage()); + } + } + } + catch (SQLException e) + { + FLog.severe("Failed to check tables on database: " + e.getMessage()); + } + } + + public void truncate(String table) + { + try + { + connection.createStatement().execute("DELETE FROM " + table); + } + catch (SQLException e) + { + FLog.severe("Failed to truncate " + table + ": " + e.getMessage()); + } + } + + public ResultSet getBanList() throws SQLException + { + return connection.createStatement().executeQuery("SELECT * FROM bans"); + } + + public ResultSet getAdminList() throws SQLException + { + return connection.createStatement().executeQuery("SELECT * FROM admins"); + } + + public void setAdminValue(Admin admin, String key, Object value) + { + try + { + Object[] data = {key, admin.getName()}; + PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE admins SET {0}=? WHERE username=''{1}''", data)); + statement = setUnknownType(statement, 1, value); + statement.executeUpdate(); + + } + catch (SQLException e) + { + FLog.severe("Failed to update admin value:"); + FLog.severe(e); + } + } + + public void setPlayerValue(PlayerData player, String key, Object value) + { + try + { + Object[] data = {key, player.getName()}; + PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE players SET {0}=? WHERE username=''{1}''", data)); + statement = setUnknownType(statement, 1, value); + statement.executeUpdate(); + + } + catch (SQLException e) + { + FLog.severe("Failed to update player value: " + e.getMessage()); + } + } + + public void updateAdminName(String oldName, String newName) + { + try + { + PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE admins SET username=? WHERE username=''{0}''", oldName)); + statement = setUnknownType(statement, 1, newName); + statement.executeUpdate(); + + } + catch (SQLException e) + { + FLog.severe("Failed to update admin name: " + e.getMessage()); + } + } + + public void updatePlayerName(String oldName, String newName) + { + try + { + PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE players SET username=? WHERE username=''{0}''", oldName)); + statement = setUnknownType(statement, 1, newName); + statement.executeUpdate(); + + } + catch (SQLException e) + { + FLog.severe("Failed to update player name: " + e.getMessage()); + } + } + + public PreparedStatement setUnknownType(PreparedStatement statement, int index, Object value) throws SQLException + { + if (value == null) + { + statement.setString(index, null); + } + else if (value.getClass().equals(String.class)) + { + String v = (String)value; + statement.setString(index, v); + } + else if (value.getClass().equals(Integer.class)) + { + int v = (int)value; + statement.setInt(index, v); + } + else if (value.getClass().equals(Boolean.class)) + { + boolean v = (boolean)value; + statement.setBoolean(index, v); + } + else if (value.getClass().equals(Long.class)) + { + long v = (long)value; + statement.setLong(index, v); + } + return statement; + } + + public Object getValue(ResultSet resultSet, String key, Object value) throws SQLException + { + Object result = null; + if (value instanceof String) + { + result = resultSet.getString(key); + } + else if (value instanceof Integer) + { + result = resultSet.getInt(key); + } + else if (value instanceof Boolean) + { + result = resultSet.getObject(key); + } + else if (value instanceof Long) + { + result = resultSet.getLong(key); + } + return result; + } + + public void addAdmin(Admin admin) + { + try + { + PreparedStatement statement = connection.prepareStatement("INSERT INTO admins VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + statement.setString(1, admin.getName()); + statement.setString(2, FUtil.listToString(admin.getIps())); + statement.setString(3, admin.getRank().toString()); + statement.setBoolean(4, admin.isActive()); + statement.setLong(5, admin.getLastLogin().getTime()); + statement.setBoolean(6, admin.getCommandSpy()); + statement.setBoolean(7, admin.getPotionSpy()); + statement.setString(8, admin.getAcFormat()); + statement.setString(9, admin.getPteroID()); + statement.executeUpdate(); + } + catch (SQLException e) + { + FLog.severe("Failed to add admin:"); + FLog.severe(e); + } + } + + public void addPlayer(PlayerData player) + { + try + { + PreparedStatement statement = connection.prepareStatement("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + statement.setString(1, player.getName()); + statement.setString(2, FUtil.listToString(player.getIps())); + statement.setString(3, FUtil.listToString(player.getNotes())); + statement.setString(4, player.getTag()); + statement.setString(5, player.getDiscordID()); + statement.setString(6, FUtil.listToString(player.getBackupCodes())); + statement.setBoolean(7, player.isMasterBuilder()); + statement.setBoolean(8, player.hasVerification()); + statement.setString(9, player.getRideMode()); + statement.setInt(10, player.getCoins()); + statement.setString(11, FUtil.listToString(player.getItems())); + statement.setInt(12, player.getTotalVotes()); + statement.setBoolean(13, player.doesDisplayDiscord()); + statement.setString(14, player.getLoginMessage()); + statement.setBoolean(15, player.hasInspection()); + statement.executeUpdate(); + } + catch (SQLException e) + { + FLog.severe("Failed to add player:"); + FLog.severe(e); + } + } + + public ResultSet getAdminByName(String name) + { + try + { + ResultSet resultSet = connection.createStatement().executeQuery(MessageFormat.format("SELECT * FROM admins WHERE username=''{0}''", name)); + if (resultSet.next()) + { + return resultSet; + } + } + catch (SQLException e) + { + FLog.severe("Failed to get admin by name:"); + FLog.severe(e); + } + + return null; + } + + public ResultSet getPlayerByName(String name) + { + try + { + ResultSet resultSet = connection.createStatement().executeQuery(MessageFormat.format("SELECT * FROM players WHERE username=''{0}''", name)); + if (resultSet.next()) + { + return resultSet; + } + } + catch (SQLException e) + { + FLog.severe("Failed to get player by name:"); + FLog.severe(e); + } + + return null; + } + + public ResultSet getMasterBuilders() + { + try + { + return connection.createStatement().executeQuery("SELECT * FROM players WHERE master_builder=true"); + } + catch (SQLException e) + { + FLog.severe("Failed to get Master Builders:"); + FLog.severe(e); + } + + return null; + } + + public ResultSet getPlayerByIp(String ip) + { + try + { + ResultSet resultSet = connection.createStatement().executeQuery(MessageFormat.format("SELECT * FROM players WHERE ips LIKE ''%{0}%''", ip)); + if (resultSet.next()) + { + return resultSet; + } + } + catch (SQLException e) + { + FLog.severe("Failed to get player by ip:"); + FLog.severe(e); + } + + return null; + } + + public void removeAdmin(Admin admin) + { + try + { + connection.createStatement().executeUpdate(MessageFormat.format("DELETE FROM admins where name=''{0}''", admin.getName())); + } + catch (SQLException e) + { + FLog.severe("Failed to remove admin:"); + FLog.severe(e); + } + } + + public void addBan(Ban ban) + { + try + { + PreparedStatement statement = connection.prepareStatement("INSERT INTO bans VALUES (?, ?, ?, ?, ?, ?, ?)"); + statement.setString(1, ban.getUsername()); + String uuid = null; + if (ban.hasUUID()) + { + uuid = ban.getUuid().toString(); + } + statement.setString(2, uuid); + statement.setString(3, FUtil.listToString(ban.getIps())); + statement.setString(4, ban.getBy()); + statement.setLong(5, ban.getAt().getTime()); + statement.setLong(6, ban.getExpiryUnix()); + statement.setString(7, ban.getReason()); + statement.executeUpdate(); + } + catch (SQLException e) + { + FLog.severe("Failed to add ban: " + e.getMessage()); + } + } + + public void removeBan(Ban ban) + { + try + { + connection.createStatement().executeUpdate(MessageFormat.format("DELETE FROM bans WHERE name=''{0}''", ban.getUsername())); + for (String ip : ban.getIps()) + { + connection.createStatement().executeUpdate(MessageFormat.format("DELETE FROM bans WHERE ips LIKE ''%{0}%''", ip)); + } + } + catch (SQLException e) + { + FLog.severe("Failed to remove ban: " + e.getMessage()); + } + } + + public boolean tableExists(DatabaseMetaData meta, String name) throws SQLException + { + return !meta.getTables(null, null, name, null).next(); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/DepreciationAggregator.java b/src/main/java/me/totalfreedom/totalfreedommod/util/DepreciationAggregator.java new file mode 100644 index 00000000..fa87fc18 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/DepreciationAggregator.java @@ -0,0 +1,28 @@ +package me.totalfreedom.totalfreedommod.util; + +import java.util.HashSet; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; + +@SuppressWarnings("deprecation") +public class DepreciationAggregator +{ + public static Block getTargetBlock(LivingEntity entity, HashSet transparent, int maxDistance) + { + return entity.getTargetBlock(transparent, maxDistance); + } + + public static OfflinePlayer getOfflinePlayer(Server server, String name) + { + return server.getOfflinePlayer(name); + } + + public static String getName_EntityType(EntityType et) + { + return et.getName(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/FLog.java b/src/main/java/me/totalfreedom/totalfreedommod/util/FLog.java new file mode 100644 index 00000000..27dda2ef --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/FLog.java @@ -0,0 +1,111 @@ +package me.totalfreedom.totalfreedommod.util; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class FLog +{ + private static final Logger FALLBACK_LOGGER = Logger.getLogger("Minecraft-Server"); + private static Logger serverLogger = null; + private static Logger pluginLogger = null; + + // Level.INFO: + public static void info(String message) + { + info(message, false); + } + + public static void info(String message, Boolean raw) + { + log(Level.INFO, message, raw); + } + + public static void info(Throwable ex) + { + log(Level.INFO, ex); + } + + // Fuck spigot for not using log4j, we would of had a debug log level if they did + public static void debug(String message) + { + if (FUtil.inDeveloperMode()) + { + log(Level.INFO, "\u001B[35m[TotalFreedomMod | DEBUG] " + message + "\u001B[0m", true); + } + } + + // Level.WARNING: + public static void warning(String message) + { + warning(message, false); + } + + public static void warning(String message, Boolean raw) + { + log(Level.WARNING, message, raw); + } + + public static void warning(Throwable ex) + { + log(Level.WARNING, ex); + } + + // Level.SEVERE: + public static void severe(String message) + { + severe(message, false); + } + + public static void severe(String message, Boolean raw) + { + log(Level.SEVERE, message, raw); + } + + public static void severe(Throwable ex) + { + log(Level.SEVERE, ex); + } + + // Utility + private static void log(Level level, String message, boolean raw) + { + getLogger(raw).log(level, message); + } + + private static void log(Level level, Throwable throwable) + { + getLogger(false).log(level, null, throwable); + } + + private static Logger getLogger(boolean raw) + { + if (raw || pluginLogger == null) + { + return (serverLogger != null ? serverLogger : FALLBACK_LOGGER); + } + else + { + return pluginLogger; + } + } + + public static Logger getPluginLogger() + { + return (pluginLogger != null ? pluginLogger : FALLBACK_LOGGER); + } + + public static void setPluginLogger(Logger logger) + { + pluginLogger = logger; + } + + public static Logger getServerLogger() + { + return (serverLogger != null ? serverLogger : FALLBACK_LOGGER); + } + + public static void setServerLogger(Logger logger) + { + serverLogger = logger; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/FSync.java b/src/main/java/me/totalfreedom/totalfreedommod/util/FSync.java new file mode 100644 index 00000000..191e0feb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/FSync.java @@ -0,0 +1,101 @@ +package me.totalfreedom.totalfreedommod.util; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +public class FSync +{ + + public static void playerMsg(final Player player, final String message) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + FUtil.playerMsg(player, message); + } + + }.runTask(plugin); + } + + public static void playerMsg(final CommandSender sender, final String message) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + sender.sendMessage(message); + } + + }.runTask(plugin); + } + + public static void playerKick(final Player player, final String reason) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + player.kickPlayer(reason); + } + + }.runTask(plugin); + } + + public static void adminChatMessage(final CommandSender sender, final String message) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + plugin.cm.adminChat(sender, message); + } + + }.runTask(plugin); + } + + public static void autoEject(final Player player, final String kickMessage) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + plugin.ae.autoEject(player, kickMessage); + } + + }.runTask(plugin); + } + + public static void bcastMsg(final String message, final ChatColor color) + { + final TotalFreedomMod plugin = TotalFreedomMod.getPlugin(); + new BukkitRunnable() + { + + @Override + public void run() + { + FUtil.bcastMsg(message, color); + } + + }.runTask(plugin); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java new file mode 100644 index 00000000..a269a296 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/FUtil.java @@ -0,0 +1,886 @@ +package me.totalfreedom.totalfreedommod.util; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.json.simple.JSONArray; + +import java.io.*; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.bukkit.Bukkit.getServer; + +public class FUtil +{ + + public static final String SAVED_FLAGS_FILENAME = "savedflags.dat"; + /* See https://github.com/TotalFreedom/License - None of the listed names may be removed. + Leaving this list here for anyone running TFM on a cracked server: + public static final List DEVELOPERS = Arrays.asList("Madgeek1450", "Prozza", "WickedGamingUK", "Wild1145", "aggelosQQ", "scripthead", "Telesphoreo", "CoolJWB"); + */ + public static final List DEVELOPERS = Arrays.asList( + "1156a81a-23fb-435e-9aff-fe9c2ea7e82d", // Madgeek1450 + "f9a1982e-252e-4ed3-92ed-52b0506a39c9", // Prozza + "90eb5d86-ed60-4165-a36e-bb77aa3c6664", // WickedGamingUK + "604cbb51-842d-4b43-8b0a-d1d7c6cd2869", // Wild1145 + "e67d77c4-fff9-4cea-94cc-9f1f1ab7806b", // aggelosQQ + "0061326b-8b3d-44c8-830a-5f2d59f5dc1b", // scripthead + "67ce0e28-3d6b-469c-ab71-304eec81b614", // CoolJWB + "03b41e15-d03f-4025-86f5-f1812df200fa", // elmon_ + "d018f2b8-ce60-4672-a45f-e580e0331299", // speednt + "458de06f-36a5-4e1b-aaa6-ec1d1751c5b6", // SupItsDillon + "c8e5af82-6aba-4dd7-83e8-474381380cc9", // Paldiu + "38ea7c82-7bdc-4f48-b7fd-0e93fc26813d", // AwesomePinch + "ba5aafba-9012-418f-9819-a7020d591068", // TFTWPhoenix + "d6dd9740-40db-45f5-ab16-4ee16a633009", // Abhi + "2e06e049-24c8-42e4-8bcf-d35372af31e6", // NotInSync + "f97c0d7b-6413-4558-a409-88f09a8f9adb" // videogamesm12 + ); + public static final List DEVELOPER_NAMES = Arrays.asList("Madgeek1450", "Prozza", "WickedGamingUK", "Wild1145", "aggelosQQ", "scripthead", "CoolJWB", "elmon_", "speednt", "SupItsDillon", "Paldiu", "AwesomePinch", "TFTWPhoenix", "abhithedev", "NotInSync", "videogamesm12"); + public static final Map CHAT_COLOR_NAMES = new HashMap<>(); + public static final List CHAT_COLOR_POOL = Arrays.asList( + ChatColor.DARK_RED, + ChatColor.RED, + ChatColor.GOLD, + ChatColor.YELLOW, + ChatColor.GREEN, + ChatColor.DARK_GREEN, + ChatColor.AQUA, + ChatColor.DARK_AQUA, + ChatColor.BLUE, + ChatColor.DARK_BLUE, + ChatColor.DARK_PURPLE, + ChatColor.LIGHT_PURPLE); + private static final SplittableRandom RANDOM = new SplittableRandom(); + private static final String CHARACTER_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final Map TIMEZONE_LOOKUP = new HashMap<>(); + public static String DATE_STORAGE_FORMAT = "EEE, d MMM yyyy HH:mm:ss Z"; + + static + { + for (ChatColor chatColor : CHAT_COLOR_POOL) + { + CHAT_COLOR_NAMES.put(chatColor.name().toLowerCase().replace("_", ""), chatColor); + } + + for (int i = -12; i <= 12; i++) + { + String sec = String.valueOf(i).replace("-", ""); + if (i > -10 && i < 10) + { + sec = "0" + sec; + } + if (i >= 0) + { + sec = "+" + sec; + } + else + { + sec = "-" + sec; + } + TIMEZONE_LOOKUP.put(i, "GMT" + sec + ":00"); + } + } + + public static void cancel(BukkitTask task) + { + if (task == null) + { + return; + } + + try + { + task.cancel(); + } + catch (Exception ignored) + { + } + } + + public static boolean isExecutive(String name) + { + return ConfigEntry.SERVER_OWNERS.getStringList().contains(name) || ConfigEntry.SERVER_EXECUTIVES.getStringList().contains(name) || ConfigEntry.SERVER_ASSISTANT_EXECUTIVES.getStringList().contains(name); + } + + public static boolean isDeveloper(Player player) + { + if (Bukkit.getOnlineMode()) + { + return DEVELOPERS.contains(player.getUniqueId().toString()); + } + else + { + return DEVELOPER_NAMES.contains(player.getName()); + } + } + + public static boolean inDeveloperMode() + { + return ConfigEntry.DEVELOPER_MODE.getBoolean(); + } + + public static String formatName(String name) + { + return WordUtils.capitalizeFully(name.replace("_", " ")); + } + + public static String showS(int count) + { + return (count == 1 ? "" : "s"); + } + + public static List getPlayerList() + { + List names = new ArrayList<>(); + for (Player player : Bukkit.getOnlinePlayers()) + { + if (!TotalFreedomMod.getPlugin().al.isVanished(player.getName())) + { + names.add(player.getName()); + } + } + return names; + } + + public static String listToString(List list) + { + if (list.size() == 0) + { + return null; + } + + return String.join(", ", list); + } + + public static List stringToList(String string) + { + if (string == null) + { + return new ArrayList<>(); + } + + return Arrays.asList(string.split(", ")); + } + + /** + * A way to get a sublist with a page index and a page size. + * + * @param list A list of objects that should be split into pages. + * @param size The size of the pages. + * @param index The page index, if outside of bounds error will be thrown. The page index starts at 0 as with all lists. + * @return A list of objects that is the page that has been selected from the previous last parameter. + */ + public static List getPageFromList(List list, int size, int index) + { + try + { + if (size >= list.size()) + { + return list; + } + else if (size * (index + 1) <= list.size()) + { + return list.subList(size * index, size * (index + 1)); + } + else + { + return list.subList(size * index, (size * index) + (list.size() % size)); + } + } + catch (IndexOutOfBoundsException e) + { + return new ArrayList<>(); + } + } + + public static List getAllMaterialNames() + { + List names = new ArrayList<>(); + for (Material material : Material.values()) + { + names.add(material.name()); + } + return names; + } + + @SuppressWarnings("unchecked") + public static UUID nameToUUID(String name) + { + try + { + JSONArray json = new JSONArray(); + json.add(name); + List headers = new ArrayList<>(); + headers.add("Accept:application/json"); + headers.add("Content-Type:application/json"); + Response response = sendRequest("https://api.mojang.com/profiles/minecraft", "POST", headers, json.toString()); + // Don't care how stupid this looks, couldn't find anything to parse a json string to something readable in java with something not horrendously huge, maybe im just retarded + Pattern pattern = Pattern.compile("(?<=\"id\":\")[a-f0-9].{31}"); + Matcher matcher = pattern.matcher(response.getMessage()); + if (matcher.find()) + { + String rawUUID = matcher.group(0).replaceFirst("([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]+)", "$1-$2-$3-$4-$5"); + return UUID.fromString(rawUUID); + } + } + catch (Exception e) + { + FLog.severe("Failed to convert name to UUID:\n" + e.toString()); + } + return null; + } + + public static Response sendRequest(String endpoint, String method, List headers, String body) throws IOException + { + URL url = new URL(endpoint); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + connection.setRequestMethod(method); + + if (headers != null) + { + + for (String header : headers) + { + String[] kv = header.split(":"); + connection.setRequestProperty(kv[0], kv[1]); + } + } + + FLog.info(connection.getRequestMethod()); + + if (body != null) + { + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.writeBytes(body); + outputStream.flush(); + outputStream.close(); + } + + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String inputLine; + StringBuilder response = new StringBuilder(); + + while ((inputLine = in.readLine()) != null) + { + response.append(inputLine); + } + + in.close(); + + return new Response(connection.getResponseCode(), response.toString()); + } + + public static void bcastMsg(String message, ChatColor color) + { + bcastMsg(message, color, true); + } + + public static void bcastMsg(String message, ChatColor color, Boolean toConsole) + { + if (toConsole) + { + FLog.info(message, true); + } + + for (Player player : Bukkit.getOnlinePlayers()) + { + player.sendMessage((color == null ? "" : color) + message); + } + } + + public static void bcastMsg(String message, Boolean toConsole) + { + bcastMsg(message, null, toConsole); + } + + public static void bcastMsg(String message) + { + FUtil.bcastMsg(message, null, true); + } + + // Still in use by listeners + public static void playerMsg(CommandSender sender, String message, ChatColor color) + { + sender.sendMessage(color + message); + } + + // Still in use by listeners + public static void playerMsg(CommandSender sender, String message) + { + FUtil.playerMsg(sender, message, ChatColor.GRAY); + } + + public static void setFlying(Player player, boolean flying) + { + player.setAllowFlight(true); + player.setFlying(flying); + } + + public static void adminAction(String adminName, String action, boolean isRed) + { + FUtil.bcastMsg(adminName + " - " + action, (isRed ? ChatColor.RED : ChatColor.AQUA)); + } + + public static String formatLocation(Location location) + { + return String.format("%s: (%d, %d, %d)", + Objects.requireNonNull(location.getWorld()).getName(), + Math.round(location.getX()), + Math.round(location.getY()), + Math.round(location.getZ())); + } + + public static boolean deleteFolder(File file) + { + if (file.exists() && file.isDirectory()) + { + return FileUtils.deleteQuietly(file); + } + return false; + } + + public static void deleteCoreDumps() + { + final File[] coreDumps = new File(".").listFiles(file -> file.getName().startsWith("java.core")); + + for (File dump : coreDumps) + { + FLog.info("Removing core dump file: " + dump.getName()); + dump.delete(); + } + } + + private static final List regxList = new ArrayList() + {{ + add("y"); + add("mo"); + add("w"); + add("d"); + add("h"); + add("m"); + add("s"); + }}; + + private static long a(String parse) + { + StringBuilder sb = new StringBuilder(); + + regxList.forEach(obj -> { + if (parse.endsWith(obj)) + { + sb.append(parse.split(obj)[0]); + } + }); + + return Long.parseLong(sb.toString()); + } + + private static TimeUnit verify(String arg) + { + TimeUnit unit = null; + for (String c : regxList) + { + if (arg.endsWith(c)) + { + switch (c) + { + case "y": + unit = (TimeUnit.YEAR); + break; + case "mo": + unit = (TimeUnit.MONTH); + break; + case "w": + unit = (TimeUnit.WEEK); + break; + case "d": + unit = (TimeUnit.DAY); + break; + case "h": + unit = (TimeUnit.HOUR); + break; + case "m": + unit = (TimeUnit.MINUTE); + break; + case "s": + unit = (TimeUnit.SECOND); + break; + } + break; + } + } + return (unit != null) ? unit : TimeUnit.DAY; + } + + public static Date parseDateOffset(String... time) + { + Instant instant = Instant.now(); + for (String arg : time) + { + instant = instant.plusSeconds(verify(arg).get() * a(arg)); + } + return Date.from(instant); + } + + public static long parseLongOffset(long unix, String... time) + { + Instant instant = Instant.ofEpochMilli(unix); + for (String arg : time) + { + instant = instant.plusSeconds(verify(arg).get() * a(arg)); + } + return FUtil.getUnixTime(Date.from(instant)); + } + + public static String playerListToNames(Set players) + { + List names = new ArrayList<>(); + for (OfflinePlayer player : players) + { + names.add(player.getName()); + } + return StringUtils.join(names, ", "); + } + + public static String dateToString(Date date) + { + return new SimpleDateFormat(DATE_STORAGE_FORMAT, Locale.ENGLISH).format(date); + } + + public static Date stringToDate(String dateString) + { + try + { + return new SimpleDateFormat(DATE_STORAGE_FORMAT, Locale.ENGLISH).parse(dateString); + } + catch (ParseException pex) + { + return new Date(0L); + } + } + + public static boolean isFromHostConsole(String senderName) + { + return ConfigEntry.HOST_SENDER_NAMES.getList().contains(senderName.toLowerCase()); + } + + public static boolean fuzzyIpMatch(String a, String b, int octets) + { + boolean match = true; + + String[] aParts = a.split("\\."); + String[] bParts = b.split("\\."); + + if (aParts.length != 4 || bParts.length != 4) + { + return false; + } + + if (octets > 4) + { + octets = 4; + } + else if (octets < 1) + { + octets = 1; + } + + for (int i = 0; i < octets; i++) + { + if (aParts[i].equals("*") || bParts[i].equals("*")) + { + continue; + } + + if (!aParts[i].equals(bParts[i])) + { + match = false; + break; + } + } + + return match; + } + + public static String getFuzzyIp(String ip) + { + final String[] ipParts = ip.split("\\."); + if (ipParts.length == 4) + { + return String.format("%s.%s.*.*", ipParts[0], ipParts[1]); + } + + return ip; + } + + //getField: Borrowed from WorldEdit + @SuppressWarnings("unchecked") + public static T getField(Object from, String name) + { + Class checkClass = from.getClass(); + do + { + try + { + Field field = checkClass.getDeclaredField(name); + field.setAccessible(true); + return (T) field.get(from); + + } + catch (NoSuchFieldException | IllegalAccessException ignored) + { + } + } + while (checkClass.getSuperclass() != Object.class + && ((checkClass = checkClass.getSuperclass()) != null)); + + return null; + } + + public static ChatColor randomChatColor() + { + return CHAT_COLOR_POOL.get(RANDOM.nextInt(CHAT_COLOR_POOL.size())); + } + + public static String rainbowify(String string) + { + Iterator CHAT_COLOR_ITERATOR = CHAT_COLOR_POOL.iterator(); + + StringBuilder newString = new StringBuilder(); + char[] chars = string.toCharArray(); + + for (char c : chars) + { + if (!CHAT_COLOR_ITERATOR.hasNext()) + { + CHAT_COLOR_ITERATOR = CHAT_COLOR_POOL.iterator(); //Restart from first colour if there are no more colours in iterator. + } + newString.append(CHAT_COLOR_ITERATOR.next()).append(c); + } + + return newString.toString(); + } + + public static String colorize(String string) + { + if (string != null) + { + Matcher matcher = Pattern.compile("&#[a-f0-9A-F]{6}").matcher(string); + while (matcher.find()) + { + String code = matcher.group().replace("&", ""); + string = string.replace("&" + code, net.md_5.bungee.api.ChatColor.of(code) + ""); + } + + string = ChatColor.translateAlternateColorCodes('&', string); + } + return string; + } + + public static Date getUnixDate(long unix) + { + return new Date(unix); + } + + public static long getUnixTime() + { + return Instant.now().toEpochMilli(); + } + + public static long getUnixTime(Date date) + { + if (date == null) + { + return 0; + } + + return date.getTime(); + } + + public static String getNMSVersion() + { + String packageName = getServer().getClass().getPackage().getName(); + return packageName.substring(packageName.lastIndexOf('.') + 1); + } + + public static int randomInteger(int min, int max) + { + int range = max - min + 1; + return (int) (Math.random() * range) + min; + } + + public static String randomString(int length) + { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789-_=+[]{};:,.<>~"; + StringBuilder randomString = new StringBuilder(); + for (int i = 0; i < length; i++) + { + int selectedCharacter = randomInteger(1, characters.length()) - 1; + + randomString.append(characters.charAt(selectedCharacter)); + } + + return randomString.toString(); + + } + + public static String randomAlphanumericString(int length) + { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789"; + StringBuilder randomString = new StringBuilder(); + for (int i = 0; i < length; i++) + { + int selectedCharacter = randomInteger(1, characters.length()) - 1; + + randomString.append(characters.charAt(selectedCharacter)); + } + + return randomString.toString(); + + } + + public static boolean isPaper() + { + try + { + Class.forName("com.destroystokyo.paper.PaperConfig"); + return true; + } + catch (ClassNotFoundException ignored) + { + return false; + } + } + + public static void fixCommandVoid(Player player) + { + for (Player p : Bukkit.getOnlinePlayers()) + { + for (Entity passengerEntity : p.getPassengers()) + { + if (passengerEntity == player) + { + p.removePassenger(passengerEntity); + } + } + } + } + + public static char getRandomCharacter() + { + return CHARACTER_STRING.charAt(new SplittableRandom().nextInt(CHARACTER_STRING.length())); + } + + public static void give(Player player, Material material, String coloredName, int amount, String... lore) + { + ItemStack stack = new ItemStack(material, amount); + ItemMeta meta = stack.getItemMeta(); + assert meta != null; + meta.setDisplayName(FUtil.colorize(coloredName)); + List loreList = new ArrayList<>(); + for (String entry : lore) + { + loreList.add(FUtil.colorize(entry)); + } + meta.setLore(loreList); + stack.setItemMeta(meta); + player.getInventory().setItem(player.getInventory().firstEmpty(), stack); + } + + public static Player getRandomPlayer() + { + List players = new ArrayList<>(Bukkit.getOnlinePlayers()); + return players.get(randomInteger(0, players.size() - 1)); + } + + // convert the current time + public static int getTimeInTicks(int tz) + { + if (timeZoneOutOfBounds(tz)) + { + return -1; + } + Calendar date = Calendar.getInstance(TimeZone.getTimeZone(TIMEZONE_LOOKUP.get(tz))); + int res = 0; + for (int i = 0; i < date.get(Calendar.HOUR_OF_DAY) - 6; i++) // oh yeah i don't know why this is 6 hours ahead + { + res += 1000; + } + int addExtra = 0; // we're adding extra to account for repeating decimals + for (int i = 0; i < date.get(Calendar.MINUTE); i++) + { + res += 16; + addExtra++; + if (addExtra == 3) + { + res += 1; + addExtra = 0; + } + } + // this is the best it can be. trust me. + return res; + } + + public static boolean timeZoneOutOfBounds(int tz) + { + return tz < -12 || tz > 12; + } + + public static String getIp(Player player) + { + return Objects.requireNonNull(player.getAddress()).getAddress().getHostAddress().trim(); + } + + public static String getIp(PlayerLoginEvent event) + { + return event.getAddress().getHostAddress().trim(); + } + + private static Color interpolateColor(Color c1, Color c2, double factor) + { + long[] c1values = {c1.getRed(), c1.getGreen(), c1.getBlue()}; + long[] c2values = {c2.getRed(), c2.getGreen(), c2.getBlue()}; + for (int i = 0; i < 3; i++) + { + c1values[i] = Math.round(c1values[i] + factor * (c2values[i] - c1values[i])); + } + return Color.fromRGB((int) c1values[0], (int) c1values[1], (int) c1values[2]); + } + + public static boolean isValidIPv4(String ip) + { + return !ip.matches("^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$") + && !ip.matches("^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\\.([*])\\.([*])$"); + } + + public static List createColorGradient(Color c1, Color c2, int steps) + { + double factor = 1.0 / (steps - 1.0); + List colors = new ArrayList<>(); + for (int i = 0; i < steps; i++) + { + colors.add(interpolateColor(c1, c2, factor * i)); + } + return colors; + } + + public static boolean colorClose(Color first, Color second, int tresHold) + { + int redDelta = Math.abs(first.getRed() - second.getRed()); + int greenDelta = Math.abs(first.getGreen() - second.getGreen()); + int blueDelta = Math.abs(first.getBlue() - second.getBlue()); + return (redDelta + greenDelta + blueDelta) < tresHold; + } + + public static Color fromAWT(java.awt.Color color) + { + return Color.fromRGB(color.getRed(), color.getGreen(), color.getBlue()); + } + + public static java.awt.Color toAWT(Color color) + { + return new java.awt.Color(color.getRed(), color.getGreen(), color.getBlue()); + } + + public static java.awt.Color getRandomAWTColor() + { + return new java.awt.Color(randomInteger(0, 255), randomInteger(0, 255), randomInteger(0, 255)); + } + + public static String getHexStringOfAWTColor(java.awt.Color color) + { + String hex = Integer.toHexString(color.getRGB() & 0xFFFFFF); + if (hex.length() < 6) + { + hex = "0" + hex; + } + return "#" + hex; + } + + public static void createExplosionOnDelay(Location location, float power, int delay) + { + new BukkitRunnable() + { + @Override + public void run() + { + Objects.requireNonNull(location.getWorld()).createExplosion(location, power); + } + }.runTaskLater(TotalFreedomMod.getPlugin(), delay); + } + + public static int getFakePlayerCount() + { + int i = TotalFreedomMod.getPlugin().al.vanished.size(); + for (String name : TotalFreedomMod.getPlugin().al.vanished) + { + if (Bukkit.getPlayer(name) == null) + { + i--; + } + } + return getServer().getOnlinePlayers().size() - i; + } + + public static class PaginationList extends ArrayList + { + + private final int epp; + + public PaginationList(int epp) + { + super(); + this.epp = epp; + } + + @SafeVarargs + public PaginationList(int epp, T... elements) + { + super(Arrays.asList(elements)); + this.epp = epp; + } + + public int getPageCount() + { + return (int) Math.ceil((double) size() / (double) epp); + } + + public List getPage(int page) + { + if (page < 1 || page > getPageCount()) + { + return null; + } + int startIndex = (page - 1) * epp; + int endIndex = Math.min(startIndex + (epp - 1), this.size() - 1); + return subList(startIndex, endIndex + 1); + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/Groups.java b/src/main/java/me/totalfreedom/totalfreedommod/util/Groups.java new file mode 100644 index 00000000..b0f66ded --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/Groups.java @@ -0,0 +1,228 @@ +package me.totalfreedom.totalfreedommod.util; + +import java.util.Arrays; +import java.util.List; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.entity.EntityType; + +public class Groups +{ + public static final List WOOL_COLORS = Arrays.asList( + Material.WHITE_WOOL, + Material.RED_WOOL, + Material.ORANGE_WOOL, + Material.YELLOW_WOOL, + Material.GREEN_WOOL, + Material.LIME_WOOL, + Material.LIGHT_BLUE_WOOL, + Material.CYAN_WOOL, + Material.BLUE_WOOL, + Material.PURPLE_WOOL, + Material.MAGENTA_WOOL, + Material.PINK_WOOL, + Material.BROWN_WOOL, + Material.GRAY_WOOL, + Material.LIGHT_GRAY_WOOL, + Material.BLACK_WOOL); + + public static final List SHULKER_BOXES = Arrays.asList( + Material.SHULKER_BOX, + Material.WHITE_SHULKER_BOX, + Material.RED_SHULKER_BOX, + Material.ORANGE_SHULKER_BOX, + Material.YELLOW_SHULKER_BOX, + Material.GREEN_SHULKER_BOX, + Material.LIME_SHULKER_BOX, + Material.LIGHT_BLUE_SHULKER_BOX, + Material.CYAN_SHULKER_BOX, + Material.BLUE_SHULKER_BOX, + Material.PURPLE_SHULKER_BOX, + Material.MAGENTA_SHULKER_BOX, + Material.PINK_SHULKER_BOX, + Material.BROWN_SHULKER_BOX, + Material.GRAY_SHULKER_BOX, + Material.LIGHT_GRAY_SHULKER_BOX, + Material.BLACK_SHULKER_BOX); + + public static final List MOB_TYPES = Arrays.asList( + EntityType.BAT, + EntityType.BEE, + EntityType.BLAZE, + EntityType.CAVE_SPIDER, + EntityType.CHICKEN, + EntityType.CAT, + EntityType.COD, + EntityType.COW, + EntityType.CREEPER, + EntityType.DOLPHIN, + EntityType.DONKEY, + EntityType.DROWNED, + EntityType.ELDER_GUARDIAN, + EntityType.ENDERMAN, + EntityType.ENDERMITE, + EntityType.EVOKER, + EntityType.FOX, + EntityType.GHAST, + EntityType.GUARDIAN, + EntityType.HOGLIN, + EntityType.HORSE, + EntityType.HUSK, + EntityType.ILLUSIONER, + EntityType.IRON_GOLEM, + EntityType.LLAMA, + EntityType.MAGMA_CUBE, + EntityType.MUSHROOM_COW, + EntityType.MULE, + EntityType.OCELOT, + EntityType.PANDA, + EntityType.PARROT, + EntityType.PHANTOM, + EntityType.PIG, + EntityType.PIGLIN, + EntityType.PIGLIN_BRUTE, + EntityType.PILLAGER, + EntityType.POLAR_BEAR, + EntityType.PUFFERFISH, + EntityType.RABBIT, + EntityType.RAVAGER, + EntityType.SALMON, + EntityType.SHEEP, + EntityType.SHULKER, + EntityType.SILVERFISH, + EntityType.SKELETON, + EntityType.SKELETON_HORSE, + EntityType.SLIME, + EntityType.SNOWMAN, + EntityType.SPIDER, + EntityType.SQUID, + EntityType.STRAY, + EntityType.STRIDER, + EntityType.TRADER_LLAMA, + EntityType.TROPICAL_FISH, + EntityType.TURTLE, + EntityType.VEX, + EntityType.VILLAGER, + EntityType.VINDICATOR, + EntityType.WANDERING_TRADER, + EntityType.WITCH, + EntityType.WITHER_SKELETON, + EntityType.WOLF, + EntityType.ZOGLIN, + EntityType.ZOMBIE, + EntityType.ZOMBIE_HORSE, + EntityType.ZOMBIFIED_PIGLIN, + EntityType.ZOMBIE_VILLAGER); + + public static final List SPAWN_EGGS = Arrays.asList( + Material.BAT_SPAWN_EGG, + Material.BEE_SPAWN_EGG, + Material.BLAZE_SPAWN_EGG, + Material.CAVE_SPIDER_SPAWN_EGG, + Material.CHICKEN_SPAWN_EGG, + Material.CAT_SPAWN_EGG, + Material.COD_SPAWN_EGG, + Material.COW_SPAWN_EGG, + Material.CREEPER_SPAWN_EGG, + Material.DOLPHIN_SPAWN_EGG, + Material.DONKEY_SPAWN_EGG, + Material.DROWNED_SPAWN_EGG, + Material.ELDER_GUARDIAN_SPAWN_EGG, + Material.ENDERMAN_SPAWN_EGG, + Material.ENDERMITE_SPAWN_EGG, + Material.EVOKER_SPAWN_EGG, + Material.FOX_SPAWN_EGG, + Material.GHAST_SPAWN_EGG, + Material.GUARDIAN_SPAWN_EGG, + Material.HOGLIN_SPAWN_EGG, + Material.HORSE_SPAWN_EGG, + Material.HUSK_SPAWN_EGG, + Material.LLAMA_SPAWN_EGG, + Material.MAGMA_CUBE_SPAWN_EGG, + Material.MOOSHROOM_SPAWN_EGG, + Material.MULE_SPAWN_EGG, + Material.OCELOT_SPAWN_EGG, + Material.PANDA_SPAWN_EGG, + Material.PARROT_SPAWN_EGG, + Material.PHANTOM_SPAWN_EGG, + Material.PIG_SPAWN_EGG, + Material.PIGLIN_SPAWN_EGG, + Material.PIGLIN_BRUTE_SPAWN_EGG, + Material.PILLAGER_SPAWN_EGG, + Material.POLAR_BEAR_SPAWN_EGG, + Material.PUFFERFISH_SPAWN_EGG, + Material.RABBIT_SPAWN_EGG, + Material.RAVAGER_SPAWN_EGG, + Material.SALMON_SPAWN_EGG, + Material.SHEEP_SPAWN_EGG, + Material.SHULKER_SPAWN_EGG, + Material.SILVERFISH_SPAWN_EGG, + Material.SKELETON_SPAWN_EGG, + Material.SKELETON_HORSE_SPAWN_EGG, + Material.SLIME_SPAWN_EGG, + Material.SPIDER_SPAWN_EGG, + Material.SQUID_SPAWN_EGG, + Material.STRAY_SPAWN_EGG, + Material.STRIDER_SPAWN_EGG, + Material.TRADER_LLAMA_SPAWN_EGG, + Material.TROPICAL_FISH_SPAWN_EGG, + Material.TURTLE_SPAWN_EGG, + Material.VEX_SPAWN_EGG, + Material.VILLAGER_SPAWN_EGG, + Material.VINDICATOR_SPAWN_EGG, + Material.WANDERING_TRADER_SPAWN_EGG, + Material.WITCH_SPAWN_EGG, + Material.WITHER_SKELETON_SPAWN_EGG, + Material.WOLF_SPAWN_EGG, + Material.ZOGLIN_SPAWN_EGG, + Material.ZOMBIE_SPAWN_EGG, + Material.ZOMBIE_HORSE_SPAWN_EGG, + Material.ZOMBIFIED_PIGLIN_SPAWN_EGG, + Material.ZOMBIE_VILLAGER_SPAWN_EGG); + + public static final List BANNERS = Arrays.asList( + Material.BLACK_BANNER, + Material.BLACK_WALL_BANNER, + Material.BLUE_BANNER, + Material.BLUE_WALL_BANNER, + Material.BROWN_BANNER, + Material.BROWN_WALL_BANNER, + Material.CYAN_BANNER, + Material.CYAN_WALL_BANNER, + Material.GRAY_BANNER, + Material.GRAY_WALL_BANNER, + Material.GREEN_BANNER, + Material.GREEN_WALL_BANNER, + Material.LIGHT_BLUE_BANNER, + Material.LIGHT_BLUE_WALL_BANNER, + Material.LIGHT_GRAY_BANNER, + Material.LIGHT_GRAY_WALL_BANNER, + Material.LIME_BANNER, + Material.LIME_WALL_BANNER, + Material.MAGENTA_BANNER, + Material.MAGENTA_WALL_BANNER, + Material.ORANGE_BANNER, + Material.ORANGE_WALL_BANNER, + Material.PINK_BANNER, + Material.PINK_WALL_BANNER, + Material.PURPLE_BANNER, + Material.PURPLE_WALL_BANNER, + Material.RED_BANNER, + Material.RED_WALL_BANNER, + Material.WHITE_BANNER, + Material.WHITE_WALL_BANNER, + Material.YELLOW_BANNER, + Material.YELLOW_WALL_BANNER); + + public static final List EXPLOSIVE_BED_BIOMES = Arrays.asList( + Biome.NETHER_WASTES, + Biome.CRIMSON_FOREST, + Biome.SOUL_SAND_VALLEY, + Biome.WARPED_FOREST, + Biome.BASALT_DELTAS, + Biome.END_BARRENS, + Biome.END_HIGHLANDS, + Biome.END_MIDLANDS, + Biome.THE_END, + Biome.SMALL_END_ISLANDS); +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/History.java b/src/main/java/me/totalfreedom/totalfreedommod/util/History.java new file mode 100644 index 00000000..682a4fbd --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/History.java @@ -0,0 +1,114 @@ +package me.totalfreedom.totalfreedommod.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.UUID; +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +public class History +{ + + public static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public static void reportHistory(final CommandSender sender, final String username) + { + new BukkitRunnable() + { + @Override + public void run() + { + UUID uuid = UUIDFetcher.fetch(username); + if (uuid != null) + { + Gson gson = new GsonBuilder().create(); + String compactUuid = uuid.toString().replace("-", ""); + try + { + //UUIDs or playernames actually work with this one + //TODO: fix the stupid api on how it's not working name histories + //URL url = new URL("https://api.ashcon.app/mojang/v2/user/" + compactUuid); + URL url = new URL("https://api.mojang.com/user/profiles/" + compactUuid + "/names"); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + //conn.setRequestProperty("User-Agent", ""); + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + FName[] oldNames = gson.fromJson(reader, FName[].class); + if (oldNames == null) + { + FSync.playerMsg(sender, ChatColor.RED + "Player not found!"); + return; + } + reader.close(); + conn.disconnect(); + Arrays.sort(oldNames); + printHistory(sender, oldNames); + } + catch (Exception ex) + { + FSync.playerMsg(sender, ChatColor.RED + "Error, see logs for more details."); + FLog.severe(ex); + } + } + else + { + FSync.playerMsg(sender, ChatColor.RED + "Player not found!"); + } + } + }.runTaskAsynchronously(TotalFreedomMod.getPlugin()); + } + + private static void printHistory(CommandSender sender, FName[] oldNames) + { + if (oldNames.length == 1) + { + FSync.playerMsg(sender, ChatColor.GREEN + oldNames[0].getName() + ChatColor.GOLD + " has never changed their name."); + return; + } + FSync.playerMsg(sender, ChatColor.GOLD + "Original name: " + ChatColor.GREEN + oldNames[0].getName()); + for (int i = 1; i < oldNames.length; i++) + { + Date date = new Date(oldNames[i].getChangedToAt()); + String formattedDate = dateFormat.format(date); + FSync.playerMsg(sender, ChatColor.BLUE + formattedDate + ChatColor.GOLD + " changed to " + ChatColor.GREEN + oldNames[i].getName()); + } + } + + private static class FName implements Comparable + { + private final String name; + private final long changedToAt; + + //Added constructor because otherwise there's no way name or changedToAt would have been anything other than null. + public FName(String name, long changedToAt) + { + this.name = name; + this.changedToAt = changedToAt; + } + + @Override + public int compareTo(FName other) + { + return Long.compare(this.changedToAt, other.changedToAt); + } + + public String getName() + { + return name; + } + + public long getChangedToAt() + { + return changedToAt; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/MethodTimer.java b/src/main/java/me/totalfreedom/totalfreedommod/util/MethodTimer.java new file mode 100644 index 00000000..80f24260 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/MethodTimer.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.util; + +public class MethodTimer +{ + + private long lastStart; + private long total = 0; + + public MethodTimer() + { + } + + public void start() + { + this.lastStart = System.currentTimeMillis(); + } + + public void update() + { + this.total += (System.currentTimeMillis() - this.lastStart); + } + + public long getTotal() + { + return this.total; + } + + public void printTotalToLog(String timerName) + { + FLog.info("DEBUG: " + timerName + " used " + this.getTotal() + " ms."); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/Response.java b/src/main/java/me/totalfreedom/totalfreedommod/util/Response.java new file mode 100644 index 00000000..867b3f7f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/Response.java @@ -0,0 +1,32 @@ +package me.totalfreedom.totalfreedommod.util; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +public class Response +{ + private final int code; + private final String message; + + public Response(int code, String message) + { + this.code = code; + this.message = message; + } + + public JSONObject getJSONMessage() throws ParseException + { + return (JSONObject)new JSONParser().parse(message); + } + + public int getCode() + { + return code; + } + + public String getMessage() + { + return message; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/TimeUnit.java b/src/main/java/me/totalfreedom/totalfreedommod/util/TimeUnit.java new file mode 100644 index 00000000..20132d0a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/TimeUnit.java @@ -0,0 +1,22 @@ +package me.totalfreedom.totalfreedommod.util; + +public enum TimeUnit +{ + SECOND(1L), + MINUTE(SECOND.get() * 60L), + HOUR(MINUTE.get() * 60L), + DAY(HOUR.get() * 24L), + WEEK(DAY.get() * 7L), + MONTH(DAY.get() * 30L), + YEAR(MONTH.get() * 12L); + + private final long time; + + TimeUnit(long time) { + this.time = time; + } + + public long get() { + return time; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/util/UUIDFetcher.java b/src/main/java/me/totalfreedom/totalfreedommod/util/UUIDFetcher.java new file mode 100644 index 00000000..d0b4881f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/util/UUIDFetcher.java @@ -0,0 +1,68 @@ +package me.totalfreedom.totalfreedommod.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; + +// UUIDFetcher retrieves UUIDs from usernames via web requests to Mojang. +public class UUIDFetcher +{ + + private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; + + public static UUID fetch(String name) + { + try + { + Gson gson = new GsonBuilder().create(); + UUID uuid; + String body = gson.toJson(name); + URL url = new URL(PROFILE_URL); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + FetchedUuid[] id = gson.fromJson( + new InputStreamReader(connection.getInputStream()), + FetchedUuid[].class); + + if (id.length == 0 || id[0].getID() == null) + { + return null; + } + + String idd = id[0].getID(); + uuid = UUID.fromString(idd.substring(0, 8) + "-" + idd.substring(8, 12) + + "-" + idd.substring(12, 16) + "-" + idd.substring(16, 20) + "-" + + idd.substring(20, 32)); + return uuid; + } + catch (IOException ex) + { + FLog.severe(ex); + } + return null; + } + + private static class FetchedUuid + { + + private String id; + + public String getID() + { + return id; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/AdminWorld.java b/src/main/java/me/totalfreedom/totalfreedommod/world/AdminWorld.java new file mode 100644 index 00000000..2a230017 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/AdminWorld.java @@ -0,0 +1,141 @@ +package me.totalfreedom.totalfreedommod.world; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerMoveEvent; + +public final class AdminWorld extends CustomWorld +{ + + private static final long CACHE_CLEAR_FREQUENCY = 30L * 1000L; //30 seconds, milliseconds + private static final long TP_COOLDOWN_TIME = 500L; //0.5 seconds, milliseconds + private static final String GENERATION_PARAMETERS = ConfigEntry.FLATLANDS_GENERATE_PARAMS.getString(); + // + private final Map teleportCooldown = new HashMap<>(); + // + private WorldWeather weather = WorldWeather.OFF; + private WorldTime time = WorldTime.INHERIT; + + public AdminWorld() + { + super("adminworld"); + } + + @Override + public void sendToWorld(Player player) + { + super.sendToWorld(player); + } + + @SuppressWarnings("deprecation") + @Override + protected World generateWorld() + { + final WorldCreator worldCreator = new WorldCreator(getName()); + worldCreator.generateStructures(false); + worldCreator.type(WorldType.NORMAL); + worldCreator.environment(World.Environment.NORMAL); + worldCreator.generator(new CleanroomChunkGenerator(GENERATION_PARAMETERS)); + + final World world = Bukkit.getServer().createWorld(worldCreator); + + assert world != null; + world.setSpawnFlags(false, false); + world.setSpawnLocation(0, 50, 0); + + final Block welcomeSignBlock = world.getBlockAt(0, 50, 0); + welcomeSignBlock.setType(Material.OAK_SIGN); + org.bukkit.block.Sign welcomeSign = (org.bukkit.block.Sign)welcomeSignBlock.getState(); + + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)welcomeSign.getData(); + signData.setFacingDirection(BlockFace.NORTH); + + welcomeSign.setLine(0, ChatColor.GREEN + "AdminWorld"); + welcomeSign.setLine(1, ChatColor.DARK_GRAY + "---"); + welcomeSign.setLine(2, ChatColor.YELLOW + "Spawn Point"); + welcomeSign.setLine(3, ChatColor.DARK_GRAY + "---"); + welcomeSign.update(); + + plugin.gr.commitGameRules(); + return world; + } + + public boolean validateMovement(PlayerMoveEvent event) + { + World world; + try + { + world = getWorld(); + } + catch (Exception ex) + { + return true; + } + + if (world == null || !Objects.equals(Objects.requireNonNull(event.getTo()).getWorld(), world)) + { + return true; + } + + final Player player = event.getPlayer(); + + Long lastTP = teleportCooldown.get(player); + + long currentTimeMillis = System.currentTimeMillis(); + if (lastTP == null || lastTP + TP_COOLDOWN_TIME <= currentTimeMillis) + { + teleportCooldown.put(player, currentTimeMillis); + FLog.info(player.getName() + " attempted to access the AdminWorld."); + event.setTo(Bukkit.getWorlds().get(0).getSpawnLocation()); + } + return false; + } + + public WorldWeather getWeatherMode() + { + return weather; + } + + public void setWeatherMode(final WorldWeather weatherMode) + { + this.weather = weatherMode; + + try + { + weatherMode.setWorldToWeather(getWorld()); + } + catch (Exception ignored) + { + } + } + + public WorldTime getTimeOfDay() + { + return time; + } + + public void setTimeOfDay(final WorldTime timeOfDay) + { + this.time = timeOfDay; + + try + { + timeOfDay.setWorldToTime(getWorld()); + } + catch (Exception ignored) + { + } + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/CleanroomChunkGenerator.java b/src/main/java/me/totalfreedom/totalfreedommod/world/CleanroomChunkGenerator.java new file mode 100644 index 00000000..8d0b9bd6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/CleanroomChunkGenerator.java @@ -0,0 +1,167 @@ +/* + * Cleanroom Generator + * Copyright (C) 2011-2012 nvx + * + * 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 . + */ + +package me.totalfreedom.totalfreedommod.world; + +import java.util.Arrays; +import java.util.Random; +import java.util.logging.Logger; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.generator.ChunkGenerator; +import org.jetbrains.annotations.NotNull; +import static java.lang.System.arraycopy; + +public class CleanroomChunkGenerator extends ChunkGenerator +{ + private final Logger log = Logger.getLogger("Minecraft"); + + private Material[] materials; + + public CleanroomChunkGenerator() + { + this("16,stone,32,dirt,1,grass_block"); + } + + public CleanroomChunkGenerator(String id) + { + if (id != null) + { + try + { + int y = 0; + + materials = new Material[128]; // Default to 128, will be resized later if required + materials[y++] = Material.BEDROCK; + + if (id.length() > 0) + { + String[] tokens = id.split("[,]"); + + if ((tokens.length % 2) != 0) + { + throw new Exception(); + } + + for (int i = 0; i < tokens.length; i += 2) + { + int height = Integer.parseInt(tokens[i]); + if (height <= 0) + { + log.warning("[CleanroomGenerator] Invalid height '" + tokens[i] + "'. Using 64 instead."); + height = 64; + } + + String[] materialTokens = tokens[i + 1].split("[:]", 2); + + if (materialTokens.length == 2) + { + log.warning("[CleanroomGenerator] Data values are no longer supported in 1.13. Defaulting to the base material for " + materialTokens[0]); + } + + Material mat = Material.matchMaterial(materialTokens[0]); + if (mat == null) + { + log.warning("[CleanroomGenerator] Invalid Block ID '" + materialTokens[0] + "'. Defaulting to stone. (Integer IDs were removed in 1.13)"); + mat = Material.STONE; + } + + if (!mat.isBlock()) + { + log.warning("[CleanroomGenerator] Error, '" + materialTokens[0] + "' is not a block. Defaulting to stone."); + mat = Material.STONE; + } + + if (y + height > materials.length) + { + Material[] newMaterials = new Material[Math.max(y + height, materials.length * 2)]; + + arraycopy(materials, 0, newMaterials, 0, y); + materials = newMaterials; + } + + Arrays.fill(materials, y, y + height, mat); + y += height; + } + } + + // Trim to size + if (materials.length > y) + { + Material[] newMaterials = new Material[y]; + arraycopy(materials, 0, newMaterials, 0, y); + materials = newMaterials; + } + } + catch (Exception e) + { + log.severe("[CleanroomGenerator] Error parsing CleanroomGenerator ID '" + id + "'. using defaults '64,1': " + e.toString()); + e.printStackTrace(); + + materials = new Material[65]; + materials[0] = Material.BEDROCK; + Arrays.fill(materials, 1, 65, Material.STONE); + } + } + else + { + materials = new Material[65]; + materials[0] = Material.BEDROCK; + Arrays.fill(materials, 1, 65, Material.STONE); + } + } + + @Override + public @NotNull ChunkData generateChunkData(World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome) + { + int maxHeight = world.getMaxHeight(); + if (materials.length > maxHeight) + { + log.warning("[CleanroomGenerator] Error, chunk height " + materials.length + " is greater than the world max height (" + maxHeight + "). Trimming to world max height."); + Material[] newMaterials = new Material[maxHeight]; + arraycopy(materials, 0, newMaterials, 0, maxHeight); + materials = newMaterials; + } + + ChunkData result = createChunkData(world); + + for (int y = 0; y < materials.length; y++) + { + result.setRegion(0, y, 0, 16, y + 1, 16, materials[y]); + } + + return result; + } + + @Override + public Location getFixedSpawnLocation(World world, @NotNull Random random) + { + if (!world.isChunkLoaded(0, 0)) + { + world.loadChunk(0, 0); + } + + if ((world.getHighestBlockYAt(0, 0) <= 0) && (world.getBlockAt(0, 0, 0).getType() == Material.AIR)) // SPACE! + { + return new Location(world, 0, 64, 0); // Lets allow people to drop a little before hitting the void then shall we? + } + + return new Location(world, 0, world.getHighestBlockYAt(0, 0), 0); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/CustomWorld.java b/src/main/java/me/totalfreedom/totalfreedommod/world/CustomWorld.java new file mode 100644 index 00000000..9ef3392a --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/CustomWorld.java @@ -0,0 +1,64 @@ +package me.totalfreedom.totalfreedommod.world; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; + +public abstract class CustomWorld extends FreedomService +{ + private final String name; + // + private World world; + + public CustomWorld(String name) + { + this.name = name; + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public final World getWorld() + { + if (world == null || !Bukkit.getWorlds().contains(world)) + { + world = generateWorld(); + } + + if (world == null) + { + FLog.warning("Could not load world: " + name); + } + + return world; + } + + public void sendToWorld(Player player) + { + try + { + PaperLib.teleportAsync(player, getWorld().getSpawnLocation()); + } + catch (Exception ex) + { + player.sendMessage(ex.getMessage()); + } + } + + protected abstract World generateWorld(); + + public String getName() + { + return name; + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/Flatlands.java b/src/main/java/me/totalfreedom/totalfreedommod/world/Flatlands.java new file mode 100644 index 00000000..32033da5 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/Flatlands.java @@ -0,0 +1,94 @@ +package me.totalfreedom.totalfreedommod.world; + +import java.io.File; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import org.apache.commons.io.FileUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +public class Flatlands extends CustomWorld +{ + + private static final String GENERATION_PARAMETERS = ConfigEntry.FLATLANDS_GENERATE_PARAMS.getString(); + + public Flatlands() + { + super("flatlands"); + } + + @SuppressWarnings("deprecation") + @Override + protected World generateWorld() + { + if (!ConfigEntry.FLATLANDS_GENERATE.getBoolean()) + { + return null; + } + + wipeFlatlandsIfFlagged(); + + final WorldCreator worldCreator = new WorldCreator(getName()); + worldCreator.generateStructures(false); + worldCreator.type(WorldType.NORMAL); + worldCreator.environment(World.Environment.NORMAL); + worldCreator.generator(new CleanroomChunkGenerator(GENERATION_PARAMETERS)); + + final World world = Bukkit.getServer().createWorld(worldCreator); + + assert world != null; + world.setSpawnFlags(false, false); + world.setSpawnLocation(0, 50, 0); + + final Block welcomeSignBlock = world.getBlockAt(0, 50, 0); + welcomeSignBlock.setType(Material.OAK_SIGN); + org.bukkit.block.Sign welcomeSign = (org.bukkit.block.Sign)welcomeSignBlock.getState(); + + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)welcomeSign.getData(); + signData.setFacingDirection(BlockFace.NORTH); + + welcomeSign.setLine(0, ChatColor.GREEN + "Flatlands"); + welcomeSign.setLine(1, ChatColor.DARK_GRAY + "---"); + welcomeSign.setLine(2, ChatColor.YELLOW + "Spawn Point"); + welcomeSign.setLine(3, ChatColor.DARK_GRAY + "---"); + welcomeSign.update(); + + plugin.gr.commitGameRules(); + + return world; + } + + + public void wipeFlatlandsIfFlagged() + { + boolean doFlatlandsWipe = false; + try + { + doFlatlandsWipe = plugin.sf.getSavedFlag("do_wipe_flatlands"); + } + catch (Exception ignored) + { + } + + if (doFlatlandsWipe) + { + if (Bukkit.getServer().getWorld("flatlands") == null) + { + FLog.info("Wiping flatlands."); + plugin.sf.setSavedFlag("do_wipe_flatlands", false); + FileUtils.deleteQuietly(new File("./flatlands")); + } + else + { + FLog.severe("Can't wipe flatlands, it is already loaded."); + } + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/MasterBuilderWorld.java b/src/main/java/me/totalfreedom/totalfreedommod/world/MasterBuilderWorld.java new file mode 100644 index 00000000..2d35349f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/MasterBuilderWorld.java @@ -0,0 +1,102 @@ +package me.totalfreedom.totalfreedommod.world; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; + +public final class MasterBuilderWorld extends CustomWorld +{ + + private static final String GENERATION_PARAMETERS = ConfigEntry.FLATLANDS_GENERATE_PARAMS.getString(); + // + private WorldWeather weather = WorldWeather.OFF; + private WorldTime time = WorldTime.INHERIT; + + public MasterBuilderWorld() + { + super("masterbuilderworld"); + } + + @Override + public void sendToWorld(Player player) + { + super.sendToWorld(player); + } + + // TODO: Replace org.bukkit.Sign with a non deprecated version. This may require extra boilerplate. + @SuppressWarnings("deprecation") + @Override + protected World generateWorld() + { + final WorldCreator worldCreator = new WorldCreator(getName()); + worldCreator.generateStructures(false); + worldCreator.type(WorldType.NORMAL); + worldCreator.environment(World.Environment.NORMAL); + worldCreator.generator(new CleanroomChunkGenerator(GENERATION_PARAMETERS)); + + final World world = server.createWorld(worldCreator); + + assert world != null; + world.setSpawnFlags(false, false); + world.setSpawnLocation(0, 50, 0); + + final Block welcomeSignBlock = world.getBlockAt(0, 50, 0); + welcomeSignBlock.setType(Material.OAK_SIGN); + org.bukkit.block.Sign welcomeSign = (org.bukkit.block.Sign)welcomeSignBlock.getState(); + + org.bukkit.material.Sign signData = (org.bukkit.material.Sign)welcomeSign.getData(); + signData.setFacingDirection(BlockFace.NORTH); + + welcomeSign.setLine(0, ChatColor.GREEN + "MB World"); + welcomeSign.setLine(1, ChatColor.DARK_GRAY + "---"); + welcomeSign.setLine(2, ChatColor.YELLOW + "Spawn Point"); + welcomeSign.setLine(3, ChatColor.DARK_GRAY + "---"); + welcomeSign.update(); + + plugin.gr.commitGameRules(); + return world; + } + + public WorldWeather getWeatherMode() + { + return weather; + } + + public void setWeatherMode(final WorldWeather weatherMode) + { + this.weather = weatherMode; + + try + { + weatherMode.setWorldToWeather(getWorld()); + } + catch (Exception ignored) + { + } + } + + public WorldTime getTimeOfDay() + { + return time; + } + + public void setTimeOfDay(final WorldTime timeOfDay) + { + this.time = timeOfDay; + + try + { + timeOfDay.setWorldToTime(getWorld()); + } + catch (Exception ignored) + { + } + } + +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/WorldManager.java b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldManager.java new file mode 100644 index 00000000..579da910 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldManager.java @@ -0,0 +1,135 @@ +package me.totalfreedom.totalfreedommod.world; + +import io.papermc.lib.PaperLib; +import me.totalfreedom.totalfreedommod.FreedomService; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +import static me.totalfreedom.totalfreedommod.util.FUtil.playerMsg; + +public class WorldManager extends FreedomService +{ + + public Flatlands flatlands; + public AdminWorld adminworld; + public MasterBuilderWorld masterBuilderWorld; + + public WorldManager() + { + this.flatlands = new Flatlands(); + this.adminworld = new AdminWorld(); + this.masterBuilderWorld = new MasterBuilderWorld(); + } + + @Override + public void onStart() + { + flatlands.getWorld(); + adminworld.getWorld(); + masterBuilderWorld.getWorld(); + + // Disable weather + if (ConfigEntry.DISABLE_WEATHER.getBoolean()) + { + for (World world : server.getWorlds()) + { + world.setThundering(false); + world.setStorm(false); + world.setThunderDuration(0); + world.setWeatherDuration(0); + } + } + } + + @Override + public void onStop() + { + flatlands.getWorld().save(); + adminworld.getWorld().save(); + masterBuilderWorld.getWorld().save(); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onThunderChange(ThunderChangeEvent event) + { + try + { + if (event.getWorld().equals(adminworld.getWorld()) && adminworld.getWeatherMode() != WorldWeather.OFF) + { + return; + } + else if (event.getWorld().equals(masterBuilderWorld.getWorld()) && masterBuilderWorld.getWeatherMode() != WorldWeather.OFF) + { + return; + } + } + catch (Exception ignored) + { + } + + if (ConfigEntry.DISABLE_WEATHER.getBoolean() && event.toThunderState()) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onWeatherChange(WeatherChangeEvent event) + { + try + { + if (event.getWorld().equals(adminworld.getWorld()) && adminworld.getWeatherMode() != WorldWeather.OFF) + { + return; + } + else if (event.getWorld().equals(masterBuilderWorld.getWorld()) && masterBuilderWorld.getWeatherMode() != WorldWeather.OFF) + { + return; + } + } + catch (Exception ignored) + { + } + + if (ConfigEntry.DISABLE_WEATHER.getBoolean() && event.toWeatherState()) + { + event.setCancelled(true); + } + } + + public void gotoWorld(Player player, String targetWorld) + { + if (player == null) + { + return; + } + + FUtil.fixCommandVoid(player); + + if (player.getWorld().getName().equalsIgnoreCase(targetWorld)) + { + playerMsg(player, "Going to main world.", ChatColor.GRAY); + PaperLib.teleportAsync(player, Bukkit.getWorlds().get(0).getSpawnLocation()); + return; + } + + for (World world : Bukkit.getWorlds()) + { + if (world.getName().equalsIgnoreCase(targetWorld)) + { + playerMsg(player, "Going to world: " + targetWorld, ChatColor.GRAY); + PaperLib.teleportAsync(player, world.getSpawnLocation()); + return; + } + } + + playerMsg(player, "World " + targetWorld + " not found.", ChatColor.GRAY); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/WorldRestrictions.java b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldRestrictions.java new file mode 100644 index 00000000..4aef7377 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldRestrictions.java @@ -0,0 +1,174 @@ +package me.totalfreedom.totalfreedommod.world; + +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.totalfreedom.totalfreedommod.FreedomService; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class WorldRestrictions extends FreedomService +{ + + private final List BLOCKED_WORLDEDIT_COMMANDS = Arrays.asList( + "green", "fixlava", "fixwater", "br", "brush", "tool", "mat", "range", "cs", "up", "fill", "setblock", "tree", "replacenear", "bigtree"); + + private final List BLOCKED_ESSENTIALS_COMMANDS = Arrays.asList( + "bigtree", "ebigtree", "largetree", "elargetree"); + + private final Map, Object> flags = new HashMap, Object>() + {{ + put(Flags.PLACE_VEHICLE, StateFlag.State.DENY); + put(Flags.DESTROY_VEHICLE, StateFlag.State.DENY); + put(Flags.ENTITY_ITEM_FRAME_DESTROY, StateFlag.State.DENY); + put(Flags.ENTITY_PAINTING_DESTROY, StateFlag.State.DENY); + }}; + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + public boolean doRestrict(Player player) + { + if (!plugin.pl.getData(player).isMasterBuilder() && plugin.pl.canManageMasterBuilders(player.getName())) + { + if (player.getWorld().equals(plugin.wm.masterBuilderWorld.getWorld())) + { + return true; + } + } + + return !plugin.al.isAdmin(player) && player.getWorld().equals(plugin.wm.adminworld.getWorld()); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onBlockPlace(BlockPlaceEvent event) + { + final Player player = event.getPlayer(); + + if (doRestrict(player)) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) + { + final Player player = event.getPlayer(); + + if (doRestrict(player)) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerInteract(PlayerInteractEvent event) + { + final Player player = event.getPlayer(); + + if (doRestrict(player)) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onArmorStandManipulate(PlayerArmorStandManipulateEvent event) + { + final Player player = event.getPlayer(); + + if (doRestrict(player)) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) + { + if (event.getDamager() instanceof Player) + { + Player player = (Player)event.getDamager(); + + if (doRestrict(player)) + { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onCommandPreprocess(PlayerCommandPreprocessEvent event) + { + final Player player = event.getPlayer(); + String command = event.getMessage().split("\\s+")[0].substring(1).toLowerCase(); + + if (doRestrict(player)) + { + /* This is a very poor way of blocking WorldEdit commands, all the methods I know of + for obtaining a list of a plugin's commands are returning null for world edit. */ + String allowed = player.getWorld().equals(plugin.wm.adminworld.getWorld()) ? "Admins" : "Master Builders"; + + if (command.startsWith("/") || BLOCKED_WORLDEDIT_COMMANDS.contains(command)) + { + player.sendMessage(ChatColor.RED + "Only " + allowed + " are allowed to use WorldEdit here."); + event.setCancelled(true); + } + + if (command.equalsIgnoreCase("coreprotect") || command.equalsIgnoreCase("core") || command.equalsIgnoreCase("co")) + { + player.sendMessage(ChatColor.RED + "Only " + allowed + " are allowed to use CoreProtect here."); + event.setCancelled(true); + } + } + + if (player.getWorld().equals(Bukkit.getWorld("plotworld"))) + { + if (BLOCKED_ESSENTIALS_COMMANDS.contains(command)) + { + player.sendMessage(ChatColor.RED + "Sorry, this command is restricted in the plotworld"); + event.setCancelled(true); + } + } + } + + public void protectWorld(World world) + { + if (!plugin.wgb.isEnabled()) + { + return; + } + + RegionManager regionManager = plugin.wgb.getRegionManager(world); + + GlobalProtectedRegion region = new GlobalProtectedRegion("__global__"); + + region.setFlags(flags); + + regionManager.addRegion(region); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/WorldTime.java b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldTime.java new file mode 100644 index 00000000..0b5fd5e7 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldTime.java @@ -0,0 +1,56 @@ +package me.totalfreedom.totalfreedommod.world; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.bukkit.World; + +public enum WorldTime +{ + + INHERIT(), + SUNRISE("sunrise,morning", 0), + NOON("noon,midday,day", 6000), + SUNSET("sunset,evening", 12000), + MIDNIGHT("midnight,night", 18000); + // + private final int timeTicks; + private final List aliases; + + WorldTime() + { + this.timeTicks = 0; + this.aliases = null; + } + + WorldTime(String aliases, int timeTicks) + { + this.timeTicks = timeTicks; + this.aliases = Arrays.asList(StringUtils.split(aliases, ",")); + } + + public static WorldTime getByAlias(String needle) + { + needle = needle.toLowerCase(); + for (WorldTime time : values()) + { + if (time.aliases != null && time.aliases.contains(needle)) + { + return time; + } + } + return null; + } + + public int getTimeTicks() + { + return timeTicks; + } + + public void setWorldToTime(World world) + { + long time = world.getTime(); + time -= time % 24000; + world.setTime(time + 24000 + getTimeTicks()); + } +} \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/world/WorldWeather.java b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldWeather.java new file mode 100644 index 00000000..ef5cf5cb --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/world/WorldWeather.java @@ -0,0 +1,43 @@ +package me.totalfreedom.totalfreedommod.world; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.bukkit.World; + +public enum WorldWeather +{ + + OFF("off"), + RAIN("rain"), + STORM("storm,thunderstorm"); + // + private final List aliases; + + WorldWeather(String aliases) + { + this.aliases = Arrays.asList(StringUtils.split(aliases, ",")); + } + + public static WorldWeather getByAlias(String needle) + { + needle = needle.toLowerCase(); + for (WorldWeather mode : values()) + { + if (mode.aliases.contains(needle)) + { + return mode; + } + } + return null; + } + + public void setWorldToWeather(World world) + { + world.setStorm(this == RAIN || this == STORM); + world.setWeatherDuration(this == RAIN || this == STORM ? 20 * 60 * 5 : 0); + + world.setThundering(this == STORM); + world.setThunderDuration(this == STORM ? 20 * 60 * 5 : 0); + } +} diff --git a/src/main/resources/activitylog.yml b/src/main/resources/activitylog.yml new file mode 100644 index 00000000..0e06fc49 --- /dev/null +++ b/src/main/resources/activitylog.yml @@ -0,0 +1,3 @@ +# +# TotalFreedomMod Activity Log +# diff --git a/src/main/resources/backup/backup.yml b/src/main/resources/backup/backup.yml new file mode 100644 index 00000000..e69de29b diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 00000000..3780220d --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,774 @@ +# +# TotalFreedomMod Configuration +# + +# Generic server information +server: + # You should change this to your server's name + name: TotalFreedom + + # The address your server is located at. Please do not include the port. + address: play.totalfreedom.me + + # Shown at the server list - Please note that support for %mcversion% has been withdrawn + motd: TotalFreedom &8- The Original All-Op Server! + + # Every word in the MOTD will be a different color unless prefixed by a color code + colorful_motd: true + + # MOTD Modification + motds: + ban: '&cYou are banned.' + adminmode: '&cServer is closed.' + lockdown: '&cServer is in lock-down.' + whitelist: '&cWhitelist enabled.' + full: '&cServer is full.' + + # Shown on join as a title + login_title: + title: '&7Welcome to &6TotalFreedom&7!' + subtitle: '&eThe Original All-Op Server!' + + # All players who show up as owner + owners: + - Wild1145 + + # All players who show up as executive + executives: [ ] + + # All players who show up as assistant executive + assistant_executives: [] + + # All those who can manage the master builder list + master_builder_management: [ ] + + # URL players should appeal for bans at + ban_url: '' + + # URL players should appeal for permanent bans at + indefban_url: '' + + # What to display at the top of the tab list + tablist_header: '' + + # What to display at the bottom of the tab list + tablist_footer: '' + +# Discord +discord: + # Do you want to enable the Discord verification system? (Disabled by default) + verification: false + # If you do not have a token, make a bot account and get one at https://discordapp.com/developers/applications/me + token: '' + # The official discord server's ID for this server + server_id: '' + # Channel to send /report messages to + report_channel_id: '' + # Channel for Discord to Minecraft and vice-versa + chat_channel_id: '' + # Channel for Discord to AdminChat and vice-versa + adminchat_channel_id: '' + # Do you want roles to sync with the server? + role_sync: false + # Master Builder role ID + master_builder_role_id: '' + # Admin role ID + admin_role_id: '' + # Senior Admin role ID + senior_admin_role_id: '' + # Developer role ID + developer_role_id: '' + # Assistant Executive role ID + assistant_executive_role_id: '' + # Executive Admin role ID + executive_role_id: '' + # Owner role ID + server_owner_role_id: '' + +# Pterodactyl +ptero: + # URL - do not leave a trailing forward slash + url: '' + # The default email domain used to set email addresses for new users - do not include the @ + default_email_domain: 'example.com' + # Server UUID + server_uuid: '' + # Admin panel API key + admin_key: '' + # Server API key + server_key: '' + +# The shop +shop: + # Enable the shop + enabled: true + + # Shop title + title: '&e&lTotalFreedom Shop' + + # Shop prefix + prefix: '&8[&eTF Shop&8]' + + # How many coins to voters get per vote? + coins_per_vote: 20 + + # Chat reactions + reactions: + + # Enable chat reactions? + enabled: true + + # How much time (in seconds) before a new reaction prompt is made + interval: 300 + + # How much time do players have to react + time: 30.0 + + # How many coins do winners get? + coins_per_win: 5 + + # How long is the random string (in characters)? + string_length: 10 + + # Login messages. Use %name% for usernames, and %rank% for ranks/titles + login_messages: + - '%name% is a terrible %coloredrank%' + - 'that %name% dude is a shitty %coloredrank%' + - 'Hi, my name is %name% and I''m %art% %coloredrank%&b that wasted coins on this login message.' + - '&aDo not fear, %coloredrank% &b%name% &ais here!' + - '%name% is %art% %coloredrank%&b, say hi to them before they gets banned for doing something stupid' + - 'Oh. It''s &l%name%&r&b the %coloredrank%&b again. Great.' + - 'Make way for %name% the %coloredrank%&b!' + - 'It''s a bird! It''s a plane! No, it''s %art% %rank%! It''s %name%' + - '%rank% %name%' + - '%coloredrank%&b %name%' + - 'A wild %name%, %art% %coloredrank%&b, appeared!' + - 'All in all, the %rank% %name% is just another brick in the wall' + - '&a%name% &bis a a geeky &6%rank%' + - '%name% is the %rank% who just exists' + - '%name% based cringe poggers based based %rank%' + - '%name% thinks that &dWindows Server 2019 &bis the best server OS' + - '%name% is %art% %coloredrank% and a &4&lDark Side worshipper.' + + # Item prices + prices: + grappling_hook: 100 + lightning_rod: 1000 + fire_ball: 500 + rideable_pearl: 700 + stacking_potato: 300 + clown_fish: 1500 + login_messages: 5000 + +# Admin list +adminlist: + + # The amount of hours after which admins may be removed from the admin list + clean_threshold_hours: 167 + + # Give the default CONSOLE admin privileges. + # Handy in development environments. + console_is_admin: true + +# CoreProtect +coreprotect: + enabled: false + host: 'localhost' + port: '3306' + username: '' + password: '' + database: 'coreprotect' + +# ForceIP configuration +forceip: + enabled: false + + # The port players should use to connect to your server; only applies to forceip + port: 25565 + + # The kick message sent to players when logging in with the wrong hostname + kickmsg: You have been kicked from the server - Please connect using %address% + +# TotalFreedom Social Media Links, casing will be preserved +social_links: + Forum: 'https://forum.totalfreedom.me' + Website: 'https://totalfreedom.me' + Discord: 'https://discord.com/invite/PW4savJR9a' +# Reddit: 'https://reddit.com/r/TotalFreedom' + Twitter: 'https://twitter.com/TotalFreedomMC' + Facebook: 'https://facebook.com/TotalFreedomMC' +# Tumblr: 'https://totalfreedom.tumblr.com' +# Instagram: 'https://www.instagram.com/totalfreedommc/' + +# Blocking certain events +allow: + fire_place: true + fire_spread: false + fluid_spread: false + lava_place: true + lava_damage: false + water_place: true + tnt_minecarts: false + explosions: false + redstone: true + fireworks: true + frostwalker: false + unsafe_enchantments: true + bells: true + armorstands: true + minecarts: true + clearonjoin: false + tpronjoin: false + structureblocks: false + jigsaws: false + grindstones: true + jukeboxes: true + spawners: false + beehives: false + respawnanchors: false + auto_tp: false + auto_clear: false + gravity: false + +blocked_commands: + # + # How globally blocked commands work: + # All sections described below are delimited by colon characters. + # Make sure that you block a command by its main command name, not an alias + # as all aliases are blocked by default. Commands are case-insensitive. + # + # * The first section is a letter which indicates which rank may use this command + # Valid ranks: + # n - Nobody (Completely disabled) + # a - Admins (Console) + # s - Senior Admins (Console) + # o - Ops (Non-Ops won't be able to use it) + # + # * The second section is a letter which indicates what to do when a player executes that command. + # Valid actions: + # b - Block the command + # a - Block the command and auto-eject that player (for ops and below) + # u - Block the command and Return an "Unknown command" message (Used to hide commands) + # + # * The third section is the command to be blocked, prefixed with a slash + # + # * The fourth section is the message to send to the player when executing that command. + # This should be omitted if unwanted. ChatColors are supported with the &-key. By default + # the starting ChatColor is set to gray. You can use the default 'That command is blocked.' message + # by using a single underscore. + # + # Examples: + # - 'n:b:/mail sendall:&4You can't send mails to everyone!' + # - 's:a:/stop' + # - 'n:b:/ban:_' + # + global: + # CoreProtect Blocked and shows as an unknown command... + - 'a:u:/co:_' + - 'n:u:/co purge:_' + - 'a:u:/coreprotect:_' + - 'n:u:/coreprotect purge:_' + - 'a:u:/core:_' + - 'n:u:/core purge:_' + + # Disabled commands + - 'n:b:/essentials:_' + - 'n:b:/time:Server-side time changing is disabled. Please use /ptime to set your own personal time.' + - 'n:b:/disguiseradius:_' + - 'n:b:/undisguiseradius:_' + - 'n:b:/debug:_' + - 'n:b:/pardon:_' + - 'n:b:/ban-ip:_' + - 'n:b:/pardon-ip:_' + - 'n:b:/toggledownfall:_' + - 'n:b:/spreadplayers:_' + - 'n:b:/blockdata:_' + - 'n:b:/clearhistory:_' + - 'n:b:/save-on:_' + - 'n:b:/save-off:_' + - 'n:b:/packet:_' + - 'n:b://eval:_' + - 'n:b:/testvote:_' + - 'n:b:/nvreload:_' + - 'n:b:/bungeeguard:_' + - 'n:b:/buycraft:_' + - 'n:b:/spigot reload:_' + - 'n:b:/mail sendall:Sending mail to all players is not allowed.' + - 'n:b:/entitydata:_' + - 'n:b:/skins:_' + - 'n:b:/advancement:_' + - 'n:b:/worldborder:The worldborder does not need to be changed. This command is disabled.' + - 'n:b:/defaultgamemode:The default gamemode should not be changed.' + - 'n:b:/thread:_' + - 'n:b:/stacktrace:_' + - 'n:b:/function:_' + - 'n:b:/forceload:_' + - 'n:b:/fill:_' + - 'n:b:/paper:_' + - 'n:b:/locate:_' + - 'n:b:/vvbukkit:_' + - 'n:b:/plot area c:_' + - 'n:b:/d minecart_command:_' + - 'n:b:/loot:_' + - 'n:b:/plan:_' + - 'n:b://material:_' + - 'n:b://mat:_' + - 'n:b:/mat:_' + - 'n:b:/vpnguard:_' + - 'n:b:/pcapi:_' + - 'n:b://distr:&cNope.' + - 'n:b:/data:_' + - 'n:b:/datapack:_' + - 'n:b://regen:_' + - 'n:b:/execute:_' + - 'n:b:/killall:_' + - 'n:b:/createdisguise:_' + - 'n:b:/attribute:_' + + # Admin commands + - 'a:b:/vive:_' + - 'a:b:/vse:_' + - 'a:b:/lightning:_' + - 'a:b:/playsound:_' + - 'a:b:/locatebiome:_' + - 'a:b:/delchunks:_' + - 'a:b:/exploitfixer:_' + - 'a:b:/massivelag:_' + - 'a:b:/ml:_' + - 'a:b:/createkit:_' + - 'a:b:/delkit:_' + - 'a:b:/region:_' + - 'a:b:/clone:_' + - 'a:b:/cap:_' + - 'a:b:/gamemode:Use /gmc and /gms to set your gamemode.' + - 'a:b:/powernbt:_' + - 'a:b:/nbt.:_' + - 'a:b:/blockshub:_' + - 'a:b:/protocol:_' + - 'a:b:/libsdisguises reload:_' + - 'a:b://awe:_' + - 'a:b:/stopfire:_' + - 'a:b:/allowfire' + - 'a:b:/stoplag:_' + - 'a:b:/worldguard:_' + - 'a:b:/slay:_' + - 'a:b:/save-all:_' + - 'a:b:/libsdisguises:_' + - 'a:b:/particle:Due to security reasons, the use of /particle has been disabled.' + - 'a:b:/rainbowclear:_' + - 'a:b:/rainbowspeed:_' + - 'a:b:/kick:_' + - 'a:b:/difficulty:_' + - 'a:b:/captchafy:_' + - 'a:b:/socialspy:_' + - 'a:b:/sproxy:_' + - 'a:b:/replaceitem:_' + - 'a:b:/kill:_' + - 'a:b:/reaction:_' + - 'a:b:/setworldspawn:_' + - 'a:b:/scoreboard:_' + - 'a:b:/setspawn:_' + - 'a:b:/forestgen:_' + - 'a:b:/setidletimeout:_' + - 'a:b:/lrbuild:_' + - 'a:b:/size:_' + - 'a:b:/break:_' + - 'a:b:/disentity:_' + - 'a:b:/reload:_' + - 'a:b:/title:_' + - 'a:b:/weather:_' + - 'a:b:/tpall:_' + - 'a:b:/etpall:_' + - 'a:b:/setblock:_' + - 'a:b:/gamerule:_' + - 'a:b:/togglejail:_' + - 'a:b:/range:_' + - 'a:b:/stop:_' + - 'a:b:/restart:_' + - 'a:b:/sr:_' + - 'a:b:/tpo:_' + - 'a:b:/tpohere:_' + - 'a:b:/tphere:_' + - 'a:b:/rfchairs:_' + - 'a:b:/ppo:_' + - 'a:b:/vulnerabilitypatcher:_' + - 'a:b:/crackshot config:_' + - 'a:b:/marry reload:_' + - 'a:b:/bh:_' + - 'a:b:/tpaall:_' + - 'a:b:/paper heap:_' + - 'a:u:/burn:_' + - 'a:b:/summon:_' + - 'a:b:/discord:_' + - 'a:b:/bossbar:_' + - 'a:b:/team:_' + - 'a:b:/spawner:_' + - 's:b:/istack:_' + - 'a:b:/getloc:_' + - 'a:b:/holo:_' + - 'a:b:/hd:_' + - 'a:b:/hologram:_' + - 'a:b:/holograms:_' + - 's:b:/awe toggle:_' + - 's:b:/tellraw:_' + - 's:b://awe toggle:_' + - 'a:b:/openinv:_' + - 'a:b:/oi:_' + - 'a:b:/open:_' + - 'a:b:/openender:_' + - 'a:b:/oe:_' + - 'a:b:/searchinv:_' + - 'a:b:/si:_' + - 'a:b:/searchender:_' + - 'a:b:/se:_' + - 'a:b:/searchenchant:_' + - 'a:b:/searchenchants:_' + - 'a:b:/anycontainer:_' + - 'a:b:/anychest:_' + - 'a:b:/sc:_' + - 'a:b:/fawe:_' + - 'a:b:/wea:_' + - 'a:b:/protocolsupport:_' + - 'a:b:/ps:_' + - 'n:b:/wra:_' + - 's:b:/viaver:_' + - 's:b:/viaversion:_' + - 's:b:/spark:_' + - 's:b://g:_' + + # Commands that cannot be used by muted players + muted: + - say + - me + - msg + - tell + - reply + - mail + + # Commands that cannot be used with /wildcard + wildcard: + - wildcard + - gtfo + - doom + - slconfig + - smite + +# Words that cannot be used in tags +forbidden_words: + - admin + - owner + - moderator + - developer + - console + - dev + - staff + - mod + - sra + - tca + - sta + - sa + +# Automatically wipe dropped objects +auto_wipe: true + +# Nuking prevention +nukemonitor: + enabled: true + count_break: 100 + count_place: 40 + range: 10.0 + +freecam_trigger_count: 10 +explosive_radius: 4.0 + +# Show all attempted commands in the log, will result in duplicate log messages +preprocess_log: true + +# Disable certain events +disable: + night: true + weather: true + + +# Enable misc. features +landmines_enabled: false +mp44_enabled: false +tossmob_enabled: false +4chan_enabled: false + +# Moblimiter +moblimiter: + enabled: true + max: 50 + disable: + dragon: true + ghast: true + slime: true + giant: true + +# Flatlands +flatlands: + generate: true + + # Flatlands generation parameters - Uses CleanroomGenerator syntax - http://dev.bukkit.org/server-mods/cleanroomgenerator/ + generate_params: 16,stone,32,dirt,1,grass_block + +# Admin-Only Mode +admin_only_mode: false + +# Toggle Chat +toggle_chat: true + +# Host Sender Names - Names that indicate automated services or host-based consoles you want to block from using some commands. +# Make sure these are all lower-case. +host_sender_names: + - rcon + - console + +# Announcer +announcer: + enabled: true + + # Interval between announcements, in seconds. + interval: 600 + + # Prefix for all announcements + prefix: '&5[&eTotalFreedom&5] &b' + + announcements: + - 'Be sure to visit our forums at &6https://forum.totalfreedom.me' + - 'You can always review the server rules here: &6https://forum.totalfreedom.me/d/7-server-conduct-policy' + - 'If you are not OP, be sure to ask!' + - 'Want to join our SMP server? You can using &6/smp-01' + - 'Somebody breaking the rules? Report it! &6/report ' + - 'Griefing is not allowed!' + - 'Hacked clients are allowed!' + - 'Interested in becoming admin? Do &6/admininfo &b for more information!' + - 'You may view all online admins via &6/list -a' + - 'Save your buildings via WorldEdit! &6https://totalfreedom.me&b for more information!' + - 'Wild1145 / Ryan is the owner of TotalFreedom.' + - 'Server lagging? Check the lag via &6/tps' + - 'Player vs player while in creative or god mode is forbidden!' + - 'Spawn killing is forbidden!' + - 'Invisible potions are allowed!' + - 'Serial griefing and trolling will result in a permanent ban!' + - 'All forms of discrimination are strictly forbidden!' + - 'Join our Discord server! https://discord.com/invite/PW4savJR9a' + - 'Remember to save your hotbar, otherwise you may lose your items!' + - 'Link your discord account with &6/linkdiscord for extra features!' + # - 'Check out our reddit at &6https://www.reddit.com/r/TotalFreedom/ &byou can even link your account!' + - 'Want to see cool builds? Check out our Master Builder World with &6/mbworld' + - 'Be sure to follow our twitter for all the latest updates. &6https://twitter.com/totalfreedommc' + +# What to display in the admininfo command. +admininfo: + - '&bHow to apply for an admin position on the TotalFreedom Server:' + - ' &6- Do not ask for admin in game' + - ' &2- Be helpful within the server' + - ' &6- Report those breaking the rules' + - ' &2- And apply on our forums at the link:' + - '&9https://forum.totalfreedom.me/d/45-admin-application-template' + +# What to display in the vote command. +votinginfo: + - '&bVoting Links:' + - ' &2- https://minecraft-mp.com/server/228386/vote/' +# - ' &2- https://minecraft-server-list.com/server/443272/vote/' + - ' &6- https://topg.org/Minecraft/in-599712' + - ' &2- https://minecraftlist.org/vote/18975' + - ' &6- https://minecraftservers.org/vote/595721' + - ' &2- https://topservers.com/minecraft/in-1275' + - ' &6- https://topmcservers.com/server/681/vote' + - ' &2- https://minecraft.buzz/server/1024&tab=vote' + +# What to display in the Master Builder info command. +masterbuilderinfo: + - '&bHow to apply for Master Builder on the TotalFreedom Server:' + - ' &6- Do not ask for Master Builder in game' + - ' &2- Screenshot some of your previous builds (be ready to prove that the builds are yours)' + - ' &6- Copy the template and create a thread on our forums' + - ' &2- Template:' + - '&9https://forum.totalfreedom.me/d/336-master-builder-application-process' + +# Famous players - cannot be banned by username +famous_players: + - honeydew + - xephos + - captainsparklez + - truemu + - kiershar + - fvdisco + - sethbling + - gamechap + - bertiechap + - vechs + - antvenom + - chimneyswift + - deadmau5 + - etho + - ethoslab + - skydoesminecraft + - skythekidrs + - tobyturner + - xxslyfoxhoundxx + - paulsoaresjr + - sips_ + - deadlox + - xxslyxx + - jeromeasf + - grum + - evilseph + - cavemanfilms + - herobrine + - whiteboy7thst + - stampylonghead + - stampylongnose + - dantdm + - gizzy14gazza + - furiousdestroyer + - pewdie + - thecampingrusher + - logdotzip + - ssundee + - mrcrainer + - achileis + - donibobes + - itsjerry + - itsharry + - bebopvox + - clankstar + - jollyajax + - b0xx3r + - vareide + - defek7 + - strutt20 + - appleg + - sterling_ + - hypixel + - huskymudkipz + - setosorcerer + - slamacow + - mrcrayfish + - zexyzek + - synhd + - syndicate + - bajancanadian + - actennisac + - bodil40 + - trumu + - huskymudkipz + - dantdm + - skeppy + - badboyhalo + # Mojang staff below this line + - aeplh + - amir343 + - angryem + - ashrafi + - binni + - blurpi + - bopogamel + - boq + - c418 + - carlmanneh + - carnalizer + - carramel + - darngeek + - dinnerbone + - eldrone + - elevenen + - el_hefe_ + - engst + - excitedze + - frukthamster + - fry_ + - grumm + - hampus + - helloiammarsh + - hey + - hoodad + - ianna_na_na + - jeb_ + - jonkagstrom + - kappe + - klumpig + - krisjelbring + - ladyagnes + - lisa + - mahuldur + - mansolson + - marc + - marc_irl + - masseffect + - midnightenforcer + - minecraftchick + - modhelius + - moesh + - mojangjonas + - mojangsta + - mollstam + - neonmaster + - olle + - olofcarlson + - phreakholm + - poipoichen + - pretto + - profmobius + - razzleberryfox + - searge + - searge_dp + - shoghicp + - slicedlime + - sockerpappan + - themogminer + - theopard + - theputtman + - tom__m + - vaktis + - volgar + - vubui + - xlson + - xsson + - yoloswag4lyfe + - zeeraw + - _alcatras + - _tommo_ + - ____fox____ + +# Pet Protect - Prevent tamed pets from being killed. +petprotect: + enabled: true + +# Logviewer +logs: + url: '' + secret: '' + +# Mojang service checker +service_checker_url: http://status.mojang.com/check + +# HTTPD server +httpd: + enabled: true + host: play.totalfreedom.me + port: 28966 + public_folder: ./public_html + +# Inactivity Auto-Kick (Requires Essentials) +autokick: + enabled: true + + # Percentage of server player capacity used at which players will be automatically kicked for being inactive. Range: 0.0 - 1.0 + threshold: 0.9 + + # Time, in seconds, after which a player should be kicked when inactive + time: 120 + +# Blocked Chat Codes - Use &code,&code2,&code3 (No spaces) +blocked_chatcodes: '&0,&k,&m,&n' + +# Enables certain things like debug logs and console bypasses +developer_mode: false + +# AntiSpam Time (minutes) +antispam_minutes: 2 diff --git a/src/main/resources/indefinitebans.yml b/src/main/resources/indefinitebans.yml new file mode 100644 index 00000000..4e4404ce --- /dev/null +++ b/src/main/resources/indefinitebans.yml @@ -0,0 +1,15 @@ +# +# TotalFreedomMod Indefinite Bans +# + +badplayer1: + uuid: '123e4567-e89b-12d3-a456-426614174000' + ips: + - 123.123.123.123 + - 321.321.321.321 + reason: 'bad player' +badplayer2: + ips: + - 111.111.111.111 +badplayer3: + ips: [] diff --git a/src/main/resources/permissions.yml b/src/main/resources/permissions.yml new file mode 100644 index 00000000..ae404160 --- /dev/null +++ b/src/main/resources/permissions.yml @@ -0,0 +1,71 @@ +# +# TotalFreedomMod Permissions +# by ZeroEpoch1969 +# + +# Note that every group inherits the previous groups' permissions +# Meaning Senior Admins have the permissions of Operators and Admins, and etc + +# This is used to remove all permission begging with the root node +# This is useful when a plugin gives all permissions to a player because they are opped +remove: + - "voxelsniper" + - "bending" + +# Operator permission nodes +operators: + # WorldEdit + - "worldedit.help" + - "worldedit.history.undo.self" + - "worldedit.history.redo.self" + - "worldedit.history.clear" + - "worldedit.limit" + - "worldedit.reorder" + - "worldedit.drawsel" + - "worldedit.searchitem" + - "worldedit.navigation.*" + - "worldedit.selection.*" + - "worldedit.wand" + - "worldedit.wand.*" + - "worldedit.region.*" + - "worldedit.generation.*" + - "worldedit.clipboard.*" + - "worldedit.tool.info" + - "worldedit.biome.*" + - "worldedit.removeabove" + - "worldedit.removebelow" + - "worldedit.removenear" + - "worldedit.replacenear" + + # LibsDisguises + - "libsdisguises.noactionbar" + + # WorldGuard + - "worldguard.region.list.own" + - "worldguard.region.addmember.own.*" + - "worldguard.region.removemember.own.*" + - "worldguard.region.info.*" + +# Master Builder permission nodes +master_builders: + - "worldedit.tool.*" + - "worldedit.brush.*" + - "worldedit.snow" + - "worldedit.green" + - "worldedit.extinguish" + - "worldedit.thaw" + - "worldguard.region.bypass.hubworld" + - "worldguard.region.bypass.masterbuilderworld" + - "voxelsniper.sniper" + - "voxelsniper.goto" + - "voxelsniper.brush.*" + +# Admin permission nodes +admins: + - "coreprotect.*" + - "worldedit.*" + - "worldguard.*" + - "bending.*" + +# Senior Admin permission nodes +senior_admins: [] diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 00000000..d5bd702c --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: TotalFreedomMod +main: me.totalfreedom.totalfreedommod.TotalFreedomMod +version: ${project.version} +description: Plugin for the Total Freedom server. +softdepend: + - BukkitTelnet + - Essentials + - CoreProtect + - LibsDisguises + - WorldEdit + - WorldGuard + - WorldGuardExtraFlags + - TFGuilds + - JDA + - Votifier +authors: [Madgeek1450, Prozza] +api-version: "1.16" \ No newline at end of file diff --git a/src/main/resources/punishments.yml b/src/main/resources/punishments.yml new file mode 100644 index 00000000..b2bb87dd --- /dev/null +++ b/src/main/resources/punishments.yml @@ -0,0 +1,3 @@ +# +# TotalFreedomMod Player Punishments +# diff --git a/supressions.xml b/supressions.xml new file mode 100644 index 00000000..09efa3c8 --- /dev/null +++ b/supressions.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/travis-upload.sh b/travis-upload.sh new file mode 100644 index 00000000..e0ed56cb --- /dev/null +++ b/travis-upload.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then + mv target/TotalFreedomMod.jar target/TotalFreedomMod-${TRAVIS_COMMIT}.jar + export SSHPASS=${SFTP_PASSWORD} + sshpass -e sftp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oLogLevel=ERROR -oBatchMode=no -b - ${SFTP_USER}@${SFTP_HOST}:${SFTP_PATH} << ! + put target/TotalFreedomMod-${TRAVIS_COMMIT}.jar + bye +! + echo "Artifact upload status: "$? +else + echo "Skipping artifact upload for pull request build." +fi