├── bungeecord ├── src │ └── main │ │ ├── resources │ │ └── bungee.yml │ │ └── java │ │ └── io │ │ └── tebex │ │ └── plugin │ │ ├── command │ │ ├── SubCommand.java │ │ ├── sub │ │ │ ├── ForceCheckCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── HelpCommand.java │ │ │ └── SecretCommand.java │ │ └── TebexCommand.java │ │ ├── event │ │ └── JoinListener.java │ │ └── manager │ │ └── CommandManager.java ├── .gitignore └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── sdk ├── src │ └── main │ │ ├── java │ │ └── io │ │ │ └── tebex │ │ │ └── sdk │ │ │ ├── obj │ │ │ ├── DiscountType.java │ │ │ ├── BasketType.java │ │ │ ├── ICategory.java │ │ │ ├── ServerEventType.java │ │ │ ├── CheckoutUrl.java │ │ │ ├── QueuedPlayer.java │ │ │ ├── ServerEvent.java │ │ │ ├── SubCategory.java │ │ │ ├── Category.java │ │ │ ├── CategoryPackage.java │ │ │ ├── QueuedCommand.java │ │ │ ├── PlayerLookupInfo.java │ │ │ └── CommunityGoal.java │ │ │ ├── placeholder │ │ │ ├── Placeholder.java │ │ │ ├── defaults │ │ │ │ ├── NamePlaceholder.java │ │ │ │ └── UuidPlaceholder.java │ │ │ └── PlaceholderManager.java │ │ │ ├── request │ │ │ ├── exception │ │ │ │ └── AnalyseException.java │ │ │ ├── response │ │ │ │ ├── OfflineCommandsResponse.java │ │ │ │ ├── PaginatedResponse.java │ │ │ │ ├── DuePlayersResponse.java │ │ │ │ └── ServerInformation.java │ │ │ ├── interceptor │ │ │ │ └── LoggingInterceptor.java │ │ │ ├── builder │ │ │ │ └── CreateCouponRequest.java │ │ │ └── TebexRequest.java │ │ │ ├── platform │ │ │ ├── config │ │ │ │ ├── IPlatformConfig.java │ │ │ │ ├── ProxyPlatformConfig.java │ │ │ │ └── ServerPlatformConfig.java │ │ │ ├── PlayerType.java │ │ │ ├── PlatformType.java │ │ │ ├── PlatformModule.java │ │ │ └── PlatformTelemetry.java │ │ │ ├── util │ │ │ ├── GsonUtil.java │ │ │ ├── FileUtils.java │ │ │ ├── UUIDUtil.java │ │ │ ├── VersionUtil.java │ │ │ ├── StringUtil.java │ │ │ ├── Pagination.java │ │ │ └── ResourceUtil.java │ │ │ ├── exception │ │ │ ├── ServerNotFoundException.java │ │ │ └── ServerNotSetupException.java │ │ │ ├── Tebex.java │ │ │ └── triage │ │ │ └── TriageEvent.java │ │ └── resources │ │ └── platform │ │ ├── proxy │ │ └── config.yml │ │ └── server │ │ └── config.yml ├── .gitignore └── build.gradle.kts ├── .gitignore ├── velocity ├── src │ └── main │ │ ├── java-templates │ │ └── io │ │ │ └── tebex │ │ │ └── plugin │ │ │ └── Constants.java.peb │ │ └── java │ │ └── io │ │ └── tebex │ │ └── plugin │ │ ├── event │ │ └── JoinListener.java │ │ ├── command │ │ ├── sub │ │ │ ├── ForceCheckCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── DebugCommand.java │ │ │ ├── BanCommand.java │ │ │ └── SecretCommand.java │ │ ├── SubCommand.java │ │ └── TebexCommand.java │ │ └── manager │ │ └── CommandManager.java ├── .gitignore └── build.gradle.kts ├── bukkit ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── io │ │ └── tebex │ │ └── plugin │ │ ├── command │ │ ├── BuyCommand.java │ │ ├── SubCommand.java │ │ ├── sub │ │ │ ├── ForceCheckCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── InfoCommand.java │ │ │ ├── GoalsCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── ReportCommand.java │ │ │ ├── CheckoutCommand.java │ │ │ ├── SendLinkCommand.java │ │ │ ├── DebugCommand.java │ │ │ ├── BanCommand.java │ │ │ ├── LookupCommand.java │ │ │ └── SecretCommand.java │ │ └── TebexCommand.java │ │ ├── util │ │ └── MaterialUtil.java │ │ ├── event │ │ └── JoinListener.java │ │ ├── placeholder │ │ └── BukkitNamePlaceholder.java │ │ └── manager │ │ └── CommandManager.java ├── .gitignore └── build.gradle.kts ├── fabric ├── gradle.properties ├── src │ └── main │ │ ├── resources │ │ └── fabric.mod.json │ │ └── java │ │ └── io │ │ └── tebex │ │ └── plugin │ │ ├── command │ │ ├── SubCommand.java │ │ ├── BuyCommand.java │ │ └── sub │ │ │ ├── ForceCheckCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── GoalsCommand.java │ │ │ ├── ReportCommand.java │ │ │ ├── InfoCommand.java │ │ │ ├── CheckoutCommand.java │ │ │ ├── SendLinkCommand.java │ │ │ ├── BanCommand.java │ │ │ ├── LookupCommand.java │ │ │ ├── DebugCommand.java │ │ │ └── SecretCommand.java │ │ ├── event │ │ └── JoinListener.java │ │ ├── util │ │ └── Multithreading.java │ │ └── manager │ │ └── CommandManager.java ├── .gitignore └── build.gradle.kts ├── settings.gradle.kts ├── .github └── ISSUE_TEMPLATE │ └── minecraft-plugin-bug-report.md ├── CONTRIBUTING.md ├── gradlew.bat ├── CHANGELOG.md ├── test.sh └── README.md /bungeecord/src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: Tebex 2 | version: '${version}' 3 | main: io.tebex.plugin.TebexPlugin -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firestarter/tebex/2.0.5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx4G 3 | org.gradle.parallel=true -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/DiscountType.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | public enum DiscountType { 4 | PERCENTAGE, 5 | VALUE 6 | } 7 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/BasketType.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | public enum BasketType { 4 | SINGLE, 5 | SUBSCRIPTION, 6 | BOTH 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | ignore/ 4 | **/build 5 | **/*.iml 6 | **/*.jar 7 | !gradle/wrapper/** 8 | **/*.zip 9 | .DS_Store 10 | **/.DS_Store 11 | docs 12 | test/** -------------------------------------------------------------------------------- /velocity/src/main/java-templates/io/tebex/plugin/Constants.java.peb: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin; 2 | 3 | public final class Constants { 4 | public static final String VERSION = "{{ version }}"; 5 | 6 | private Constants() {} 7 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/placeholder/Placeholder.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.placeholder; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | 5 | public interface Placeholder { 6 | String handle(QueuedPlayer player, String command); 7 | } -------------------------------------------------------------------------------- /bukkit/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Tebex 2 | version: '${version}' 3 | main: io.tebex.plugin.TebexPlugin 4 | api-version: 1.13 5 | softdepend: 6 | - BuycraftX 7 | commands: 8 | tebex: 9 | description: The main command 10 | aliases: 11 | - buycraft -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/ICategory.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import java.util.List; 4 | 5 | public interface ICategory { 6 | int getId(); 7 | 8 | int getOrder(); 9 | 10 | String getName(); 11 | 12 | String getGuiItem(); 13 | 14 | List getPackages(); 15 | } 16 | -------------------------------------------------------------------------------- /fabric/gradle.properties: -------------------------------------------------------------------------------- 1 | # Fabric Properties 2 | # check these on https://fabricmc.net/develop 3 | minecraft_version=1.16.5 4 | yarn_mappings=1.16.5+build.10 5 | loader_version=0.14.21 6 | 7 | # Mod Properties 8 | mod_version=1.0.0 9 | maven_group=io.tebex.plugin 10 | archives_base_name=tebexplugin 11 | 12 | # Dependencies 13 | fabric_version=0.42.0+1.16 -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/ServerEventType.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | public enum ServerEventType { 4 | JOIN("server.join"), 5 | LEAVE("server.leave"); 6 | 7 | private final String name; 8 | 9 | ServerEventType(String name) { 10 | this.name = name; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/exception/AnalyseException.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.exception; 2 | 3 | public class AnalyseException extends Throwable { 4 | private final String message; 5 | 6 | public AnalyseException(String message) { 7 | this.message = message; 8 | } 9 | 10 | public String getMessage() { 11 | return message; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/config/IPlatformConfig.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform.config; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | 5 | /** 6 | * The base PlatformConfig class holds the configuration for the Tebex SDK. 7 | */ 8 | public interface IPlatformConfig { 9 | int getConfigVersion(); 10 | String getSecretKey(); 11 | boolean isVerbose(); 12 | YamlDocument getYamlDocument(); 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/GsonUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | public class GsonUtil { 10 | public static List arrayToList(JsonArray array) { 11 | return array.asList().stream().map(JsonElement::getAsInt).collect(Collectors.toList()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = "Fabric" 5 | url = uri("https://maven.fabricmc.net/") 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | 11 | plugins { 12 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" 13 | } 14 | 15 | rootProject.name = "TebexPlugin" 16 | 17 | listOf("sdk", "bukkit", "bungeecord", "velocity", "fabric").forEach(::include) 18 | 19 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/PlayerType.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform; 2 | 3 | /** 4 | * The PlayerType enum represents the various player client types that can connect to the server. 5 | * The Analyse SDK supports both Java and Bedrock Edition players. 6 | */ 7 | public enum PlayerType { 8 | /** 9 | * Represents a Java Edition player. 10 | */ 11 | JAVA, 12 | 13 | /** 14 | * Represents a Bedrock Edition player. 15 | */ 16 | BEDROCK 17 | } -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "tebex", 4 | "version": "${version}", 5 | "name": "Tebex", 6 | "description": "The Tebex adapter.", 7 | "authors": [], 8 | "contact": {}, 9 | "license": "MIT", 10 | "environment": "server", 11 | "entrypoints": { 12 | "server": [ 13 | "io.tebex.plugin.TebexPlugin" 14 | ] 15 | }, 16 | "depends": { 17 | "fabricloader": ">=0.14.9", 18 | "fabric": "*", 19 | "minecraft": "1.16.5" 20 | } 21 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/exception/ServerNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.exception; 2 | 3 | /** 4 | * Represents an exception thrown when a requested server is not found. 5 | */ 6 | public class ServerNotFoundException extends Throwable { 7 | 8 | /** 9 | * Returns the error message associated with the exception. 10 | * 11 | * @return The error message. 12 | */ 13 | @Override 14 | public String getMessage() { 15 | return "That server doesn't exist!"; 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/exception/ServerNotSetupException.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.exception; 2 | 3 | /** 4 | * Represents an exception thrown when the server hasn't properly set up Analyse. 5 | */ 6 | public class ServerNotSetupException extends Throwable { 7 | 8 | /** 9 | * Returns the error message associated with the exception. 10 | * 11 | * @return The error message. 12 | */ 13 | @Override 14 | public String getMessage() { 15 | return "Tebex not setup!"; 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/resources/platform/proxy/config.yml: -------------------------------------------------------------------------------- 1 | # Tebex v${version} - https://tebex.io 2 | # Check out https://tebex.io/docs if you need help. 3 | 4 | # Whether to check for updates or not. 5 | check-for-updates: true 6 | 7 | # Debug Mode 8 | verbose: false 9 | 10 | # These settings are exclusive to this server. 11 | # Do not share these credentials with anyone. 12 | server: 13 | # Secret Key 14 | # This is used to identify your server. 15 | secret-key: '' 16 | 17 | # Configuration Version 18 | # !! Do not change this value !! 19 | config-version: 2 -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/response/OfflineCommandsResponse.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.response; 2 | 3 | import io.tebex.sdk.obj.QueuedCommand; 4 | 5 | import java.util.List; 6 | 7 | public class OfflineCommandsResponse { 8 | private final boolean limited; 9 | private final List commands; 10 | 11 | public OfflineCommandsResponse(boolean limited, List commands) { 12 | this.limited = limited; 13 | this.commands = commands; 14 | } 15 | 16 | public boolean isLimited() { 17 | return limited; 18 | } 19 | 20 | public List getCommands() { 21 | return commands; 22 | } 23 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import java.io.File; 4 | 5 | public class FileUtils { 6 | public static boolean deleteDirectory(File dir) { 7 | if (dir.isDirectory()) { 8 | File[] children = dir.listFiles(); 9 | if (children != null) { 10 | for (File child : children) { 11 | boolean success = deleteDirectory(child); 12 | if (!success) { 13 | return false; 14 | } 15 | } 16 | } 17 | } 18 | 19 | // Either file or an empty directory 20 | return dir.delete(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/PlatformType.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform; 2 | 3 | /** 4 | * The PlatformType enum represents the different server platforms supported by the Analyse SDK. 5 | * The current supported platforms include Bukkit, BungeeCord, and Velocity. 6 | */ 7 | public enum PlatformType { 8 | /** 9 | * Represents the Bukkit server platform. 10 | */ 11 | BUKKIT, 12 | 13 | /** 14 | * Represents the BungeeCord server platform. 15 | */ 16 | BUNGEECORD, 17 | 18 | /** 19 | * Represents the Velocity server platform. 20 | */ 21 | VELOCITY, 22 | 23 | /** 24 | * Represents the Fabric server platform. 25 | */ 26 | FABRIC 27 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/CheckoutUrl.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import java.util.Date; 4 | 5 | public class CheckoutUrl { 6 | private final String url; 7 | private final Date expires; 8 | 9 | public CheckoutUrl(String url, Date expires) { 10 | this.url = url; 11 | this.expires = expires; 12 | } 13 | 14 | public String getUrl() { 15 | return url; 16 | } 17 | 18 | public Date getExpiry() { 19 | return expires; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "CheckoutUrl{" + 25 | "url='" + url + '\'' + 26 | ", expires=" + expires + 27 | '}'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bukkit/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /fabric/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /sdk/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /bungeecord/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/placeholder/defaults/NamePlaceholder.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.placeholder.defaults; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | import io.tebex.sdk.placeholder.Placeholder; 5 | import io.tebex.sdk.placeholder.PlaceholderManager; 6 | 7 | public class NamePlaceholder implements Placeholder { 8 | private final PlaceholderManager placeholderManager; 9 | 10 | public NamePlaceholder(PlaceholderManager placeholderManager) { 11 | this.placeholderManager = placeholderManager; 12 | } 13 | 14 | @Override 15 | public String handle(QueuedPlayer player, String command) { 16 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(player.getName()); 17 | } 18 | } -------------------------------------------------------------------------------- /velocity/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/UUIDUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import java.util.UUID; 4 | 5 | public class UUIDUtil { 6 | /** 7 | * Translates a Mojang-style UUID into an UUID Java can use. The Tebex plugin API returns all results with 8 | * Mojang-style UUIDs. 9 | * 10 | * @param id the Mojang UUID to use 11 | * @return the Java UUID or null if id provided is null 12 | */ 13 | public static UUID mojangIdToJavaId(String id) { 14 | if (id == null) { 15 | return null; 16 | } 17 | 18 | return UUID.fromString(id.replaceFirst( 19 | "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", 20 | "$1-$2-$3-$4-$5" 21 | )); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/response/PaginatedResponse.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.response; 2 | 3 | import io.tebex.sdk.util.Pagination; 4 | 5 | import java.util.List; 6 | 7 | public class PaginatedResponse { 8 | private final Pagination pagination; 9 | private final List data; 10 | 11 | public PaginatedResponse(Pagination pagination, List data) { 12 | this.pagination = pagination; 13 | this.data = data; 14 | } 15 | 16 | public Pagination getPagination() { 17 | return pagination; 18 | } 19 | 20 | public List getData() { 21 | return data; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "PaginatedResponse{" + 27 | "pagination=" + pagination + 28 | ", data=" + data + 29 | '}'; 30 | } 31 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/VersionUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | public class VersionUtil { 4 | public static boolean isNewerVersion(String currentVersion, String newVersion) { 5 | String[] currentVersionArray = currentVersion.split("\\."); 6 | String[] newVersionArray = newVersion.split("\\."); 7 | 8 | int length = Math.max(currentVersionArray.length, newVersionArray.length); 9 | 10 | for (int i = 0; i < length; i++) { 11 | int current = i < currentVersionArray.length ? Integer.parseInt(currentVersionArray[i]) : 0; 12 | int next = i < newVersionArray.length ? Integer.parseInt(newVersionArray[i]) : 0; 13 | 14 | if (next > current) { 15 | return true; 16 | } else if (next < current) { 17 | return false; 18 | } 19 | } 20 | 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/BuyCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.gui.BuyGUI; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | public class BuyCommand extends Command { 11 | private final TebexPlugin platform; 12 | 13 | public BuyCommand(String command, TebexPlugin platform) { 14 | super(command); 15 | this.platform = platform; 16 | } 17 | 18 | @Override 19 | public boolean execute(CommandSender sender, String label, String[] args) { 20 | if(! platform.isSetup()) { 21 | sender.sendMessage(ChatColor.RED + "Tebex is not setup yet!"); 22 | return true; 23 | } 24 | 25 | platform.getBuyGUI().open((Player) sender); 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/placeholder/defaults/UuidPlaceholder.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.placeholder.defaults; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | import io.tebex.sdk.placeholder.Placeholder; 5 | import io.tebex.sdk.placeholder.PlaceholderManager; 6 | import io.tebex.sdk.util.UUIDUtil; 7 | 8 | public class UuidPlaceholder implements Placeholder { 9 | private final PlaceholderManager placeholderManager; 10 | 11 | public UuidPlaceholder(PlaceholderManager placeholderManager) { 12 | this.placeholderManager = placeholderManager; 13 | } 14 | 15 | @Override 16 | public String handle(QueuedPlayer player, String command) { 17 | if (player.getUuid() == null) { 18 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(player.getName()); 19 | } 20 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(UUIDUtil.mojangIdToJavaId(player.getUuid()).toString()); 21 | } 22 | } -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/SubCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import org.bukkit.command.CommandSender; 5 | 6 | public abstract class SubCommand { 7 | private final TebexPlugin platform; 8 | private final String name; 9 | private final String permission; 10 | 11 | public SubCommand(TebexPlugin platform, String name, String permission) { 12 | this.platform = platform; 13 | this.name = name; 14 | this.permission = permission; 15 | } 16 | 17 | public abstract void execute(final CommandSender sender, final String[] args); 18 | 19 | public TebexPlugin getPlatform() { 20 | return platform; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public String getPermission() { 28 | return permission; 29 | } 30 | 31 | public abstract String getDescription(); 32 | public String getUsage() { 33 | return ""; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/ForceCheckCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class ForceCheckCommand extends SubCommand { 8 | private final TebexPlugin platform; 9 | 10 | public ForceCheckCommand(TebexPlugin platform) { 11 | super(platform, "forcecheck", "tebex.forcecheck"); 12 | this.platform = platform; 13 | } 14 | 15 | @Override 16 | public void execute(CommandSender sender, String[] args) { 17 | if(! platform.isSetup()) { 18 | sender.sendMessage("§cTebex is not setup yet!"); 19 | return; 20 | } 21 | 22 | sender.sendMessage("§b[Tebex] §7Performing force check..."); 23 | getPlatform().performCheck(false); 24 | } 25 | 26 | @Override 27 | public String getDescription() { 28 | return "Checks immediately for new purchases."; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/SubCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import net.md_5.bungee.api.CommandSender; 5 | 6 | public abstract class SubCommand { 7 | private final TebexPlugin platform; 8 | private final String name; 9 | private final String permission; 10 | 11 | public SubCommand(TebexPlugin platform, String name, String permission) { 12 | this.platform = platform; 13 | this.name = name; 14 | this.permission = permission; 15 | } 16 | 17 | public abstract void execute(final CommandSender sender, final String[] args); 18 | 19 | public TebexPlugin getPlatform() { 20 | return platform; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public String getPermission() { 28 | return permission; 29 | } 30 | 31 | public abstract String getDescription(); 32 | 33 | public String getUsage() { 34 | return ""; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/event/JoinListener.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.event; 2 | 3 | import com.velocitypowered.api.event.Subscribe; 4 | import com.velocitypowered.api.event.connection.LoginEvent; 5 | import com.velocitypowered.api.proxy.Player; 6 | import io.tebex.plugin.TebexPlugin; 7 | import io.tebex.sdk.obj.QueuedPlayer; 8 | 9 | import java.util.UUID; 10 | 11 | public class JoinListener { 12 | private final TebexPlugin plugin; 13 | 14 | public JoinListener(TebexPlugin plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | @Subscribe 19 | public void onPlayerConnect(LoginEvent event) { 20 | Player player = event.getPlayer(); 21 | 22 | Object playerId = plugin.getPlayerId(player.getUsername(), player.getUniqueId()); 23 | 24 | if (!plugin.getQueuedPlayers().containsKey(playerId)) { 25 | return; 26 | } 27 | 28 | plugin.handleOnlineCommands(new QueuedPlayer(plugin.getQueuedPlayers().get(playerId), player.getUsername(), player.getUniqueId().toString())); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/sub/ForceCheckCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import net.md_5.bungee.api.ChatColor; 6 | import net.md_5.bungee.api.CommandSender; 7 | 8 | public class ForceCheckCommand extends SubCommand { 9 | private final TebexPlugin platform; 10 | 11 | public ForceCheckCommand(TebexPlugin platform) { 12 | super(platform, "forcecheck", "tebex.forcecheck"); 13 | this.platform = platform; 14 | } 15 | 16 | @Override 17 | public void execute(CommandSender sender, String[] args) { 18 | if(! platform.isSetup()) { 19 | sender.sendMessage(ChatColor.RED + "Tebex is not setup yet!"); 20 | return; 21 | } 22 | 23 | sender.sendMessage("§b[Tebex] §7Performing force check.."); 24 | getPlatform().performCheck(); 25 | } 26 | 27 | @Override 28 | public String getDescription() { 29 | return "Rechecks for new purchases."; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/event/JoinListener.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.event; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.sdk.obj.QueuedPlayer; 5 | import net.md_5.bungee.api.event.LoginEvent; 6 | import net.md_5.bungee.api.plugin.Listener; 7 | import net.md_5.bungee.event.EventHandler; 8 | 9 | import java.util.UUID; 10 | 11 | public class JoinListener implements Listener { 12 | private final TebexPlugin plugin; 13 | 14 | public JoinListener(TebexPlugin plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | @EventHandler 19 | public void onPlayerConnect(LoginEvent event) { 20 | UUID uuid = event.getConnection().getUniqueId(); 21 | String name = event.getConnection().getName(); 22 | 23 | Object playerId = plugin.getPlayerId(name, uuid); 24 | 25 | if (!plugin.getQueuedPlayers().containsKey(playerId)) { 26 | return; 27 | } 28 | 29 | plugin.handleOnlineCommands(new QueuedPlayer(plugin.getQueuedPlayers().get(playerId), name, uuid.toString())); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/SubCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import net.minecraft.server.command.ServerCommandSource; 6 | 7 | public abstract class SubCommand { 8 | private final TebexPlugin platform; 9 | private final String name; 10 | private final String permission; 11 | 12 | public SubCommand(TebexPlugin platform, String name, String permission) { 13 | this.platform = platform; 14 | this.name = name; 15 | this.permission = permission; 16 | } 17 | 18 | public abstract void execute(final CommandContext context); 19 | 20 | public TebexPlugin getPlatform() { 21 | return platform; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public String getPermission() { 29 | return permission; 30 | } 31 | 32 | public abstract String getDescription(); 33 | public String getUsage() { 34 | return ""; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/BuyCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.gui.BuyGUI; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.server.network.ServerPlayerEntity; 9 | import net.minecraft.text.LiteralText; 10 | 11 | public class BuyCommand { 12 | private final TebexPlugin plugin; 13 | public BuyCommand(TebexPlugin plugin) { 14 | this.plugin = plugin; 15 | } 16 | 17 | public int execute(CommandContext context) { 18 | final ServerCommandSource source = context.getSource(); 19 | 20 | try { 21 | ServerPlayerEntity player = source.getPlayer(); 22 | new BuyGUI(plugin).open(player); 23 | } catch (CommandSyntaxException e) { 24 | source.sendFeedback(new LiteralText("§b[Tebex] §7You must be a player to run this command!"), false); 25 | } 26 | 27 | return 1; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bungeecord/build.gradle: -------------------------------------------------------------------------------- 1 | group = rootProject.group 2 | version = rootProject.version 3 | 4 | dependencies { 5 | implementation project(':sdk') 6 | implementation 'net.sf.trove4j:trove4j:3.0.3' // Add trove4j dependency 7 | 8 | compileOnly 'net.md-5:bungeecord-api:1.18-R0.1-SNAPSHOT' 9 | compileOnly 'dev.dejvokep:boosted-yaml:1.3' 10 | } 11 | 12 | shadowJar { 13 | configurations = [project.configurations.runtimeClasspath] 14 | 15 | relocate 'gnu.trove4j', 'net.analyse.plugin.libs.trove4j' // Relocate trove4j 16 | relocate 'okhttp3', 'net.analyse.plugin.libs.okhttp3' // Relocate okhttp 17 | relocate 'okio', 'net.analyse.plugin.libs.okio' // Relocate okio (okhttp dependency) 18 | relocate 'dev.dejvokep.boostedyaml', 'net.analyse.plugin.libs.boostedyaml' // Relocate boostedyaml 19 | relocate 'org.jetbrains.annotations', 'net.analyse.plugin.libs.jetbrains' // Relocate jetbrains 20 | relocate 'kotlin', 'net.analyse.plugin.libs.kotlin' // Relocate jetbrains 21 | minimize() 22 | 23 | // minimize { 24 | // exclude(project(":geyser-bridge")) 25 | // exclude(project(":geyser-common")) 26 | // } 27 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/QueuedPlayer.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class QueuedPlayer { 6 | private final int id; 7 | private final String name; 8 | private final String uuid; 9 | 10 | /** 11 | * Constructs a Player instance. 12 | * 13 | * @param id The Tebex player ID. 14 | * @param name The player name. 15 | * @param uuid The player UUID. 16 | */ 17 | public QueuedPlayer(int id, String name, String uuid) { 18 | this.id = id; 19 | this.name = name; 20 | this.uuid = uuid; 21 | } 22 | 23 | public int getId() { 24 | return id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public String getUuid() { 32 | return uuid; 33 | } 34 | 35 | public static QueuedPlayer fromJson(JsonObject object) { 36 | return new QueuedPlayer( 37 | object.get("id").getAsInt(), 38 | object.get("name").getAsString(), 39 | !object.get("uuid").isJsonNull() ? object.get("uuid").getAsString() : null 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/ForceCheckCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | 7 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 8 | 9 | public class ForceCheckCommand extends SubCommand { 10 | private final TebexPlugin platform; 11 | 12 | public ForceCheckCommand(TebexPlugin platform) { 13 | super(platform, "forcecheck", "tebex.forcecheck"); 14 | this.platform = platform; 15 | } 16 | 17 | @Override 18 | public void execute(CommandSource sender, String[] args) { 19 | if(! platform.isSetup()) { 20 | sender.sendMessage(legacySection().deserialize("§cTebex is not setup yet!")); 21 | return; 22 | } 23 | 24 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Performing force check...")); 25 | getPlatform().performCheck(false); 26 | } 27 | 28 | @Override 29 | public String getDescription() { 30 | return "Checks immediately for new purchases."; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/sub/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import net.md_5.bungee.api.CommandSender; 7 | 8 | import java.io.IOException; 9 | 10 | public class ReloadCommand extends SubCommand { 11 | public ReloadCommand(TebexPlugin platform) { 12 | super(platform, "reload", "tebex.admin"); 13 | } 14 | 15 | @Override 16 | public void execute(CommandSender sender, String[] args) { 17 | TebexPlugin platform = getPlatform(); 18 | try { 19 | YamlDocument configYaml = platform.initPlatformConfig(); 20 | platform.loadServerPlatformConfig(configYaml); 21 | platform.refreshListings(); 22 | sender.sendMessage("§8[Tebex] §7Successfully reloaded."); 23 | } catch (IOException e) { 24 | sender.sendMessage("§8[Tebex] §cFailed to reload the plugin: Check Console."); 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | @Override 30 | public String getDescription() { 31 | return "Reloads the plugin."; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/util/MaterialUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.util; 2 | 3 | import com.cryptomorin.xseries.XMaterial; 4 | 5 | import java.util.Optional; 6 | 7 | public class MaterialUtil { 8 | public static Optional fromString(String material) { 9 | try { 10 | int id = Integer.parseInt(material); 11 | return XMaterial.matchXMaterial(id, (byte) 0); 12 | } catch(NumberFormatException e) { 13 | if(material.contains(":")) { 14 | String[] split = material.split(":"); 15 | try { 16 | int id = Integer.parseInt(split[0]); 17 | byte data = Byte.parseByte(split[1]); 18 | return XMaterial.matchXMaterial(id, data); 19 | } catch(NumberFormatException ex) { 20 | if(split[0].equalsIgnoreCase("minecraft")) { 21 | return XMaterial.matchXMaterial(split[1].toUpperCase()); 22 | } 23 | 24 | return XMaterial.matchXMaterial(material); 25 | } 26 | } else { 27 | return XMaterial.matchXMaterial(material); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/Tebex.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk; 2 | 3 | import io.tebex.sdk.platform.Platform; 4 | 5 | /** 6 | * The Tebex class serves as the entry point for the Tebex SDK and provides methods to 7 | * initialise and access the platform instance. The SDK is designed to work with various server 8 | * platforms, such as Bukkit or Sponge, through the use of the Platform interface. 9 | */ 10 | public class Tebex { 11 | private static Platform platform; 12 | 13 | /** 14 | * Private constructor to prevent instantiation of this singleton class. 15 | */ 16 | public Tebex() { 17 | throw new UnsupportedOperationException("This is a singleton class and cannot be instantiated"); 18 | } 19 | 20 | /** 21 | * Initialises the Tebex SDK with the provided platform instance. 22 | * 23 | * @param platform The platform instance to initialise the SDK with 24 | */ 25 | public static void init(Platform platform) { 26 | Tebex.platform = platform; 27 | } 28 | 29 | /** 30 | * Retrieves the currently initialised platform instance. 31 | * 32 | * @return The current platform instance, or null if the SDK has not been initialized 33 | */ 34 | public static Platform get() { 35 | return platform; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/response/DuePlayersResponse.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.response; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | 5 | import java.util.List; 6 | 7 | public class DuePlayersResponse { 8 | private final boolean executeOffline; 9 | private final int nextCheck; 10 | private final boolean more; 11 | private final List players; 12 | 13 | /** 14 | * Constructs a DuePlayersResponse instance. 15 | * 16 | * @param executeOffline Can execute offline. 17 | * @param nextCheck The next check time in seconds. 18 | * @param more If there are more players. 19 | * @param players The list of players. 20 | */ 21 | public DuePlayersResponse(boolean executeOffline, int nextCheck, boolean more, List players) { 22 | this.executeOffline = executeOffline; 23 | this.nextCheck = nextCheck; 24 | this.more = more; 25 | this.players = players; 26 | } 27 | 28 | public boolean canExecuteOffline() { 29 | return executeOffline; 30 | } 31 | 32 | public int getNextCheck() { 33 | return nextCheck; 34 | } 35 | 36 | public boolean isMore() { 37 | return more; 38 | } 39 | 40 | public List getPlayers() { 41 | return players; 42 | } 43 | } -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/event/JoinListener.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.event; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.sdk.obj.QueuedPlayer; 5 | import io.tebex.sdk.obj.ServerEvent; 6 | import io.tebex.sdk.obj.ServerEventType; 7 | import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; 8 | import net.minecraft.server.network.ServerPlayerEntity; 9 | 10 | import java.util.Date; 11 | 12 | public class JoinListener { 13 | private final TebexPlugin plugin; 14 | 15 | public JoinListener(TebexPlugin plugin) { 16 | this.plugin = plugin; 17 | ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> onPlayerJoin(handler.player)); 18 | } 19 | 20 | private void onPlayerJoin(ServerPlayerEntity player) { 21 | Object playerId = plugin.getPlayerId(player.getName().asString(), player.getUuid()); 22 | plugin.getServerEvents().add(new ServerEvent(player.getUuid().toString(), player.getName().asString(), player.getIp(), ServerEventType.JOIN, new Date().toString())); 23 | 24 | if(! plugin.getQueuedPlayers().containsKey(playerId)) { 25 | return; 26 | } 27 | 28 | plugin.handleOnlineCommands(new QueuedPlayer(plugin.getQueuedPlayers().get(playerId), player.getName().asString(), player.getUuid().toString())); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/PlatformModule.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform; 2 | 3 | import io.tebex.sdk.Tebex; 4 | 5 | /** 6 | * The PlatformModule class represents a module that can be integrated into a server platform. 7 | * It provides base methods to manage the lifecycle of the module, such as enabling or disabling it. 8 | */ 9 | public abstract class PlatformModule { 10 | /** 11 | * Retrieves the name of the module. 12 | * 13 | * @return The name of the module. 14 | */ 15 | public abstract String getName(); 16 | 17 | /** 18 | * Called when the module is enabled. 19 | */ 20 | public abstract void onEnable(); 21 | 22 | /** 23 | * Called when the module is disabled. 24 | */ 25 | public abstract void onDisable(); 26 | 27 | /** 28 | * Retrieves the current platform instance. 29 | * 30 | * @return The current platform instance. 31 | */ 32 | public Platform getPlatform() { 33 | return Tebex.get(); 34 | } 35 | 36 | /** 37 | * Retrieves the required plugin for the module. 38 | * Override this method if the module depends on another plugin. 39 | * 40 | * @return The required plugin name, or null if no plugin is required. 41 | */ 42 | public String getRequiredPlugin() { 43 | return null; 44 | } 45 | } -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/ForceCheckCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import net.minecraft.server.command.ServerCommandSource; 7 | import net.minecraft.text.LiteralText; 8 | 9 | public class ForceCheckCommand extends SubCommand { 10 | private final TebexPlugin platform; 11 | 12 | public ForceCheckCommand(TebexPlugin platform) { 13 | super(platform, "forcecheck", "tebex.forcecheck"); 14 | this.platform = platform; 15 | } 16 | 17 | @Override 18 | public void execute(CommandContext context) { 19 | if(! platform.isSetup()) { 20 | context.getSource().sendFeedback(new LiteralText("§cTebex is not setup yet!"), false); 21 | return; 22 | } 23 | 24 | // if running from console, return 25 | if (context.getSource().getEntity() != null) { 26 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Performing force check..."), false); 27 | } 28 | 29 | getPlatform().performCheck(false); 30 | } 31 | 32 | @Override 33 | public String getDescription() { 34 | return "Checks immediately for new purchases."; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/sub/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import io.tebex.plugin.manager.CommandManager; 6 | import net.md_5.bungee.api.CommandSender; 7 | 8 | import java.util.Comparator; 9 | 10 | public class HelpCommand extends SubCommand { 11 | private final CommandManager commandManager; 12 | 13 | public HelpCommand(TebexPlugin platform, CommandManager commandManager) { 14 | super(platform, "help", "tebex.admin"); 15 | this.commandManager = commandManager; 16 | } 17 | 18 | @Override 19 | public void execute(CommandSender sender, String[] args) { 20 | sender.sendMessage("§b[Tebex] §7Plugin Commands:"); 21 | 22 | commandManager 23 | .getCommands() 24 | .values() 25 | .stream() 26 | .sorted(Comparator.comparing(SubCommand::getName)) 27 | .forEach(subCommand -> sender.sendMessage(" §8- §f/tebex " + subCommand.getName() + "§f" + (!subCommand.getUsage().isEmpty() ? " §3" + subCommand.getUsage() + " " : " ") + "§7§o(" + subCommand.getDescription() + ")")); 28 | } 29 | 30 | @Override 31 | public String getDescription() { 32 | return "Shows this help page."; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/event/JoinListener.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.event; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.sdk.obj.QueuedPlayer; 5 | import io.tebex.sdk.obj.ServerEvent; 6 | import io.tebex.sdk.obj.ServerEventType; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerJoinEvent; 11 | 12 | import java.util.Date; 13 | 14 | public class JoinListener implements Listener { 15 | private final TebexPlugin plugin; 16 | 17 | public JoinListener(TebexPlugin plugin) { 18 | this.plugin = plugin; 19 | } 20 | 21 | @EventHandler 22 | public void onPlayerJoin(PlayerJoinEvent event) { 23 | Player player = event.getPlayer(); 24 | Object playerId = plugin.getPlayerId(player.getName(), player.getUniqueId()); 25 | plugin.getServerEvents().add(new ServerEvent(player.getUniqueId().toString(), player.getName(), player.getAddress().getAddress().getHostAddress(), ServerEventType.JOIN, new Date().toString())); 26 | 27 | if(! plugin.getQueuedPlayers().containsKey(playerId)) { 28 | return; 29 | } 30 | 31 | plugin.handleOnlineCommands(new QueuedPlayer(plugin.getQueuedPlayers().get(playerId), player.getName(), player.getUniqueId().toString())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/placeholder/BukkitNamePlaceholder.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.placeholder; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | import io.tebex.sdk.placeholder.Placeholder; 5 | import io.tebex.sdk.placeholder.PlaceholderManager; 6 | import io.tebex.sdk.util.UUIDUtil; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.OfflinePlayer; 9 | 10 | public class BukkitNamePlaceholder implements Placeholder { 11 | private final PlaceholderManager placeholderManager; 12 | 13 | public BukkitNamePlaceholder(PlaceholderManager placeholderManager) { 14 | this.placeholderManager = placeholderManager; 15 | } 16 | 17 | @Override 18 | public String handle(QueuedPlayer player, String command) { 19 | if (player.getUuid() == null || player.getUuid().isEmpty()) { 20 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(player.getName()); 21 | } 22 | 23 | OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(UUIDUtil.mojangIdToJavaId(player.getUuid())); 24 | if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) { 25 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(player.getName()); 26 | } 27 | 28 | return placeholderManager.getUsernameRegex().matcher(command).replaceAll(offlinePlayer.getName()); 29 | } 30 | } -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.plugin.gui.BuyGUI; 7 | import org.bukkit.command.CommandSender; 8 | 9 | import java.io.IOException; 10 | 11 | public class ReloadCommand extends SubCommand { 12 | public ReloadCommand(TebexPlugin platform) { 13 | super(platform, "reload", "tebex.admin"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandSender sender, String[] args) { 18 | TebexPlugin platform = getPlatform(); 19 | try { 20 | YamlDocument configYaml = platform.initPlatformConfig(); 21 | platform.loadServerPlatformConfig(configYaml); 22 | platform.reloadConfig(); 23 | platform.setBuyGUI(new BuyGUI(platform)); 24 | platform.refreshListings(); 25 | 26 | sender.sendMessage("§8[Tebex] §7Successfully reloaded."); 27 | } catch (IOException e) { 28 | sender.sendMessage("§8[Tebex] §cFailed to reload the plugin: Check Console."); 29 | throw new RuntimeException(e); 30 | } 31 | } 32 | 33 | @Override 34 | public String getDescription() { 35 | return "Reloads the plugin."; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/SubCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.tebex.plugin.TebexPlugin; 5 | import net.kyori.adventure.text.Component; 6 | 7 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 8 | 9 | public abstract class SubCommand { 10 | private final TebexPlugin platform; 11 | private final String name; 12 | private final String permission; 13 | 14 | public SubCommand(TebexPlugin platform, String name, String permission) { 15 | this.platform = platform; 16 | this.name = name; 17 | this.permission = permission; 18 | } 19 | 20 | public abstract void execute(final CommandSource sender, final String[] args); 21 | 22 | public TebexPlugin getPlatform() { 23 | return platform; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public String getPermission() { 31 | return permission; 32 | } 33 | 34 | public abstract String getDescription(); 35 | 36 | public String getUsage() { 37 | return ""; 38 | } 39 | 40 | protected Component getInvalidUsageMessage() { 41 | return legacySection().deserialize("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 42 | } 43 | } -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | 8 | import java.io.IOException; 9 | 10 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 11 | 12 | public class ReloadCommand extends SubCommand { 13 | public ReloadCommand(TebexPlugin platform) { 14 | super(platform, "reload", "tebex.admin"); 15 | } 16 | 17 | @Override 18 | public void execute(CommandSource sender, String[] args) { 19 | TebexPlugin platform = getPlatform(); 20 | try { 21 | YamlDocument configYaml = platform.initPlatformConfig(); 22 | platform.loadServerPlatformConfig(configYaml); 23 | platform.refreshListings(); 24 | sender.sendMessage(legacySection().deserialize("§8[Tebex] §7Successfully reloaded.")); 25 | } catch (IOException e) { 26 | sender.sendMessage(legacySection().deserialize("§8[Tebex] §cFailed to reload the plugin: Check Console.")); 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | @Override 32 | public String getDescription() { 33 | return "Reloads the plugin."; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/InfoCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class InfoCommand extends SubCommand { 8 | public InfoCommand(TebexPlugin platform) { 9 | super(platform, "info", "tebex.info"); 10 | } 11 | 12 | @Override 13 | public void execute(CommandSender sender, String[] args) { 14 | TebexPlugin platform = getPlatform(); 15 | 16 | if (platform.isSetup()) { 17 | sender.sendMessage("§b[Tebex] §7Information for this server:"); 18 | sender.sendMessage("§b[Tebex] §7" + platform.getStoreInformation().getServer().getName() + " for webstore " + platform.getStoreInformation().getStore().getName()); 19 | sender.sendMessage("§b[Tebex] §7Server prices are in " + platform.getStoreInformation().getStore().getCurrency().getIso4217()); 20 | sender.sendMessage("§b[Tebex] §7Webstore domain " + platform.getStoreInformation().getStore().getDomain()); 21 | } else { 22 | sender.sendMessage("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."); 23 | } 24 | } 25 | 26 | @Override 27 | public String getDescription() { 28 | return "Gets information about this server's connected store."; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/interceptor/LoggingInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.interceptor; 2 | 3 | import io.tebex.sdk.Tebex; 4 | import okhttp3.Interceptor; 5 | import okhttp3.MediaType; 6 | import okhttp3.Request; 7 | import okhttp3.RequestBody; 8 | import okhttp3.Response; 9 | import okio.Buffer; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.io.IOException; 13 | 14 | public class LoggingInterceptor implements Interceptor { 15 | @NotNull 16 | @Override 17 | public Response intercept(Chain chain) throws IOException { 18 | Request request = chain.request(); 19 | 20 | if (Tebex.get().getPlatformConfig().isVerbose()) { 21 | RequestBody requestBody = request.body(); 22 | String requestBodyString = null; 23 | 24 | if (requestBody != null) { 25 | Buffer buffer = new Buffer(); 26 | requestBody.writeTo(buffer); 27 | 28 | MediaType contentType = requestBody.contentType(); 29 | if (contentType != null) { 30 | requestBodyString = buffer.readString(contentType.charset()); 31 | } 32 | } 33 | 34 | Tebex.get().debug(String.format(" -> %1$s %2$s | %3$s", 35 | request.method(), request.url(), 36 | requestBodyString != null ? requestBodyString : "No body")); 37 | } 38 | 39 | return chain.proceed(request); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/GoalsCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import io.tebex.sdk.obj.CommunityGoal; 6 | import org.bukkit.command.CommandSender; 7 | 8 | import java.util.List; 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public class GoalsCommand extends SubCommand { 12 | public GoalsCommand(TebexPlugin platform) { 13 | super(platform, "goals", "tebex.goals"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandSender sender, String[] args) { 18 | TebexPlugin platform = getPlatform(); 19 | 20 | try { 21 | List goals = platform.getSDK().getCommunityGoals().get(); 22 | for (CommunityGoal goal: goals) { 23 | if (goal.getStatus() != CommunityGoal.Status.DISABLED) { 24 | sender.sendMessage("§b[Tebex] §7Community Goals: "); 25 | sender.sendMessage(String.format("§b[Tebex] §7- %s (%.2f/%.2f) [%s]", goal.getName(), goal.getCurrent(), goal.getTarget(), goal.getStatus())); 26 | } 27 | } 28 | } catch (InterruptedException | ExecutionException e) { 29 | sender.sendMessage("§b[Tebex] §7Unexpected response: " + e.getMessage()); 30 | } 31 | } 32 | 33 | @Override 34 | public String getDescription() { 35 | return "Shows active and completed community goals."; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.plugin.manager.CommandManager; 7 | 8 | import java.util.Comparator; 9 | 10 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 11 | 12 | public class HelpCommand extends SubCommand { 13 | private final CommandManager commandManager; 14 | 15 | public HelpCommand(TebexPlugin platform, CommandManager commandManager) { 16 | super(platform, "help", "tebex.admin"); 17 | this.commandManager = commandManager; 18 | } 19 | 20 | @Override 21 | public void execute(CommandSource sender, String[] args) { 22 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Plugin Commands:")); 23 | 24 | commandManager 25 | .getCommands() 26 | .values() 27 | .stream() 28 | .sorted(Comparator.comparing(SubCommand::getName)) 29 | .forEach(subCommand -> sender.sendMessage(legacySection().deserialize(" §8- §f/tebex " + subCommand.getName() + "§f" + (!subCommand.getUsage().isEmpty() ? " §3" + subCommand.getUsage() + " " : " ") + "§7§o(" + subCommand.getDescription() + ")"))); 30 | } 31 | 32 | @Override 33 | public String getDescription() { 34 | return "Shows this help page."; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.plugin.gui.BuyGUI; 7 | import io.tebex.plugin.manager.CommandManager; 8 | import org.bukkit.command.CommandSender; 9 | 10 | import java.io.IOException; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.Comparator; 14 | 15 | public class HelpCommand extends SubCommand { 16 | private final CommandManager commandManager; 17 | public HelpCommand(TebexPlugin platform, CommandManager commandManager) { 18 | super(platform, "help", "tebex.admin"); 19 | this.commandManager = commandManager; 20 | } 21 | 22 | @Override 23 | public void execute(CommandSender sender, String[] args) { 24 | sender.sendMessage("§b[Tebex] §7Plugin Commands:"); 25 | 26 | commandManager 27 | .getCommands() 28 | .values() 29 | .stream() 30 | .sorted(Comparator.comparing(SubCommand::getName)) 31 | .forEach(subCommand -> sender.sendMessage(" §8- §f/tebex " + subCommand.getName() + "§f" + (!subCommand.getUsage().isEmpty() ? " §3" + subCommand.getUsage() + " " : " ") + "§7§o(" + subCommand.getDescription() + ")")); 32 | } 33 | 34 | @Override 35 | public String getDescription() { 36 | return "Shows this help page."; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/minecraft-plugin-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Minecraft Plugin Bug Report 3 | about: Let us know about an issue with the Minecraft plugin. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Please include the following information with your report 11 | - Server type and version (ex. Spigot 1.18.2) 12 | - Tebex plugin version (ex. 2.0.1) 13 | - Description of the issue with any error messages, stack traces, or relevant logs. 14 | 15 | Delete this line and the notice below before submitting your issue. 16 | ## ⚠️ Do Not Submit Feature Requests as Issues 17 | 18 | Tebex has dedicated tracking of feature requests and user suggestions depending on the platform you're inquiring about. **If you have a suggestion, please use the appropriate link below to let us know.** 19 | 20 | Any suggestions created as issues will be closed and forwarded to our [Feedback Form](https://wkf.ms/45PQwfE). 21 | 22 | #### For Plugin Features 23 | - Relating to the Minecraft plugin, commands, its functionality, etc. 24 | - Please use the [feedback form](https://wkf.ms/45PQwfE) to tell us your feature request or suggestion. 25 | 26 | #### For Webstore Features 27 | - Relating to the webstore, packages, coupons, sales, etc on https://tebex.io/ 28 | - Please use our [product suggestions](https://suggestions.tebex.io/) site to submit your feature request. 29 | 30 | #### For Customer Support 31 | - We do not provide customer support via GitHub issues. 32 | - Please contact our support team at [support@tebex.io](mailto:support@tebex.io) 33 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.LiteralText; 9 | 10 | import java.io.IOException; 11 | 12 | public class ReloadCommand extends SubCommand { 13 | public ReloadCommand(TebexPlugin platform) { 14 | super(platform, "reload", "tebex.reload"); 15 | } 16 | 17 | @Override 18 | public void execute(CommandContext context) { 19 | final ServerCommandSource source = context.getSource(); 20 | 21 | TebexPlugin platform = getPlatform(); 22 | try { 23 | YamlDocument configYaml = platform.initPlatformConfig(); 24 | platform.loadServerPlatformConfig(configYaml); 25 | platform.refreshListings(); 26 | // platform.setBuyGUI(new BuyGUI(platform)); 27 | 28 | source.sendFeedback(new LiteralText("§8[Tebex] §7Successfully reloaded."), false); 29 | } catch (IOException e) { 30 | source.sendFeedback(new LiteralText("§8[Tebex] §cFailed to reload the plugin: Check Console."), false); 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | 35 | @Override 36 | public String getDescription() { 37 | return "Reloads the plugin."; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/ServerEvent.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class ServerEvent { 6 | @SerializedName("username_id") 7 | private final String uuid; 8 | 9 | private final String username; 10 | private final String ip; 11 | 12 | @SerializedName("event_type") 13 | private final String eventType; 14 | 15 | @SerializedName("event_date") 16 | private final String eventDate; 17 | 18 | public ServerEvent(String uuid, String username, String ip, ServerEventType eventType, String eventDate) { 19 | this.uuid = uuid; 20 | this.username = username; 21 | this.ip = ip; 22 | this.eventType = eventType.getName(); 23 | this.eventDate = eventDate; 24 | } 25 | 26 | public String getUuid() { 27 | return uuid; 28 | } 29 | 30 | public String getUsername() { 31 | return username; 32 | } 33 | 34 | public String getIp() { 35 | return ip; 36 | } 37 | 38 | public String getEventType() { 39 | return eventType; 40 | } 41 | 42 | public String getEventDate() { 43 | return eventDate; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "ServerEvent{" + 49 | "uuid='" + uuid + '\'' + 50 | ", username='" + username + '\'' + 51 | ", ip='" + ip + '\'' + 52 | ", eventType=" + eventType + 53 | ", eventDate='" + eventDate + '\'' + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.plugin.manager.CommandManager; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.LiteralText; 9 | import net.minecraft.text.Text; 10 | 11 | import java.util.Comparator; 12 | 13 | public class HelpCommand extends SubCommand { 14 | private final CommandManager commandManager; 15 | public HelpCommand(TebexPlugin platform, CommandManager commandManager) { 16 | super(platform, "help", "tebex.help"); 17 | this.commandManager = commandManager; 18 | } 19 | 20 | @Override 21 | public void execute(CommandContext context) { 22 | final ServerCommandSource source = context.getSource(); 23 | 24 | source.sendFeedback(new LiteralText("§b[Tebex] §7Plugin Commands:"), false); 25 | 26 | commandManager 27 | .getCommands() 28 | .stream() 29 | .sorted(Comparator.comparing(SubCommand::getName)) 30 | .forEach(subCommand -> source.sendFeedback(new LiteralText(" §8- §f/tebex " + subCommand.getName() + "§f" + (!subCommand.getUsage().isBlank() ? " §3" + subCommand.getUsage() + " " : " ") + "§7§o(" + subCommand.getDescription() + ")"), false)); 31 | } 32 | 33 | @Override 34 | public String getDescription() { 35 | return "Shows this help page."; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bukkit/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | group = rootProject.group 4 | version = rootProject.version 5 | 6 | dependencies { 7 | implementation(project(":sdk")) 8 | implementation("it.unimi.dsi:fastutil:8.5.6") 9 | implementation("com.github.cryptomorin:XSeries:9.3.1") { isTransitive = false } 10 | implementation("dev.triumphteam:triumph-gui:3.1.2") 11 | 12 | compileOnly("org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT") 13 | compileOnly("dev.dejvokep:boosted-yaml:1.3") 14 | compileOnly("me.clip:placeholderapi:2.11.3") 15 | } 16 | 17 | tasks.named("shadowJar", ShadowJar::class.java) { 18 | configurations = listOf(project.configurations.runtimeClasspath.get()) 19 | 20 | relocate("it.unimi", "io.tebex.plugin.libs.fastutil") 21 | relocate("okhttp3", "io.tebex.plugin.libs.okhttp3") 22 | relocate("net.kyori", "io.tebex.plugin.libs.kyori") 23 | relocate("okio", "io.tebex.plugin.libs.okio") 24 | relocate("dev.dejvokep.boostedyaml", "io.tebex.plugin.libs.boostedyaml") 25 | relocate("org.jetbrains.annotations", "io.tebex.plugin.libs.jetbrains") 26 | relocate("kotlin", "io.tebex.plugin.libs.kotlin") 27 | relocate("com.github.benmanes.caffeine", "io.tebex.plugin.libs.caffeine") 28 | relocate("com.google.gson", "io.tebex.plugin.libs.gson") 29 | minimize() 30 | } 31 | 32 | tasks.register("copyToServer", Copy::class.java) { 33 | from(project.tasks.named("shadowJar").get().outputs) 34 | into("/Users/charlie/Documents/MCServers/MCServer/plugins") 35 | 36 | // rely on the shadowJar task to build the jar 37 | dependsOn("shadowJar") 38 | } 39 | -------------------------------------------------------------------------------- /velocity/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("net.kyori.blossom") version "2.1.0" 3 | id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7" 4 | } 5 | 6 | sourceSets { 7 | main { 8 | blossom { 9 | javaSources { 10 | property("version", rootProject.version.toString()) 11 | } 12 | } 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation(project(":sdk")) 18 | implementation("net.sf.trove4j:trove4j:3.0.3") // Add trove4j dependency 19 | 20 | compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") 21 | annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") 22 | compileOnly("dev.dejvokep:boosted-yaml:1.3") 23 | } 24 | 25 | tasks { 26 | compileJava { 27 | options.release.set(17) 28 | options.encoding = Charsets.UTF_8.name() 29 | } 30 | shadowJar { 31 | configurations = listOf(project.configurations.runtimeClasspath.get()) 32 | 33 | relocate("gnu.trove4j", "net.analyse.plugin.libs.trove4j") // Relocate trove4j 34 | relocate("okhttp3", "net.analyse.plugin.libs.okhttp3") // Relocate okhttp 35 | relocate("okio", "net.analyse.plugin.libs.okio") // Relocate okio (okhttp dependency) 36 | relocate("dev.dejvokep.boostedyaml", "net.analyse.plugin.libs.boostedyaml") // Relocate boostedyaml 37 | relocate("org.jetbrains.annotations", "net.analyse.plugin.libs.jetbrains") // Relocate jetbrains 38 | relocate("kotlin", "net.analyse.plugin.libs.kotlin") // Relocate jetbrains 39 | minimize() 40 | } 41 | } 42 | 43 | java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 44 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/ReportCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 7 | import org.bukkit.command.CommandSender; 8 | 9 | public class ReportCommand extends SubCommand { 10 | public ReportCommand(TebexPlugin platform) { 11 | super(platform, "report", "tebex.report"); 12 | } 13 | 14 | @Override 15 | public void execute(CommandSender sender, String[] args) { 16 | TebexPlugin platform = getPlatform(); 17 | 18 | ServerPlatformConfig config = platform.getPlatformConfig(); 19 | YamlDocument configFile = config.getYamlDocument(); 20 | 21 | if (args.length < 1) { 22 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 23 | return; 24 | } 25 | 26 | String message = String.join(" ", args); 27 | if (message.isEmpty()) { 28 | sender.sendMessage("§b[Tebex] §7A message is required for your report."); 29 | } else { 30 | sender.sendMessage("§b[Tebex] §7Sending your report to Tebex..."); 31 | platform.sendTriageEvent(new Error("User reported error in-game: " + message)); 32 | } 33 | } 34 | 35 | @Override 36 | public String getDescription() { 37 | return "Reports a problem to Tebex along with information about your webstore, server, etc."; 38 | } 39 | 40 | @Override 41 | public String getUsage() { 42 | return "''"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/manager/CommandManager.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.manager; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Maps; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.plugin.command.TebexCommand; 8 | import io.tebex.plugin.command.sub.ForceCheckCommand; 9 | import io.tebex.plugin.command.sub.HelpCommand; 10 | import io.tebex.plugin.command.sub.ReloadCommand; 11 | import io.tebex.plugin.command.sub.SecretCommand; 12 | import net.md_5.bungee.api.plugin.PluginManager; 13 | 14 | import java.util.Map; 15 | 16 | public class CommandManager { 17 | private final TebexPlugin platform; 18 | private final Map commands; 19 | 20 | public CommandManager(TebexPlugin platform) { 21 | this.platform = platform; 22 | this.commands = Maps.newHashMap(); 23 | } 24 | 25 | public void register() { 26 | ImmutableList.of( 27 | new SecretCommand(platform), 28 | new ReloadCommand(platform), 29 | new ForceCheckCommand(platform), 30 | new HelpCommand(platform, this) 31 | ).forEach(command -> { 32 | commands.put(command.getName(), command); 33 | }); 34 | 35 | TebexCommand tebexCommand = new TebexCommand(this, "tebex"); 36 | 37 | PluginManager pluginManager = platform.getProxy().getPluginManager(); 38 | pluginManager.registerCommand(platform, tebexCommand); 39 | } 40 | 41 | public Map getCommands() { 42 | return commands; 43 | } 44 | 45 | public TebexPlugin getPlatform() { 46 | return platform; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/placeholder/PlaceholderManager.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.placeholder; 2 | 3 | import io.tebex.sdk.obj.QueuedPlayer; 4 | import io.tebex.sdk.placeholder.defaults.NamePlaceholder; 5 | import io.tebex.sdk.placeholder.defaults.UuidPlaceholder; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.Pattern; 10 | 11 | public class PlaceholderManager { 12 | private static final Pattern USERNAME_PATTERN = Pattern.compile("[{\\(<\\[](name|player|username)[}\\)>\\]]", Pattern.CASE_INSENSITIVE); 13 | private static final Pattern UNIQUE_ID_PATTERN = Pattern.compile("[{\\(<\\[](uuid|id)[}\\)>\\]]", Pattern.CASE_INSENSITIVE); 14 | 15 | private final List placeholders; 16 | 17 | 18 | public PlaceholderManager() { 19 | this.placeholders = new ArrayList<>(); 20 | } 21 | 22 | public void register(Placeholder placeholder) { 23 | if(this.placeholders.contains(placeholder)) 24 | throw new IllegalArgumentException("Placeholder already registered"); 25 | 26 | this.placeholders.add(placeholder); 27 | } 28 | 29 | public void registerDefaults() { 30 | register(new NamePlaceholder(this)); 31 | register(new UuidPlaceholder(this)); 32 | } 33 | 34 | public String handlePlaceholders(QueuedPlayer player, String command) { 35 | for (Placeholder placeholder : this.placeholders) { 36 | command = placeholder.handle(player, command); 37 | } 38 | 39 | return command; 40 | } 41 | 42 | public Pattern getUsernameRegex() { 43 | return USERNAME_PATTERN; 44 | } 45 | 46 | public Pattern getUniqueIdRegex() { 47 | return UNIQUE_ID_PATTERN; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/GoalsCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.obj.CommunityGoal; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.LiteralText; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.ExecutionException; 12 | 13 | public class GoalsCommand extends SubCommand { 14 | public GoalsCommand(TebexPlugin platform) { 15 | super(platform, "goals", "tebex.goals"); 16 | } 17 | 18 | @Override 19 | public void execute(CommandContext sender) { 20 | TebexPlugin platform = getPlatform(); 21 | 22 | try { 23 | List goals = platform.getSDK().getCommunityGoals().get(); 24 | for (CommunityGoal goal: goals) { 25 | if (goal.getStatus() != CommunityGoal.Status.DISABLED) { 26 | sender.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Community Goals: "), false); 27 | sender.getSource().sendFeedback(new LiteralText(String.format("§b[Tebex] §7- %s (%.2f/%.2f) [%s]", goal.getName(), goal.getCurrent(), goal.getTarget(), goal.getStatus())), false); 28 | } 29 | } 30 | } catch (InterruptedException | ExecutionException e) { 31 | sender.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Unexpected response: " + e.getMessage()), false); 32 | } 33 | } 34 | 35 | @Override 36 | public String getDescription() { 37 | return "Shows active and completed community goals."; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/ReportCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 8 | import net.minecraft.server.command.ServerCommandSource; 9 | import net.minecraft.text.LiteralText; 10 | 11 | public class ReportCommand extends SubCommand { 12 | public ReportCommand(TebexPlugin platform) { 13 | super(platform, "report", "tebex.report"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandContext context) { 18 | TebexPlugin platform = getPlatform(); 19 | 20 | ServerPlatformConfig config = platform.getPlatformConfig(); 21 | YamlDocument configFile = config.getYamlDocument(); 22 | 23 | String message = context.getArgument("message", String.class); 24 | 25 | if (message.isBlank()) { 26 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7A message is required for your report."), false); 27 | } else { 28 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Sending your report to Tebex..."), false); 29 | platform.sendTriageEvent(new Error("User reported error in-game: " + message)); 30 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Report sent successfully."), false); 31 | } 32 | } 33 | 34 | @Override 35 | public String getDescription() { 36 | return "Reports a problem to Tebex along with information about your webstore, server, etc."; 37 | } 38 | 39 | @Override 40 | public String getUsage() { 41 | return "''"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/CheckoutCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import io.tebex.sdk.obj.CheckoutUrl; 6 | import org.bukkit.command.CommandSender; 7 | 8 | import java.util.concurrent.ExecutionException; 9 | 10 | public class CheckoutCommand extends SubCommand { 11 | public CheckoutCommand(TebexPlugin platform) { 12 | super(platform, "checkout", "tebex.checkout"); 13 | } 14 | 15 | @Override 16 | public void execute(CommandSender sender, String[] args) { 17 | TebexPlugin platform = getPlatform(); 18 | 19 | if (!platform.isSetup()) { 20 | sender.sendMessage("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."); 21 | return; 22 | } 23 | 24 | if (args.length == 0) { 25 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 26 | return; 27 | } 28 | 29 | try { 30 | int packageId = Integer.parseInt(args[0]); 31 | CheckoutUrl checkoutUrl = platform.getSDK().createCheckoutUrl(packageId, sender.getName()).get(); 32 | sender.sendMessage("§b[Tebex] §7Checkout started! Click here to complete payment: " + checkoutUrl.getUrl()); 33 | } catch (InterruptedException|ExecutionException e) { 34 | sender.sendMessage("§b[Tebex] §7Failed to get checkout link for package, check package ID: " + e.getMessage()); 35 | } 36 | } 37 | 38 | @Override 39 | public String getDescription() { 40 | return "Creates payment link for a package"; 41 | } 42 | 43 | @Override 44 | public String getUsage() { 45 | return ""; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sdk/src/main/resources/platform/server/config.yml: -------------------------------------------------------------------------------- 1 | # Tebex v${version} - https://tebex.io 2 | # Check out https://tebex.io/docs if you need help. 3 | 4 | # Buy Command 5 | # This is the command that players will use to open the Tebex GUI. 6 | buy-command: 7 | enabled: true 8 | name: buy 9 | 10 | # Whether to check for updates or not. 11 | check-for-updates: true 12 | 13 | # Automatic reporting of exceptions to Tebex 14 | auto-report-enabled: true 15 | 16 | # Debug Mode 17 | verbose: false 18 | 19 | # GUI Settings 20 | gui: 21 | menu: 22 | home: 23 | title: "Server Shop" 24 | rows: 3 25 | category: 26 | title: "Viewing %category%" 27 | rows: 3 28 | sub-category: 29 | title: "Viewing %sub_category% (%category%)" 30 | rows: 1 31 | item: 32 | back: 33 | name: "&cBack" 34 | material: COMPASS 35 | lore: [] 36 | category: 37 | name: "&b%category%" 38 | material: CHEST 39 | lore: 40 | - "&7Click to view packages!" 41 | package-sale: 42 | name: "&b%package_name%" 43 | material: PAPER 44 | lore: 45 | - "&7Price: &f%package_currency%%package_sale_price% &7(&m%package_currency%%package_price%&r&7)" 46 | - "&7" 47 | - "&7Click to purchase!" 48 | package: 49 | name: "&b%package_name%" 50 | material: PAPER 51 | lore: 52 | - "&7Price: &f%package_currency%%package_price%" 53 | - "&7" 54 | - "&7Click to purchase!" 55 | 56 | # These settings are exclusive to this server. 57 | # Do not share these credentials with anyone. 58 | server: 59 | # Proxy Mode 60 | # If you are using BungeeCord/Velocity, set this to true. 61 | proxy: false 62 | 63 | # Secret Key 64 | # This is used to identify your server. 65 | secret-key: '' 66 | 67 | # Configuration Version 68 | # !! Do not change this value !! 69 | config-version: 2 -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/manager/CommandManager.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.manager; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Maps; 5 | import com.velocitypowered.api.command.CommandMeta; 6 | import com.velocitypowered.api.command.SimpleCommand; 7 | import io.tebex.plugin.TebexPlugin; 8 | import io.tebex.plugin.command.SubCommand; 9 | import io.tebex.plugin.command.TebexCommand; 10 | import io.tebex.plugin.command.sub.*; 11 | 12 | import java.util.Map; 13 | 14 | public class CommandManager { 15 | private final TebexPlugin platform; 16 | private final Map commands; 17 | 18 | public CommandManager(TebexPlugin platform) { 19 | this.platform = platform; 20 | this.commands = Maps.newHashMap(); 21 | } 22 | 23 | public void register() { 24 | ImmutableList.of( 25 | new SecretCommand(platform), 26 | new ReloadCommand(platform), 27 | new ForceCheckCommand(platform), 28 | new HelpCommand(platform, this), 29 | new BanCommand(platform), 30 | new DebugCommand(platform) 31 | ).forEach(command -> commands.put(command.getName(), command)); 32 | 33 | SimpleCommand tebexCommand = new TebexCommand(this); 34 | 35 | com.velocitypowered.api.command.CommandManager commandManager = platform.getProxy().getCommandManager(); 36 | 37 | CommandMeta commandMeta = commandManager.metaBuilder("tebex") 38 | .aliases("tbx", "buycraft") 39 | .plugin(platform) 40 | .build(); 41 | 42 | platform.getProxy().getCommandManager().register(commandMeta, tebexCommand); 43 | } 44 | 45 | public Map getCommands() { 46 | return commands; 47 | } 48 | 49 | public TebexPlugin getPlatform() { 50 | return platform; 51 | } 52 | } -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/InfoCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.SDK; 8 | import io.tebex.sdk.exception.ServerNotFoundException; 9 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 10 | import net.minecraft.server.command.ServerCommandSource; 11 | import net.minecraft.text.LiteralText; 12 | 13 | import java.io.IOException; 14 | 15 | public class InfoCommand extends SubCommand { 16 | public InfoCommand(TebexPlugin platform) { 17 | super(platform, "info", "tebex.info"); 18 | } 19 | 20 | @Override 21 | public void execute(CommandContext context) { 22 | final ServerCommandSource source = context.getSource(); 23 | TebexPlugin platform = getPlatform(); 24 | 25 | if (platform.isSetup()) { 26 | source.sendFeedback(new LiteralText("§b[Tebex] §7Information for this server:"), false); 27 | source.sendFeedback(new LiteralText("§b[Tebex] §7" + platform.getStoreInformation().getServer().getName() + " for webstore " + platform.getStoreInformation().getStore().getName()), false); 28 | source.sendFeedback(new LiteralText("§b[Tebex] §7Server prices are in " + platform.getStoreInformation().getStore().getCurrency().getIso4217()), false); 29 | source.sendFeedback(new LiteralText("§b[Tebex] §7Webstore domain " + platform.getStoreInformation().getStore().getDomain()), false); 30 | } else { 31 | source.sendFeedback(new LiteralText("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."), false); 32 | } 33 | } 34 | 35 | @Override 36 | public String getDescription() { 37 | return "Gets information about this server's connected store."; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/CheckoutCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.obj.CheckoutUrl; 8 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 9 | import net.minecraft.server.command.ServerCommandSource; 10 | import net.minecraft.text.LiteralText; 11 | 12 | import java.util.concurrent.ExecutionException; 13 | 14 | public class CheckoutCommand extends SubCommand { 15 | public CheckoutCommand(TebexPlugin platform) { 16 | super(platform, "checkout", "tebex.checkout"); 17 | } 18 | 19 | @Override 20 | public void execute(CommandContext context) { 21 | TebexPlugin platform = getPlatform(); 22 | 23 | if (!platform.isSetup()) { 24 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."), false); 25 | return; 26 | } 27 | 28 | Integer packageId = context.getArgument("packageId", Integer.class); 29 | try { 30 | CheckoutUrl checkoutUrl = platform.getSDK().createCheckoutUrl(packageId, context.getSource().getName()).get(); 31 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Checkout started! Click here to complete payment: " + checkoutUrl.getUrl()), false); 32 | } catch (InterruptedException|ExecutionException e) { 33 | context.getSource().sendError(new LiteralText("§b[Tebex] §7Failed to get checkout link for package: " + e.getMessage())); 34 | } 35 | } 36 | 37 | @Override 38 | public String getDescription() { 39 | return "Creates payment link for a package"; 40 | } 41 | 42 | @Override 43 | public String getUsage() { 44 | return ""; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.ZoneId; 5 | import java.time.ZonedDateTime; 6 | import java.time.format.DateTimeFormatter; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | 12 | public class StringUtil { 13 | private static final DateTimeFormatter LEGACY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 14 | private static final DateTimeFormatter MODERN_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx"); 15 | 16 | private static final HashSet TRUTHY_STRINGS = new HashSet<>(Arrays.asList("true", "yes", "on", "1", "enabled", "enable", "cap")); 17 | 18 | private static final HashSet FALSY_STRINGS = new HashSet<>(Arrays.asList("false", "no", "off", "0", "disabled", "disable", "nocap")); 19 | 20 | public static String pluralise(int count, String singular, String plural) { 21 | return count == 1 ? singular : plural; 22 | } 23 | public static String pluralise(int count, String word) { 24 | return pluralise(count, word, word + "s"); 25 | } 26 | 27 | public static ZonedDateTime toLegacyDate(String date) { 28 | return LocalDateTime.parse(date, LEGACY_FORMATTER).atZone(ZoneId.of("UTC")); 29 | } 30 | 31 | public static ZonedDateTime toModernDate(String date) { 32 | return LocalDateTime.parse(date, MODERN_FORMATTER).atZone(ZoneId.of("UTC")); 33 | } 34 | 35 | public static boolean isTruthy(String input) 36 | { 37 | if (input == null || input.isEmpty()) 38 | { 39 | return false; 40 | } 41 | 42 | return TRUTHY_STRINGS.contains(input.trim().toLowerCase()); 43 | } 44 | 45 | public static boolean isFalsy(String input) 46 | { 47 | if (input == null || input.isEmpty()) 48 | { 49 | return true; 50 | } 51 | 52 | return FALSY_STRINGS.contains(input.trim().toLowerCase()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/manager/CommandManager.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.manager; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Maps; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.plugin.command.TebexCommand; 8 | import io.tebex.plugin.command.sub.*; 9 | import org.bukkit.command.PluginCommand; 10 | 11 | import java.util.Map; 12 | 13 | public class CommandManager { 14 | private final TebexPlugin platform; 15 | private final Map commands; 16 | 17 | public CommandManager(TebexPlugin platform) { 18 | this.platform = platform; 19 | this.commands = Maps.newHashMap(); 20 | } 21 | 22 | public void register() { 23 | ImmutableList.of( 24 | new SecretCommand(platform), 25 | new ReloadCommand(platform), 26 | new ForceCheckCommand(platform), 27 | new HelpCommand(platform, this), 28 | new BanCommand(platform), 29 | new CheckoutCommand(platform), 30 | new DebugCommand(platform), 31 | new InfoCommand(platform), 32 | new LookupCommand(platform), 33 | new ReportCommand(platform), 34 | new SendLinkCommand(platform), 35 | new GoalsCommand(platform) 36 | ).forEach(command -> { 37 | commands.put(command.getName(), command); 38 | }); 39 | 40 | TebexCommand tebexCommand = new TebexCommand(this); 41 | PluginCommand pluginCommand = platform.getCommand("tebex"); 42 | 43 | if(pluginCommand == null) { 44 | throw new RuntimeException("Tebex command not found."); 45 | } 46 | 47 | pluginCommand.setExecutor(tebexCommand); 48 | pluginCommand.setTabCompleter(tebexCommand); 49 | } 50 | 51 | public Map getCommands() { 52 | return commands; 53 | } 54 | 55 | public TebexPlugin getPlatform() { 56 | return platform; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/TebexCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.tebex.plugin.manager.CommandManager; 5 | import net.md_5.bungee.api.CommandSender; 6 | import net.md_5.bungee.api.plugin.Command; 7 | 8 | import java.util.Arrays; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | public class TebexCommand extends Command { 13 | private CommandManager commandManager; 14 | 15 | public TebexCommand(CommandManager commandManager, String name) { 16 | super(name); 17 | this.commandManager = commandManager; 18 | } 19 | 20 | @Override 21 | public void execute(CommandSender sender, String[] args) { 22 | if(args.length == 0) { 23 | sender.sendMessage("§8[Tebex] §7Welcome to Tebex!"); 24 | sender.sendMessage("§8[Tebex] §7This server is running version §fv" + commandManager.getPlatform().getDescription().getVersion() + "§7."); 25 | return; 26 | } 27 | 28 | Map commands = commandManager.getCommands(); 29 | if(! commands.containsKey(args[0].toLowerCase())) { 30 | sender.sendMessage("§8[Tebex] §7Unknown command."); 31 | return; 32 | } 33 | 34 | final SubCommand subCommand = commands.get(args[0].toLowerCase()); 35 | if (! sender.hasPermission(subCommand.getPermission())) { 36 | sender.sendMessage("§b[Tebex] §7You do not have access to that command."); 37 | return; 38 | } 39 | 40 | subCommand.execute(sender, Arrays.copyOfRange(args, 1, args.length)); 41 | } 42 | 43 | public Iterable onTabComplete(CommandSender sender, String[] args) { 44 | if(args.length == 1) { 45 | return commandManager.getCommands() 46 | .keySet() 47 | .stream() 48 | .filter(s -> s.startsWith(args[0])) 49 | .collect(Collectors.toList()); 50 | } 51 | 52 | return ImmutableList.of(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/SendLinkCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.obj.CheckoutUrl; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.server.network.ServerPlayerEntity; 9 | import net.minecraft.text.LiteralText; 10 | 11 | import java.util.concurrent.ExecutionException; 12 | 13 | public class SendLinkCommand extends SubCommand { 14 | public SendLinkCommand(TebexPlugin platform) { 15 | super(platform, "sendlink", "tebex.sendlink"); 16 | } 17 | 18 | @Override 19 | public void execute(CommandContext context) { 20 | TebexPlugin platform = getPlatform(); 21 | 22 | String username = context.getArgument("username", String.class).trim(); 23 | Integer packageId = context.getArgument("packageId", Integer.class); 24 | 25 | ServerPlayerEntity player = context.getSource().getMinecraftServer().getPlayerManager().getPlayer(username); 26 | if (player == null) { 27 | context.getSource().sendError(new LiteralText("§b[Tebex] §7Could not find a player with that name on the server.")); 28 | return; 29 | } 30 | 31 | try { 32 | CheckoutUrl checkoutUrl = platform.getSDK().createCheckoutUrl(packageId, username).get(); 33 | player.sendMessage(new LiteralText("§b[Tebex] §7A checkout link has been created for you. Click here to complete payment: " + checkoutUrl.getUrl()), false); 34 | } catch (InterruptedException|ExecutionException e) { 35 | context.getSource().sendError(new LiteralText("§b[Tebex] §7Failed to get checkout link for package: " + e.getMessage())); 36 | } 37 | } 38 | 39 | @Override 40 | public String getDescription() { 41 | return "Creates payment link for a package and sends it to a player"; 42 | } 43 | 44 | @Override 45 | public String getUsage() { 46 | return " "; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/SendLinkCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import io.tebex.sdk.obj.CheckoutUrl; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public class SendLinkCommand extends SubCommand { 12 | public SendLinkCommand(TebexPlugin platform) { 13 | super(platform, "sendlink", "tebex.sendlink"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandSender sender, String[] args) { 18 | TebexPlugin platform = getPlatform(); 19 | 20 | if (args.length != 2) { 21 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 22 | return; 23 | } 24 | 25 | String username = args[0].trim(); 26 | try { 27 | Player player = sender.getServer().getPlayer(username); 28 | if (player == null) { 29 | sender.sendMessage("§b[Tebex] §7Could not find a player with that name on the server."); 30 | return; 31 | } 32 | 33 | int packageId = Integer.parseInt(args[1]); 34 | CheckoutUrl checkoutUrl = platform.getSDK().createCheckoutUrl(packageId, username).get(); 35 | player.sendMessage("§b[Tebex] §7A checkout link has been created for you. Click here to complete payment: " + checkoutUrl.getUrl()); 36 | } catch (InterruptedException|ExecutionException e) { 37 | sender.sendMessage("§b[Tebex] §7Failed to get checkout link for package: " + e.getMessage()); 38 | } catch (NumberFormatException e) { 39 | sender.sendMessage("§b[Tebex] §7Package ID must be a number."); 40 | } 41 | } 42 | 43 | @Override 44 | public String getDescription() { 45 | return "Creates payment link for a package and sends it to a player"; 46 | } 47 | 48 | @Override 49 | public String getUsage() { 50 | return " "; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/DebugCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 7 | import io.tebex.sdk.util.StringUtil; 8 | import net.kyori.adventure.text.Component; 9 | import org.bukkit.command.CommandSender; 10 | 11 | import java.io.IOException; 12 | 13 | public class DebugCommand extends SubCommand { 14 | public DebugCommand(TebexPlugin platform) { 15 | super(platform, "debug", "tebex.debug"); 16 | } 17 | 18 | @Override 19 | public void execute(CommandSender sender, String[] args) { 20 | TebexPlugin platform = getPlatform(); 21 | 22 | ServerPlatformConfig config = platform.getPlatformConfig(); 23 | YamlDocument configFile = config.getYamlDocument(); 24 | 25 | if (args.length != 1) { 26 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 27 | return; 28 | } 29 | 30 | if (StringUtil.isTruthy(args[0])) { 31 | sender.sendMessage("§b[Tebex] §7Debug mode enabled."); 32 | config.setVerbose(true); 33 | configFile.set("verbose", true); 34 | } else if (StringUtil.isFalsy(args[0])) { 35 | sender.sendMessage("§b[Tebex] §7Debug mode disabled."); 36 | config.setVerbose(false); 37 | configFile.set("verbose", false); 38 | } else { 39 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 40 | } 41 | 42 | try { 43 | configFile.save(); 44 | } catch (IOException e) { 45 | sender.sendMessage("§b[Tebex] §7Failed to save configuration file."); 46 | } 47 | } 48 | 49 | @Override 50 | public String getDescription() { 51 | return "Enables more verbose logging."; 52 | } 53 | 54 | @Override 55 | public String getUsage() { 56 | return ""; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/Pagination.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class Pagination { 6 | private final int totalResults; 7 | private final int currentPage; 8 | private final int lastPage; 9 | private final String previous; 10 | private final String next; 11 | 12 | public Pagination(int totalResults, int currentPage, int lastPage, String previous, String next) { 13 | this.totalResults = totalResults; 14 | this.currentPage = currentPage; 15 | this.lastPage = lastPage; 16 | this.previous = previous; 17 | this.next = next; 18 | } 19 | 20 | public int getTotalResults() { 21 | return totalResults; 22 | } 23 | 24 | public int getCurrentPage() { 25 | return currentPage; 26 | } 27 | 28 | public int getLastPage() { 29 | return lastPage; 30 | } 31 | 32 | public boolean hasPrevious() { 33 | return previous != null; 34 | } 35 | 36 | public boolean hasNext() { 37 | return next != null; 38 | } 39 | 40 | public String getPrevious() { 41 | return previous; 42 | } 43 | 44 | public String getNext() { 45 | return next; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "Pagination{" + 51 | "totalResults=" + totalResults + 52 | ", currentPage=" + currentPage + 53 | ", lastPage=" + lastPage + 54 | ", previous='" + previous + '\'' + 55 | ", next='" + next + '\'' + 56 | '}'; 57 | } 58 | 59 | public static Pagination fromJsonObject(JsonObject jsonObject) { 60 | return new Pagination( 61 | jsonObject.get("totalResults").getAsInt(), 62 | jsonObject.get("currentPage").getAsInt(), 63 | jsonObject.get("lastPage").getAsInt(), 64 | !jsonObject.get("previous").isJsonNull() ? jsonObject.get("previous").getAsString() : null, 65 | !jsonObject.get("next").isJsonNull() ? jsonObject.get("next").getAsString() : null 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/BanCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import org.bukkit.command.CommandSender; 6 | 7 | import java.util.concurrent.ExecutionException; 8 | 9 | public class BanCommand extends SubCommand { 10 | public BanCommand(TebexPlugin platform) { 11 | super(platform, "ban", "tebex.ban"); 12 | } 13 | 14 | @Override 15 | public void execute(CommandSender sender, String[] args) { 16 | TebexPlugin platform = getPlatform(); 17 | 18 | if (args.length < 1) { // require username at minimum 19 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 20 | return; 21 | } 22 | 23 | String playerName = args[0]; 24 | String reason = ""; 25 | String ip = ""; 26 | 27 | if (args.length > 1) { // second param provided 28 | reason = args[1]; 29 | } 30 | if (args.length > 2) { // third param provided 31 | ip = args[2]; 32 | } 33 | 34 | if (!platform.isSetup()) { 35 | sender.sendMessage("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."); 36 | return; 37 | } 38 | 39 | try { 40 | boolean success = platform.getSDK().createBan(playerName, ip, reason).get(); 41 | if (success) { 42 | sender.sendMessage("§b[Tebex] §7Player banned successfully."); 43 | } else { 44 | sender.sendMessage("§b[Tebex] §7Failed to ban player."); 45 | } 46 | } catch (InterruptedException | ExecutionException e) { 47 | sender.sendMessage("§b[Tebex] §7Error while banning player: " + e.getMessage()); 48 | } 49 | } 50 | 51 | @Override 52 | public String getDescription() { 53 | return "Bans a player from using the webstore. Unbans can only be made via the web panel."; 54 | } 55 | 56 | @Override 57 | public String getUsage() { 58 | return " "; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/TebexCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.tebex.plugin.manager.CommandManager; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.command.TabExecutor; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | public class TebexCommand implements TabExecutor { 15 | private CommandManager commandManager; 16 | 17 | public TebexCommand(CommandManager commandManager) { 18 | this.commandManager = commandManager; 19 | } 20 | 21 | @Override 22 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 23 | if(args.length == 0) { 24 | sender.sendMessage("§8[Tebex] §7Welcome to Tebex!"); 25 | sender.sendMessage("§8[Tebex] §7This server is running version §fv" + commandManager.getPlatform().getDescription().getVersion() + "§7."); 26 | return true; 27 | } 28 | 29 | Map commands = commandManager.getCommands(); 30 | if(! commands.containsKey(args[0].toLowerCase())) { 31 | sender.sendMessage("§8[Tebex] §7Unknown command."); 32 | return true; 33 | } 34 | 35 | final SubCommand subCommand = commands.get(args[0].toLowerCase()); 36 | if (! sender.hasPermission(subCommand.getPermission())) { 37 | sender.sendMessage("§b[Tebex] §7You do not have access to that command."); 38 | return true; 39 | } 40 | 41 | subCommand.execute(sender, Arrays.copyOfRange(args, 1, args.length)); 42 | return true; 43 | } 44 | 45 | @Override 46 | public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { 47 | if(args.length == 1) { 48 | return commandManager.getCommands() 49 | .keySet() 50 | .stream() 51 | .filter(s -> s.startsWith(args[0])) 52 | .collect(Collectors.toList()); 53 | } 54 | 55 | return ImmutableList.of(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/DebugCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.platform.config.ProxyPlatformConfig; 8 | import io.tebex.sdk.util.StringUtil; 9 | import net.kyori.adventure.text.Component; 10 | 11 | import java.io.IOException; 12 | import java.util.Set; 13 | 14 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 15 | 16 | public class DebugCommand extends SubCommand { 17 | public DebugCommand(TebexPlugin platform) { 18 | super(platform, "debug", "tebex.debug"); 19 | } 20 | 21 | @Override 22 | public void execute(CommandSource sender, String[] args) { 23 | if (args.length != 1) { // require option 24 | sender.sendMessage(getInvalidUsageMessage()); 25 | return; 26 | } 27 | 28 | TebexPlugin platform = getPlatform(); 29 | 30 | ProxyPlatformConfig config = platform.getPlatformConfig(); 31 | YamlDocument configFile = config.getYamlDocument(); 32 | 33 | if (StringUtil.isTruthy(args[0])) { 34 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Debug mode enabled.")); 35 | config.setVerbose(true); 36 | configFile.set("verbose", true); 37 | } else if (StringUtil.isFalsy(args[0])){ 38 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Debug mode disabled.")); 39 | config.setVerbose(false); 40 | configFile.set("verbose", false); 41 | } else { 42 | sender.sendMessage(getInvalidUsageMessage()); 43 | } 44 | 45 | try { 46 | configFile.save(); 47 | } catch (IOException e) { 48 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Failed to save configuration file.")); 49 | } 50 | } 51 | 52 | @Override 53 | public String getDescription() { 54 | return "Enables more verbose logging."; 55 | } 56 | 57 | @Override 58 | public String getUsage() { 59 | return ""; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/BanCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import net.minecraft.server.command.ServerCommandSource; 7 | import net.minecraft.text.LiteralText; 8 | 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public class BanCommand extends SubCommand { 12 | public BanCommand(TebexPlugin platform) { 13 | super(platform, "ban", "tebex.ban"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandContext context) { 18 | final ServerCommandSource source = context.getSource(); 19 | TebexPlugin platform = getPlatform(); 20 | 21 | String playerName = context.getArgument("playerName", String.class); 22 | String reason = ""; 23 | String ip = ""; 24 | 25 | try { 26 | reason = context.getArgument("reason", String.class); 27 | ip = context.getArgument("ip", String.class); 28 | } catch (IllegalArgumentException ignored) {} 29 | 30 | if (!platform.isSetup()) { 31 | source.sendFeedback(new LiteralText("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."), false); 32 | return; 33 | } 34 | 35 | try { 36 | boolean success = platform.getSDK().createBan(playerName, ip, reason).get(); 37 | if (success) { 38 | source.sendFeedback(new LiteralText("§b[Tebex] §7Player banned successfully."), false); 39 | } else { 40 | source.sendFeedback(new LiteralText("§b[Tebex] §7Failed to ban player."), false); 41 | } 42 | } catch (InterruptedException | ExecutionException e) { 43 | source.sendFeedback(new LiteralText("§b[Tebex] §7Error while banning player: " + e.getMessage()), false); 44 | } 45 | } 46 | 47 | @Override 48 | public String getDescription() { 49 | return "Bans a player from using the webstore. Unbans can only be made via the web panel."; 50 | } 51 | 52 | @Override 53 | public String getUsage() { 54 | return " "; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/SubCategory.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | public class SubCategory implements ICategory { 9 | private final int id; 10 | private final int order; 11 | private final String name; 12 | private final String guiItem; 13 | private final Category parentCategory; 14 | private final List categoryPackages; 15 | 16 | public SubCategory(int id, int order, String name, String guiItem, Category parentCategory, List categoryPackages) { 17 | this.id = id; 18 | this.order = order; 19 | this.name = name; 20 | this.guiItem = guiItem; 21 | this.parentCategory = parentCategory; 22 | this.categoryPackages = categoryPackages; 23 | } 24 | 25 | @Override 26 | public int getId() { 27 | return id; 28 | } 29 | 30 | @Override 31 | public int getOrder() { 32 | return order; 33 | } 34 | 35 | @Override 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | @Override 41 | public String getGuiItem() { 42 | return guiItem; 43 | } 44 | 45 | public Category getParent() { 46 | return parentCategory; 47 | } 48 | 49 | public List getPackages() { 50 | return categoryPackages; 51 | } 52 | 53 | public static SubCategory fromJsonObject(JsonObject jsonObject, Category category) { 54 | return new SubCategory( 55 | jsonObject.get("id").getAsInt(), 56 | jsonObject.get("order").getAsInt(), 57 | jsonObject.get("name").getAsString(), 58 | jsonObject.get("gui_item").getAsString(), 59 | category, 60 | jsonObject.getAsJsonArray("packages").asList().stream().map(item -> CategoryPackage.fromJsonObject(item.getAsJsonObject())).collect(Collectors.toList()) 61 | ); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "Category{" + 67 | "id=" + id + 68 | ", order=" + order + 69 | ", name='" + name + '\'' + 70 | ", packages=" + categoryPackages + 71 | '}'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/LookupCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.obj.PlayerLookupInfo; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.LiteralText; 9 | 10 | import java.util.concurrent.ExecutionException; 11 | 12 | public class LookupCommand extends SubCommand { 13 | public LookupCommand(TebexPlugin platform) { 14 | super(platform, "lookup", "tebex.lookup"); 15 | } 16 | 17 | @Override 18 | public void execute(CommandContext context) { 19 | final ServerCommandSource source = context.getSource(); 20 | TebexPlugin platform = getPlatform(); 21 | 22 | if (!platform.isSetup()) { 23 | source.sendFeedback(new LiteralText("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."), false); 24 | return; 25 | } 26 | 27 | String username = context.getArgument("username", String.class); 28 | 29 | PlayerLookupInfo lookupInfo = null; 30 | try { 31 | lookupInfo = platform.getSDK().getPlayerLookupInfo(username).get(); 32 | } catch (InterruptedException|ExecutionException e) { 33 | source.sendError(new LiteralText("§b[Tebex] §7Failed to complete player lookup. " + e.getMessage())); 34 | return; 35 | } 36 | 37 | source.sendFeedback(new LiteralText("§b[Tebex] §7Username: " + lookupInfo.getLookupPlayer().getUsername()), false); 38 | source.sendFeedback(new LiteralText("§b[Tebex] §7Id: " + lookupInfo.getLookupPlayer().getId()), false); 39 | source.sendFeedback(new LiteralText("§b[Tebex] §7Chargeback Rate: " + lookupInfo.chargebackRate), false); 40 | source.sendFeedback(new LiteralText("§b[Tebex] §7Bans Total: " + lookupInfo.banCount), false); 41 | source.sendFeedback(new LiteralText("§b[Tebex] §7Payments: " + lookupInfo.payments.size()), false); 42 | } 43 | 44 | @Override 45 | public String getDescription() { 46 | return "Gets user transaction info from your webstore."; 47 | } 48 | 49 | @Override 50 | public String getUsage() { 51 | return ""; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/BanCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | 7 | import java.util.concurrent.ExecutionException; 8 | 9 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 10 | 11 | public class BanCommand extends SubCommand { 12 | public BanCommand(TebexPlugin platform) { 13 | super(platform, "ban", "tebex.ban"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandSource sender, String[] args) { 18 | TebexPlugin platform = getPlatform(); 19 | 20 | if (args.length < 1) { // require username at minimum 21 | sender.sendMessage(getInvalidUsageMessage()); 22 | return; 23 | } 24 | 25 | String playerName = args[0]; 26 | String reason = ""; 27 | String ip = ""; 28 | 29 | if (args.length > 1) { // second param provided 30 | reason = args[1]; 31 | } 32 | if (args.length > 2) { // third param provided 33 | ip = args[2]; 34 | } 35 | 36 | if (!platform.isSetup()) { 37 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key.")); 38 | return; 39 | } 40 | 41 | try { 42 | boolean success = platform.getSDK().createBan(playerName, ip, reason).get(); 43 | if (success) { 44 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Player banned successfully.")); 45 | } else { 46 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Failed to ban player.")); 47 | } 48 | } catch (InterruptedException | ExecutionException e) { 49 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Error while banning player: " + e.getMessage())); 50 | } 51 | } 52 | 53 | @Override 54 | public String getDescription() { 55 | return "Bans a player from using the webstore. Unbans can only be made via the web panel."; 56 | } 57 | 58 | @Override 59 | public String getUsage() { 60 | return " "; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/DebugCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.SDK; 8 | import io.tebex.sdk.exception.ServerNotFoundException; 9 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 10 | import io.tebex.sdk.util.StringUtil; 11 | import net.minecraft.server.command.ServerCommandSource; 12 | import net.minecraft.text.LiteralText; 13 | 14 | import java.io.IOException; 15 | 16 | public class DebugCommand extends SubCommand { 17 | public DebugCommand(TebexPlugin platform) { 18 | super(platform, "debug", "tebex.debug"); 19 | } 20 | 21 | @Override 22 | public void execute(CommandContext context) { 23 | final ServerCommandSource source = context.getSource(); 24 | TebexPlugin platform = getPlatform(); 25 | 26 | ServerPlatformConfig config = platform.getPlatformConfig(); 27 | YamlDocument configFile = config.getYamlDocument(); 28 | 29 | String input = context.getArgument("trueOrFalse", String.class); 30 | if (StringUtil.isTruthy(input)) { 31 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Debug mode enabled."), false); 32 | config.setVerbose(true); 33 | configFile.set("verbose", true); 34 | } else if (StringUtil.isFalsy(input)) { 35 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Debug mode disabled."), false); 36 | config.setVerbose(false); 37 | configFile.set("verbose", false); 38 | } else { 39 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()), false); 40 | } 41 | 42 | try { 43 | configFile.save(); 44 | } catch (IOException e) { 45 | context.getSource().sendFeedback(new LiteralText("§b[Tebex] §7Failed to save configuration file."), false); 46 | } 47 | } 48 | 49 | @Override 50 | public String getDescription() { 51 | return "Enables more verbose logging."; 52 | } 53 | 54 | @Override 55 | public String getUsage() { 56 | return ""; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/LookupCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import io.tebex.plugin.TebexPlugin; 4 | import io.tebex.plugin.command.SubCommand; 5 | import io.tebex.sdk.obj.PlayerLookupInfo; 6 | import org.bukkit.command.CommandSender; 7 | 8 | import java.util.concurrent.CompletableFuture; 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public class LookupCommand extends SubCommand { 12 | public LookupCommand(TebexPlugin platform) { 13 | super(platform, "lookup", "tebex.lookup"); 14 | } 15 | 16 | @Override 17 | public void execute(CommandSender sender, String[] args) { 18 | TebexPlugin platform = getPlatform(); 19 | 20 | if (!platform.isSetup()) { 21 | sender.sendMessage("§b[Tebex] §7This server is not connected to a webstore. Use /tebex secret to set your store key."); 22 | return; 23 | } 24 | 25 | if (args.length != 1) { 26 | sender.sendMessage("§b[Tebex] §7Invalid command usage. Use /tebex " + this.getName() + " " + getUsage()); 27 | return; 28 | } 29 | 30 | String username = args[0]; 31 | 32 | PlayerLookupInfo lookupInfo = null; 33 | try { 34 | CompletableFuture future = platform.getSDK().getPlayerLookupInfo(username); 35 | lookupInfo = future.get(); 36 | } catch (InterruptedException|ExecutionException e) { 37 | sender.sendMessage("§b[Tebex] §7" + e.getMessage()); 38 | return; 39 | } 40 | 41 | if (lookupInfo != null) { 42 | sender.sendMessage("§b[Tebex] §7Username: " + lookupInfo.getLookupPlayer().getUsername()); 43 | sender.sendMessage("§b[Tebex] §7Id: " + lookupInfo.getLookupPlayer().getId()); 44 | sender.sendMessage("§b[Tebex] §7Chargeback Rate: " + lookupInfo.chargebackRate); 45 | sender.sendMessage("§b[Tebex] §7Bans Total: " + lookupInfo.banCount); 46 | sender.sendMessage("§b[Tebex] §7Payments: " + lookupInfo.payments.size()); 47 | } else { 48 | sender.sendMessage("§b[Tebex] §7No information found for that player."); 49 | } 50 | 51 | } 52 | 53 | @Override 54 | public String getDescription() { 55 | return "Gets user transaction info from your webstore."; 56 | } 57 | 58 | @Override 59 | public String getUsage() { 60 | return ""; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/TebexCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import io.tebex.plugin.manager.CommandManager; 5 | import com.velocitypowered.api.command.SimpleCommand; 6 | import com.velocitypowered.api.command.CommandSource; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 14 | 15 | public class TebexCommand implements SimpleCommand { 16 | private final CommandManager commandManager; 17 | 18 | public TebexCommand(CommandManager commandManager) { 19 | this.commandManager = commandManager; 20 | } 21 | 22 | @Override 23 | public void execute(Invocation invocation) { 24 | CommandSource sender = invocation.source(); 25 | String[] args = invocation.arguments(); 26 | 27 | if(args.length == 0) { 28 | sender.sendMessage(legacySection().deserialize("§8[Tebex] §7Welcome to Tebex!")); 29 | sender.sendMessage(legacySection().deserialize("§8[Tebex] §7This server is running version §fv" + commandManager.getPlatform().getVersion() + "§7.")); 30 | return; 31 | } 32 | 33 | Map commands = commandManager.getCommands(); 34 | if(! commands.containsKey(args[0].toLowerCase())) { 35 | sender.sendMessage(legacySection().deserialize("§8[Tebex] §7Unknown command.")); 36 | return; 37 | } 38 | 39 | final SubCommand subCommand = commands.get(args[0].toLowerCase()); 40 | if (! sender.hasPermission(subCommand.getPermission())) { 41 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7You do not have access to that command.")); 42 | return; 43 | } 44 | 45 | subCommand.execute(sender, Arrays.copyOfRange(args, 1, args.length)); 46 | } 47 | 48 | @Override 49 | public List suggest(Invocation invocation) { 50 | String[] args = invocation.arguments(); 51 | 52 | if(args.length == 1) { 53 | return commandManager.getCommands() 54 | .keySet() 55 | .stream() 56 | .filter(s -> s.startsWith(args[0])) 57 | .collect(Collectors.toList()); 58 | } 59 | 60 | return ImmutableList.of(); 61 | } 62 | } -------------------------------------------------------------------------------- /fabric/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | group = rootProject.group 4 | version = rootProject.version 5 | 6 | plugins { 7 | java 8 | id("com.github.johnrengelman.shadow") 9 | id("fabric-loom") 10 | } 11 | 12 | var minecraftVersion = properties["minecraft_version"] as String 13 | var yarnMappings = properties["yarn_mappings"] as String 14 | var loaderVersion = properties["loader_version"] as String 15 | var fabricVersion = properties["fabric_version"] as String 16 | 17 | java { 18 | toolchain.languageVersion.set(JavaLanguageVersion.of(16)) 19 | sourceCompatibility = JavaVersion.VERSION_16 20 | targetCompatibility = JavaVersion.VERSION_16 21 | } 22 | 23 | dependencies { 24 | shadow(project(":sdk")) 25 | shadow("it.unimi.dsi:fastutil:8.5.6") 26 | shadow("com.github.cryptomorin:XSeries:9.3.1") { 27 | isTransitive = false 28 | } 29 | 30 | minecraft("com.mojang:minecraft:${minecraftVersion}") 31 | mappings("net.fabricmc:yarn:${yarnMappings}:v2") 32 | 33 | modImplementation("eu.pb4:sgui:0.5.0") 34 | include("eu.pb4:sgui:0.5.0") 35 | 36 | modImplementation("net.fabricmc:fabric-loader:${loaderVersion}") 37 | modImplementation("net.fabricmc.fabric-api:fabric-api:${fabricVersion}") 38 | 39 | compileOnly("dev.dejvokep:boosted-yaml:1.3") 40 | } 41 | 42 | 43 | tasks.named("shadowJar", ShadowJar::class.java) { 44 | configurations = listOf(project.configurations.shadow.get()) 45 | 46 | relocate("it.unimi", "io.tebex.plugin.libs.fastutil") 47 | relocate("okhttp3", "io.tebex.plugin.libs.okhttp3") 48 | relocate("okio", "io.tebex.plugin.libs.okio") 49 | relocate("dev.dejvokep.boostedyaml", "io.tebex.plugin.libs.boostedyaml") 50 | relocate("org.jetbrains.annotations", "io.tebex.plugin.libs.jetbrains") 51 | relocate("kotlin", "io.tebex.plugin.libs.kotlin") 52 | relocate("com.github.benmanes.caffeine", "io.tebex.plugin.libs.caffeine") 53 | relocate("com.google.gson", "io.tebex.plugin.libs.gson") 54 | minimize() 55 | 56 | archiveFileName.set("${project.name}-${project.version}-shadow.jar") 57 | 58 | finalizedBy("remapJar") 59 | } 60 | 61 | tasks.remapJar { 62 | dependsOn("shadowJar") 63 | val shadowJar = tasks.shadowJar.get() 64 | 65 | inputFile.set(shadowJar.archiveFile) 66 | archiveFileName.set("tebex-${project.name}-${project.version}.jar") 67 | archiveClassifier.set(shadowJar.archiveClassifier) 68 | delete(shadowJar.archiveFile) 69 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/config/ProxyPlatformConfig.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform.config; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | 5 | /** 6 | * The ProxyPlatformConfig class holds the configuration for the Tebex SDK. 7 | */ 8 | public class ProxyPlatformConfig implements IPlatformConfig { 9 | private final int configVersion; 10 | private YamlDocument yamlDocument; 11 | 12 | private boolean verbose; 13 | private String secretKey; 14 | 15 | /** 16 | * Creates a PlatformConfig instance with the provided configuration version. 17 | * 18 | * @param configVersion The configuration version. 19 | */ 20 | public ProxyPlatformConfig(int configVersion) { 21 | this.configVersion = configVersion; 22 | } 23 | 24 | /** 25 | * Sets the secret key. 26 | * 27 | * @param secretKey The secret key. 28 | */ 29 | public void setSecretKey(String secretKey) { 30 | this.secretKey = secretKey; 31 | } 32 | 33 | public void setVerbose(boolean verbose) { 34 | this.verbose = verbose; 35 | } 36 | 37 | /** 38 | * Sets the YAML document for this configuration. 39 | * 40 | * @param yamlDocument The YAML document. 41 | */ 42 | public void setYamlDocument(YamlDocument yamlDocument) { 43 | this.yamlDocument = yamlDocument; 44 | } 45 | 46 | /** 47 | * Returns the configuration version. 48 | * 49 | * @return The configuration version. 50 | */ 51 | @Override 52 | public int getConfigVersion() { 53 | return configVersion; 54 | } 55 | 56 | /** 57 | * Returns the secret key. 58 | * 59 | * @return The secret key. 60 | */ 61 | @Override 62 | public String getSecretKey() { 63 | return secretKey; 64 | } 65 | 66 | @Override 67 | public boolean isVerbose() { 68 | return verbose; 69 | } 70 | 71 | /** 72 | * Returns the YAML document for this configuration. 73 | * 74 | * @return The YAML document. 75 | */ 76 | @Override 77 | public YamlDocument getYamlDocument() { 78 | return yamlDocument; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "ProxyPlatformConfig{" + 84 | "configVersion=" + configVersion + 85 | ", yamlDocument=" + yamlDocument + 86 | ", verbose=" + verbose + 87 | ", secretKey='" + secretKey + '\'' + 88 | '}'; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/util/Multithreading.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.util; 2 | 3 | import java.util.concurrent.*; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class Multithreading { 7 | private static final AtomicInteger counter = new AtomicInteger(0); 8 | 9 | private static final ScheduledExecutorService RUNNABLE_POOL = Executors.newScheduledThreadPool(10, r -> 10 | new Thread(r, "Tebex Thread " + counter.incrementAndGet())); 11 | 12 | public static ThreadPoolExecutor POOL = new ThreadPoolExecutor(10, 30, 13 | 0L, TimeUnit.SECONDS, 14 | new LinkedBlockingQueue<>(), 15 | r -> new Thread(r, String.format("Thread %s", counter.incrementAndGet()))); 16 | 17 | public static ScheduledFuture schedule(Runnable r, long initialDelay, long delay, TimeUnit unit) { 18 | return RUNNABLE_POOL.scheduleAtFixedRate(r, initialDelay, delay, unit); 19 | } 20 | 21 | public static ScheduledFuture schedule(Runnable r, long delay, TimeUnit unit) { 22 | return RUNNABLE_POOL.schedule(r, delay, unit); 23 | } 24 | 25 | public static Executor delayedExecutor(long delay, TimeUnit unit) { 26 | return task -> schedule(task, delay, unit); 27 | } 28 | 29 | public static void runAsync(Runnable runnable) { 30 | POOL.execute(runnable); 31 | } 32 | 33 | public static Future submit(Runnable runnable) { 34 | return POOL.submit(runnable); 35 | } 36 | 37 | public static void executeAsync(Runnable runnable) { 38 | runAsync(runnable); 39 | } 40 | 41 | public static void executeAsyncLater(Runnable runnable, long time, TimeUnit unit) { 42 | schedule(runnable, time, unit); 43 | } 44 | 45 | public static void executeAsync(Runnable runnable, long initialDelay, long time, TimeUnit unit) { 46 | schedule(runnable, initialDelay, time, unit); 47 | } 48 | 49 | public static void executeBlocking(Runnable runnable) throws InterruptedException, ExecutionException { 50 | Future future = submit(runnable); 51 | future.get(); // This will block until the task is completed 52 | } 53 | 54 | public static void executeBlockingLater(Runnable runnable, long time, TimeUnit unit) throws InterruptedException, ExecutionException { 55 | Future future = schedule(runnable, time, unit); 56 | future.get(); // This will block until the task is completed 57 | } 58 | 59 | public static void shutdown() { 60 | POOL.shutdown(); 61 | RUNNABLE_POOL.shutdown(); 62 | } 63 | } -------------------------------------------------------------------------------- /bungeecord/src/main/java/io/tebex/plugin/command/sub/SecretCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.sdk.SDK; 7 | import io.tebex.sdk.exception.ServerNotFoundException; 8 | import io.tebex.sdk.platform.config.ProxyPlatformConfig; 9 | import net.md_5.bungee.api.CommandSender; 10 | 11 | import java.io.IOException; 12 | 13 | public class SecretCommand extends SubCommand { 14 | public SecretCommand(TebexPlugin platform) { 15 | super(platform, "secret", "tebex.setup"); 16 | } 17 | 18 | @Override 19 | public void execute(CommandSender sender, String[] args) { 20 | if(args.length == 0) { 21 | sender.sendMessage("§b[Tebex] §7Usage: §f/tebex secret "); 22 | return; 23 | } 24 | 25 | String serverToken = args[0]; 26 | TebexPlugin platform = getPlatform(); 27 | 28 | SDK analyse = platform.getSDK(); 29 | ProxyPlatformConfig analyseConfig = platform.getPlatformConfig(); 30 | YamlDocument configFile = analyseConfig.getYamlDocument(); 31 | 32 | analyse.setSecretKey(serverToken); 33 | 34 | platform.getSDK().getServerInformation().thenAccept(serverInformation -> { 35 | analyseConfig.setSecretKey(serverToken); 36 | configFile.set("server.secret-key", serverToken); 37 | 38 | try { 39 | configFile.save(); 40 | } catch (IOException e) { 41 | sender.sendMessage("§b[Tebex] §7Failed to save config: " + e.getMessage()); 42 | } 43 | 44 | sender.sendMessage("§b[Tebex] §7Connected to §b" + serverInformation.getServer().getName() + "§7."); 45 | platform.configure(); 46 | }).exceptionally(ex -> { 47 | Throwable cause = ex.getCause(); 48 | 49 | if(cause instanceof ServerNotFoundException) { 50 | sender.sendMessage("§b[Tebex] §7Server not found. Please check your secret key."); 51 | platform.halt(); 52 | } else { 53 | sender.sendMessage("§b[Tebex] §cAn error occurred: " + cause.getMessage()); 54 | cause.printStackTrace(); 55 | } 56 | 57 | return null; 58 | }); 59 | } 60 | 61 | @Override 62 | public String getDescription() { 63 | return "Connects to your Tebex store."; 64 | } 65 | 66 | @Override 67 | public String getUsage() { 68 | return ""; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bukkit/src/main/java/io/tebex/plugin/command/sub/SecretCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | import io.tebex.plugin.TebexPlugin; 5 | import io.tebex.plugin.command.SubCommand; 6 | import io.tebex.plugin.gui.BuyGUI; 7 | import io.tebex.sdk.SDK; 8 | import io.tebex.sdk.exception.ServerNotFoundException; 9 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 10 | import org.bukkit.command.CommandSender; 11 | 12 | import java.io.IOException; 13 | 14 | public class SecretCommand extends SubCommand { 15 | public SecretCommand(TebexPlugin platform) { 16 | super(platform, "secret", "tebex.setup"); 17 | } 18 | 19 | @Override 20 | public void execute(CommandSender sender, String[] args) { 21 | if(args.length == 0) { 22 | sender.sendMessage("§b[Tebex] §7Usage: §f/tebex secret "); 23 | return; 24 | } 25 | 26 | String serverToken = args[0]; 27 | TebexPlugin platform = getPlatform(); 28 | 29 | SDK analyse = platform.getSDK(); 30 | ServerPlatformConfig analyseConfig = platform.getPlatformConfig(); 31 | YamlDocument configFile = analyseConfig.getYamlDocument(); 32 | 33 | analyse.setSecretKey(serverToken); 34 | 35 | platform.getSDK().getServerInformation().thenAccept(serverInformation -> { 36 | analyseConfig.setSecretKey(serverToken); 37 | configFile.set("server.secret-key", serverToken); 38 | 39 | try { 40 | configFile.save(); 41 | } catch (IOException e) { 42 | sender.sendMessage("§b[Tebex] §7Failed to save config: " + e.getMessage()); 43 | } 44 | 45 | 46 | platform.loadServerPlatformConfig(configFile); 47 | platform.reloadConfig(); 48 | platform.setBuyGUI(new BuyGUI(platform)); 49 | platform.refreshListings(); 50 | platform.configure(); 51 | 52 | sender.sendMessage("§b[Tebex] §7Connected to §b" + serverInformation.getServer().getName() + "§7."); 53 | }).exceptionally(ex -> { 54 | Throwable cause = ex.getCause(); 55 | 56 | if(cause instanceof ServerNotFoundException) { 57 | sender.sendMessage("§b[Tebex] §7Server not found. Please check your secret key."); 58 | platform.halt(); 59 | } else { 60 | sender.sendMessage("§b[Tebex] §cAn error occurred: " + cause.getMessage()); 61 | cause.printStackTrace(); 62 | } 63 | 64 | return null; 65 | }); 66 | } 67 | 68 | @Override 69 | public String getDescription() { 70 | return "Connects to your Tebex store."; 71 | } 72 | 73 | @Override 74 | public String getUsage() { 75 | return ""; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/tebex/plugin/command/sub/SecretCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.SDK; 8 | import io.tebex.sdk.exception.ServerNotFoundException; 9 | import io.tebex.sdk.platform.config.ProxyPlatformConfig; 10 | 11 | import java.io.IOException; 12 | 13 | import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection; 14 | 15 | public class SecretCommand extends SubCommand { 16 | public SecretCommand(TebexPlugin platform) { 17 | super(platform, "secret", "tebex.setup"); 18 | } 19 | 20 | @Override 21 | public void execute(CommandSource sender, String[] args) { 22 | if (args.length == 0) { 23 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Usage: §f/tebex secret ")); 24 | return; 25 | } 26 | 27 | String serverToken = args[0]; 28 | TebexPlugin platform = getPlatform(); 29 | 30 | SDK analyse = platform.getSDK(); 31 | ProxyPlatformConfig analyseConfig = platform.getPlatformConfig(); 32 | YamlDocument configFile = analyseConfig.getYamlDocument(); 33 | 34 | analyse.setSecretKey(serverToken); 35 | 36 | platform.getSDK().getServerInformation().thenAccept(serverInformation -> { 37 | analyseConfig.setSecretKey(serverToken); 38 | configFile.set("server.secret-key", serverToken); 39 | 40 | try { 41 | configFile.save(); 42 | } catch (IOException e) { 43 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Failed to save config: " + e.getMessage())); 44 | } 45 | 46 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Connected to §b" + serverInformation.getServer().getName() + "§7.")); 47 | platform.configure(); 48 | }).exceptionally(ex -> { 49 | Throwable cause = ex.getCause(); 50 | 51 | if (cause instanceof ServerNotFoundException) { 52 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §7Server not found. Please check your secret key.")); 53 | platform.halt(); 54 | } else { 55 | sender.sendMessage(legacySection().deserialize("§b[Tebex] §cAn error occurred: " + cause.getMessage())); 56 | cause.printStackTrace(); 57 | } 58 | 59 | return null; 60 | }); 61 | } 62 | 63 | @Override 64 | public String getDescription() { 65 | return "Connects to your Tebex store."; 66 | } 67 | 68 | @Override 69 | public String getUsage() { 70 | return ""; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/command/sub/SecretCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.command.sub; 2 | 3 | import com.mojang.brigadier.context.CommandContext; 4 | import dev.dejvokep.boostedyaml.YamlDocument; 5 | import io.tebex.plugin.TebexPlugin; 6 | import io.tebex.plugin.command.SubCommand; 7 | import io.tebex.sdk.SDK; 8 | import io.tebex.sdk.exception.ServerNotFoundException; 9 | import io.tebex.sdk.platform.config.ServerPlatformConfig; 10 | import net.minecraft.server.command.ServerCommandSource; 11 | import net.minecraft.text.LiteralText; 12 | 13 | import java.io.IOException; 14 | 15 | public class SecretCommand extends SubCommand { 16 | public SecretCommand(TebexPlugin platform) { 17 | super(platform, "secret", "tebex.secret"); 18 | } 19 | 20 | @Override 21 | public void execute(CommandContext context) { 22 | final ServerCommandSource source = context.getSource(); 23 | 24 | String serverToken = context.getArgument("key", String.class); 25 | TebexPlugin platform = getPlatform(); 26 | 27 | if(platform.isSetup()) { 28 | source.sendFeedback(new LiteralText("§b[Tebex] §7Already connected to a store."), false); 29 | return; 30 | } 31 | 32 | SDK analyse = platform.getSDK(); 33 | ServerPlatformConfig analyseConfig = platform.getPlatformConfig(); 34 | YamlDocument configFile = analyseConfig.getYamlDocument(); 35 | 36 | analyse.setSecretKey(serverToken); 37 | 38 | platform.getSDK().getServerInformation().thenAccept(serverInformation -> { 39 | analyseConfig.setSecretKey(serverToken); 40 | configFile.set("server.secret-key", serverToken); 41 | 42 | try { 43 | configFile.save(); 44 | } catch (IOException e) { 45 | source.sendFeedback(new LiteralText("§b[Tebex] §7Failed to save config: " + e.getMessage()), false); 46 | } 47 | 48 | source.sendFeedback(new LiteralText("§b[Tebex] §7Connected to §b" + serverInformation.getServer().getName() + "§7."), false); 49 | platform.configure(); 50 | }).exceptionally(ex -> { 51 | Throwable cause = ex.getCause(); 52 | 53 | if(cause instanceof ServerNotFoundException) { 54 | source.sendFeedback(new LiteralText("§b[Tebex] §7Server not found. Please check your secret key."), false); 55 | platform.halt(); 56 | } else { 57 | source.sendFeedback(new LiteralText("§b[Tebex] §cAn error occurred: " + cause.getMessage()), false); 58 | cause.printStackTrace(); 59 | } 60 | 61 | return null; 62 | }); 63 | } 64 | 65 | @Override 66 | public String getDescription() { 67 | return "Connects to your Tebex store."; 68 | } 69 | 70 | @Override 71 | public String getUsage() { 72 | return ""; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | [issue]: https://github.com/tebexio/Tebex-Minecraft/issues/new 2 | 3 | [wiki]: https://docs.tebex.io/creators/ 4 | 5 | [master]: https://github.com/tebexio/Tebex-Minecraft/tree/main 6 | 7 | [feedback form]: https://wkf.ms/45PQwfE 8 | 9 | [product suggestions]: https://suggestions.tebex.io/ 10 | 11 | # Contributing Guidelines 12 | 13 | We welcome everyone to contribute towards the Tebex Project, but doing so will require you to follow specific rules to 14 | keep a consistent and welcoming way of contributing. 15 | 16 | ## ⚠️ Do Not Submit Feature Requests as Issues 17 | 18 | Tebex has dedicated tracking of feature requests and user suggestions depending on the platform you're inquiring about. **If you have a suggestion, please use the appropriate link below to let us know.** 19 | 20 | Any suggestions created as issues will be closed and forwarded to our [Feedback Form]. 21 | 22 | #### For Plugin Features 23 | - Relating to the Minecraft plugin, commands, its functionality, etc. 24 | - Please use the [feedback form] to tell us your feature request or suggestion. 25 | 26 | #### For Webstore Features 27 | - Relating to the webstore, packages, coupons, sales, etc on https://tebex.io/ 28 | - Please use our [product suggestions] site to submit your feature request. 29 | 30 | #### For Customer Support 31 | - We do not provide customer support via GitHub issues. 32 | - Please contact our support team at [support@tebex.io](mailto:support@tebex.io) 33 | 34 | ## Issues 35 | 36 | Issues are intended to document bugs or problems that occur within our project. To report a bug, please open an [issue] and follow the rules below. 37 | 38 | ### Follow the template 39 | 40 | We may have issue templates to help us get the required information more easily. Please follow the provided template when 41 | either filing a bug report or feature request. 42 | 43 | ### Use the latest version 44 | 45 | When it comes to bug reports should you always check first that you're using the latest release of Tebex. It is likely that a bug you're experiencing may 46 | have already been fixed if a newer version of the plugin is available. 47 | 48 | ### No duplicate issues 49 | 50 | Make sure that there aren't any existing issues relating to the problem, which are still open, or are closed with a solution/explanation. 51 | 52 | We encourage you to leave a comment or reaction if an issue already exists. Comment with your bug report info or even leave just a 👍 if the issue is relevant for you. This helps our team prioritize which issues we approach. 53 | 54 | ## Pull requests 55 | 56 | As an open source project are we welcoming all contributions to improve Tebex, being it changes to its code, or 57 | contributions to its documentation such as the [Wiki] or the Javadocs. 58 | 59 | ### Code contributions 60 | When contributing towards the code of Tebex, be it new features or just bug fixes, your changes should follow the 61 | general code styling used in the project. Use complete comments and Javadoc where applicable. -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/Category.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | public class Category implements ICategory { 10 | private final int id; 11 | private final int order; 12 | private final String name; 13 | private final String guiItem; 14 | private final boolean onlySubcategories; 15 | private List subCategories; 16 | private final List categoryPackages; 17 | 18 | public Category(int id, int order, String name, String guiItem, boolean onlySubcategories, List categoryPackages) { 19 | this.id = id; 20 | this.order = order; 21 | this.name = name; 22 | this.guiItem = guiItem; 23 | this.onlySubcategories = onlySubcategories; 24 | this.subCategories = new ArrayList<>(); 25 | this.categoryPackages = categoryPackages; 26 | } 27 | 28 | @Override 29 | public int getId() { 30 | return id; 31 | } 32 | 33 | @Override 34 | public int getOrder() { 35 | return order; 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | @Override 44 | public String getGuiItem() { 45 | return guiItem; 46 | } 47 | 48 | public boolean isOnlySubcategories() { 49 | return onlySubcategories; 50 | } 51 | 52 | public List getSubCategories() { 53 | return subCategories; 54 | } 55 | 56 | public List getPackages() { 57 | return categoryPackages; 58 | } 59 | 60 | public void setSubCategories(List subCategories) { 61 | this.subCategories = subCategories; 62 | } 63 | 64 | public static Category fromJsonObject(JsonObject jsonObject) { 65 | Category category = new Category( 66 | jsonObject.get("id").getAsInt(), 67 | jsonObject.get("order").getAsInt(), 68 | jsonObject.get("name").getAsString(), 69 | jsonObject.get("gui_item").getAsString(), 70 | jsonObject.has("only_subcategories") && jsonObject.get("only_subcategories").getAsBoolean(), 71 | jsonObject.getAsJsonArray("packages").asList().stream().map(item -> CategoryPackage.fromJsonObject(item.getAsJsonObject())).collect(Collectors.toList()) 72 | ); 73 | 74 | category.setSubCategories(jsonObject.getAsJsonArray("subcategories").asList().stream().map(item -> SubCategory.fromJsonObject(item.getAsJsonObject(), category)).collect(Collectors.toList())); 75 | 76 | return category; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "Category{" + 82 | "id=" + id + 83 | ", order=" + order + 84 | ", name='" + name + '\'' + 85 | ", onlySubcategories=" + onlySubcategories + 86 | ", subCategories=" + subCategories + 87 | ", packages=" + categoryPackages + 88 | '}'; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /sdk/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | plugins { 3 | id("maven-publish") 4 | id("signing") 5 | } 6 | 7 | group = rootProject.group 8 | version = rootProject.version 9 | 10 | val javaVersion = JavaVersion.current() 11 | val ossrhUsername = System.getenv("OSSRH_USERNAME") ?: properties["ossrhUsername"] as String? 12 | val ossrhPassword = System.getenv("OSSRH_PASSWORD") ?: properties["ossrhPassword"] as String? 13 | 14 | repositories { 15 | mavenLocal() 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("com.squareup.okhttp3:okhttp:4.10.0") 21 | implementation("dev.dejvokep:boosted-yaml:1.3") 22 | implementation("com.google.code.gson:gson:2.10.1") 23 | compileOnly("com.google.guava:guava:30.1.1-jre") 24 | testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") 25 | } 26 | 27 | tasks.named("test") { 28 | useJUnitPlatform() 29 | 30 | maxHeapSize = "1G" 31 | 32 | testLogging { 33 | events("passed") 34 | } 35 | } 36 | 37 | tasks.withType { 38 | options.compilerArgs.addAll(listOf("-Xlint:deprecation", "-Xlint:unchecked")) 39 | } 40 | 41 | // auto minimize shadowjar 42 | tasks.withType { 43 | minimize() 44 | } 45 | 46 | java { 47 | withSourcesJar() 48 | withJavadocJar() 49 | } 50 | 51 | val sdkJar by tasks.registering(Jar::class) { 52 | archiveClassifier.set("") 53 | dependsOn("shadowJar") 54 | } 55 | 56 | publishing { 57 | publications { 58 | create("sdk") { 59 | from(components["java"]) 60 | 61 | pom { 62 | name.set("Tebex SDK") 63 | description.set("The official Java SDK for Tebex.") 64 | url.set("https://github.com/tebexio/BuycraftX") 65 | 66 | licenses { 67 | license { 68 | name.set("MIT License") 69 | url.set("https://github.com/track/plugin/blob/main/LICENSE") 70 | } 71 | } 72 | 73 | scm { 74 | url.set("https://github.com/track/plugin") 75 | connection.set("scm:https://github.com/track/plugin.git") 76 | developerConnection.set("scm:git@github.com:track/plugin.git") 77 | } 78 | 79 | developers { 80 | developer { 81 | id.set("charliejoseph") 82 | name.set("Charlie Joseph") 83 | email.set("charlie.joseph@overwolf.com") 84 | } 85 | } 86 | } 87 | } 88 | } 89 | repositories { 90 | maven { 91 | url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") 92 | credentials { 93 | username = ossrhUsername 94 | password = ossrhPassword 95 | } 96 | } 97 | } 98 | } 99 | 100 | signing { 101 | sign(publishing.publications["sdk"]) 102 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/CategoryPackage.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class CategoryPackage { 6 | private final int id; 7 | private final int order; 8 | private final String name; 9 | private final double price; 10 | private final String image; 11 | private final String itemId; 12 | private final Sale sale; 13 | 14 | public CategoryPackage(int id, int order, String name, double price, String image, String itemId, Sale sale) { 15 | this.id = id; 16 | this.order = order; 17 | this.name = name; 18 | this.price = price; 19 | this.image = image; 20 | this.itemId = itemId; 21 | this.sale = sale; 22 | } 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | 28 | public int getOrder() { 29 | return order; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public double getPrice() { 37 | return price; 38 | } 39 | 40 | public String getImage() { 41 | return image; 42 | } 43 | 44 | public String getGuiItem() { 45 | return itemId; 46 | } 47 | 48 | public Sale getSale() { 49 | return sale; 50 | } 51 | 52 | public boolean hasSale() { 53 | return sale.isActive(); 54 | } 55 | 56 | public static class Sale { 57 | private final boolean active; 58 | private final double discount; 59 | 60 | public Sale(boolean active, double discount) { 61 | this.active = active; 62 | this.discount = discount; 63 | } 64 | 65 | public boolean isActive() { 66 | return active; 67 | } 68 | 69 | public double getDiscount() { 70 | return discount; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "Sale{" + 76 | "active=" + active + 77 | ", discount=" + discount + 78 | '}'; 79 | } 80 | } 81 | 82 | public static CategoryPackage fromJsonObject(JsonObject jsonObject) { 83 | JsonObject sale = jsonObject.getAsJsonObject("sale"); 84 | 85 | return new CategoryPackage( 86 | jsonObject.get("id").getAsInt(), 87 | jsonObject.get("order").getAsInt(), 88 | jsonObject.get("name").getAsString(), 89 | jsonObject.get("price").getAsDouble(), 90 | jsonObject.get("image").getAsString(), 91 | jsonObject.get("gui_item").getAsString(), 92 | new Sale(sale.get("active").getAsBoolean(), sale.get("discount").getAsDouble()) 93 | ); 94 | } 95 | 96 | @Override 97 | public String toString() { 98 | return "Package{" + 99 | "id=" + id + 100 | ", order=" + order + 101 | ", name='" + name + '\'' + 102 | ", price=" + price + 103 | ", image='" + image + '\'' + 104 | ", itemId=" + itemId + 105 | ", sale=" + sale + 106 | '}'; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /fabric/src/main/java/io/tebex/plugin/manager/CommandManager.java: -------------------------------------------------------------------------------- 1 | package io.tebex.plugin.manager; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.arguments.StringArgumentType; 6 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 7 | import io.tebex.plugin.TebexPlugin; 8 | import io.tebex.plugin.command.BuyCommand; 9 | import io.tebex.plugin.command.SubCommand; 10 | import io.tebex.plugin.command.sub.*; 11 | import net.minecraft.server.command.ServerCommandSource; 12 | import net.minecraft.text.LiteralText; 13 | 14 | import java.util.List; 15 | 16 | import static net.minecraft.server.command.CommandManager.argument; 17 | import static net.minecraft.server.command.CommandManager.literal; 18 | 19 | public class CommandManager { 20 | private final TebexPlugin platform; 21 | private final List commands; 22 | 23 | public CommandManager(TebexPlugin platform) { 24 | this.platform = platform; 25 | this.commands = ImmutableList.of( 26 | new SecretCommand(platform), 27 | new ReloadCommand(platform), 28 | new ForceCheckCommand(platform), 29 | new HelpCommand(platform, this), 30 | new BanCommand(platform), 31 | new CheckoutCommand(platform), 32 | new DebugCommand(platform), 33 | new InfoCommand(platform), 34 | new LookupCommand(platform), 35 | new ReportCommand(platform), 36 | new SendLinkCommand(platform), 37 | new GoalsCommand(platform) 38 | ); 39 | } 40 | 41 | public void register(CommandDispatcher dispatcher) { 42 | LiteralArgumentBuilder baseCommand = literal("tebex").executes(context -> { 43 | final ServerCommandSource source = context.getSource(); 44 | source.sendFeedback(new LiteralText("§8[Tebex] §7Welcome to Tebex!"), false); 45 | source.sendFeedback(new LiteralText("§8[Tebex] §7This server is running version §fv" + platform.getVersion() + "§7."), false); 46 | 47 | return 1; 48 | }); 49 | 50 | BuyCommand buyCommand = new BuyCommand(platform); 51 | dispatcher.register(literal(platform.getPlatformConfig().getBuyCommandName()).executes(buyCommand::execute)); 52 | 53 | commands.forEach(command -> { 54 | LiteralArgumentBuilder subCommand = literal(command.getName()); 55 | 56 | if(command.getName().equalsIgnoreCase("secret")) { 57 | baseCommand.then(subCommand.then(argument("key", StringArgumentType.string()).executes(context -> { 58 | command.execute(context); 59 | return 1; 60 | }))); 61 | 62 | return; 63 | } 64 | 65 | baseCommand.then(subCommand.executes(context -> { 66 | command.execute(context); 67 | return 1; 68 | })); 69 | }); 70 | 71 | dispatcher.register(baseCommand); 72 | } 73 | 74 | public TebexPlugin getPlatform() { 75 | return platform; 76 | } 77 | 78 | public List getCommands() { 79 | return commands; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2.0.4 2 | ===== 3 | 4 | ### Fixes 5 | - The `{id}` parameter is now properly replaced on Geyser and Minecraft Offline/Geyser store types. For offline servers, it will be replaced with the user's name. For online servers, this will be the player's UUID. 6 | - Fix for `java.lang.String cannot be cast to class java.util.UUID` on Offline/Geyser servers 7 | - Fixed the use of deprecated characters directly in components in the Velocity module 8 | - Relocated Adventure to prevent conflicts with older Adventure APIs on the server 9 | 10 | 2.0.3 11 | ===== 12 | 13 | ### Features 14 | - **SDK:** `{uuid}` command parameter is now filled by the plugin if a uuid is available and not filled by Tebex API 15 | 16 | ### Fixes 17 | - **Minecraft Offline/Geyser:** Offline actions (such as removing groups) with no payment or package attached will now be processed properly. 18 | - **Minecraft Offline/Geyser:** Certain types of offline commands still could not be parsed and executed, causing console errors. 19 | - **Bukkit:** `/sendlink` now sends the checkout link to the target player 20 | - **SDK:** mojangIdToJavaId() no longer returns a null ID if any provided parameters are null 21 | 22 | 2.0.2 23 | ===== 24 | 25 | ### Features 26 | - Improvements to debug mode showing relevant HTTP request and response data 27 | - `/tebex lookup` provides more in-depth feedback when users are not found 28 | 29 | ### Changes 30 | - `/tebex ban` no longer requires an ip or reason 31 | 32 | ### Fixes 33 | - Servers running offline mode are now able to process commands 34 | - Online commands are handled more efficiently on large servers to avoid rate limits (code 429) 35 | - Fix for `Failed to get online commands: java.lang.UnsupportedOperationException: JsonNull` caused by online commands with no package reference 36 | - `/tebex sendlink` incorrectly used player id instead of username 37 | - Fix `/tebex report` now sends the entire report 38 | - Arguments such as `{id}` and `{username}` are now properly parsed for all command types 39 | 40 | 2.0.1 41 | ===== 42 | 43 | ### Fixes 44 | - Command usage instructions are now shown if incorrect/not enough args are used 45 | - Store information was not properly reloaded after running `tebex secret`, causing errors until the server was restarted. 46 | - `/tebex lookup` now uses the appropriate endpoint 47 | - Some commands' usage instructions improperly included a `.` in the command name 48 | - `/tebex report` now properly sends all report information to Tebex 49 | 50 | 2.0.0 51 | ===== 52 | - Plugin has been rewritten from the ground up to include support for: **Bukkit**, **Spigot**, **PaperSpigot**, **Fabric**, **Velocity**, and **BungeeCord**. 53 | - Your configuration will be migrated from previous versions, if detected. 54 | 55 | ### Features 56 | - Added `/tebex debug ` for in-depth logging 57 | - Added `/tebex report` to send in-game reports to Tebex 58 | - Added `/tebex goals` to print active community goals 59 | - Added `/tebex checkout` to checkout via command line 60 | - Added `/tebex sendlink` to send a checkout link to a player 61 | - Added automatic error reporting which can be toggled on/off in configuration. 62 | 63 | ### Changes 64 | - Use `/buy` command to open store GUI instead of using signs. 65 | 66 | ### Fixes 67 | - Less console spam. Enable in-depth logging with `/tebex debug` 68 | - Any API timeouts are now reported and handled gracefully -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/PlatformTelemetry.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform; 2 | 3 | /** 4 | * The PlatformTelemetry class contains information about the server platform 5 | * and environment, such as server software, plugin version, and Java version. 6 | */ 7 | public class PlatformTelemetry { 8 | private final String pluginVersion; 9 | private final String serverSoftware; 10 | private final String serverVersion; 11 | private final String javaVersion; 12 | private final String systemArch; 13 | private final boolean onlineMode; 14 | 15 | /** 16 | * Creates a PlatformTelemetry instance with the provided information. 17 | * 18 | * @param pluginVersion The plugin version. 19 | * @param serverSoftware The server software name. 20 | * @param serverVersion The server version. 21 | * @param javaVersion The Java version. 22 | * @param systemArch The system architecture. 23 | * @param onlineMode The server's online mode status. 24 | */ 25 | public PlatformTelemetry(String pluginVersion, String serverSoftware, String serverVersion, String javaVersion, String systemArch, boolean onlineMode) { 26 | this.pluginVersion = pluginVersion; 27 | this.serverSoftware = serverSoftware; 28 | this.serverVersion = serverVersion; 29 | this.javaVersion = javaVersion; 30 | this.systemArch = systemArch; 31 | this.onlineMode = onlineMode; 32 | } 33 | 34 | /** 35 | * Retrieves the plugin version. 36 | * 37 | * @return The plugin version. 38 | */ 39 | public String getPluginVersion() { 40 | return pluginVersion; 41 | } 42 | 43 | /** 44 | * Retrieves the server software name. 45 | * 46 | * @return The server software name. 47 | */ 48 | public String getServerSoftware() { 49 | return serverSoftware; 50 | } 51 | 52 | /** 53 | * Retrieves the server version. 54 | * 55 | * @return The server version. 56 | */ 57 | public String getServerVersion() { 58 | return serverVersion; 59 | } 60 | 61 | /** 62 | * Retrieves the Java version. 63 | * 64 | * @return The Java version. 65 | */ 66 | public String getJavaVersion() { 67 | return javaVersion; 68 | } 69 | 70 | /** 71 | * Retrieves the system architecture. 72 | * 73 | * @return The system architecture. 74 | */ 75 | public String getSystemArch() { 76 | return systemArch; 77 | } 78 | 79 | /** 80 | * Checks if the server is running in online mode. 81 | * 82 | * @return True if the server is in online mode, false otherwise. 83 | */ 84 | public boolean isOnlineMode() { 85 | return onlineMode; 86 | } 87 | 88 | /** 89 | * Generates a string representation of the PlatformTelemetry object. 90 | * 91 | * @return A string representation of the PlatformTelemetry object. 92 | */ 93 | @Override 94 | public String toString() { 95 | return "PlatformTelemetry{" + 96 | "pluginVersion='" + pluginVersion + '\'' + 97 | ", serverSoftware='" + serverSoftware + '\'' + 98 | ", serverVersion='" + serverVersion + '\'' + 99 | ", javaVersion='" + javaVersion + '\'' + 100 | ", systemArch='" + systemArch + '\'' + 101 | ", onlineMode=" + onlineMode + 102 | '}'; 103 | } 104 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/response/ServerInformation.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.response; 2 | 3 | public class ServerInformation { 4 | private final Store store; 5 | private final Server server; 6 | 7 | /** 8 | * Constructs a ServerInformation instance. 9 | * 10 | * @param store The store. 11 | * @param server The server. 12 | */ 13 | public ServerInformation(Store store, Server server) { 14 | this.store = store; 15 | this.server = server; 16 | } 17 | 18 | /** 19 | * Returns the store associated. 20 | * 21 | * @return The store. 22 | */ 23 | public Store getStore() { 24 | return store; 25 | } 26 | 27 | /** 28 | * Returns the server associated. 29 | * 30 | * @return The server. 31 | */ 32 | public Server getServer() { 33 | return server; 34 | } 35 | 36 | public static class Store { 37 | private final int id; 38 | private final String domain; 39 | private final String name; 40 | private final Currency currency; 41 | private final boolean onlineMode; 42 | private final String gameType; 43 | private final boolean logEvents; 44 | 45 | /** 46 | * Constructs a Store instance. 47 | * 48 | * @param id The store ID. 49 | * @param domain The store domain. 50 | * @param name The store name. 51 | * @param currency The store currency. 52 | * @param onlineMode The store online mode. 53 | * @param gameType The store game type. 54 | * @param logEvents The store log events. 55 | */ 56 | public Store(int id, String domain, String name, Currency currency, boolean onlineMode, String gameType, boolean logEvents) { 57 | this.id = id; 58 | this.domain = domain; 59 | this.name = name; 60 | this.currency = currency; 61 | this.onlineMode = onlineMode; 62 | this.gameType = gameType; 63 | this.logEvents = logEvents; 64 | } 65 | 66 | public int getId() { 67 | return id; 68 | } 69 | 70 | public String getDomain() { 71 | return domain; 72 | } 73 | 74 | public String getName() { 75 | return name; 76 | } 77 | 78 | public Currency getCurrency() { 79 | return currency; 80 | } 81 | 82 | public boolean isOnlineMode() { 83 | return onlineMode; 84 | } 85 | 86 | public String getGameType() { 87 | return gameType; 88 | } 89 | 90 | public boolean isLogEvents() { 91 | return logEvents; 92 | } 93 | 94 | public static class Currency { 95 | private final String iso4217; 96 | private final String symbol; 97 | 98 | public Currency(String iso4217, String symbol) { 99 | this.iso4217 = iso4217; 100 | this.symbol = symbol; 101 | } 102 | 103 | public String getIso4217() { 104 | return iso4217; 105 | } 106 | 107 | public String getSymbol() { 108 | return symbol; 109 | } 110 | } 111 | } 112 | 113 | public static class Server { 114 | private final int id; 115 | private final String name; 116 | 117 | public Server(int id, String name) { 118 | this.id = id; 119 | this.name = name; 120 | } 121 | 122 | public int getId() { 123 | return id; 124 | } 125 | 126 | public String getName() { 127 | return name; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/QueuedCommand.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | public class QueuedCommand { 4 | private final int id; 5 | private final String command; 6 | private final Integer payment; 7 | private final Integer packageId; // may be null 8 | private final Integer delay; 9 | private final Integer requiredSlots; 10 | private final QueuedPlayer player; 11 | private final boolean online; 12 | 13 | public QueuedCommand(int id, String command, int payment, int packageId, int delay, int requiredSlots) { 14 | this.id = id; 15 | this.command = command; 16 | this.payment = payment; 17 | this.packageId = packageId; 18 | this.delay = delay; 19 | this.requiredSlots = requiredSlots; 20 | this.player = null; 21 | this.online = true; 22 | } 23 | 24 | public QueuedCommand(int id, String command, int payment, int packageId, int delay, int requiredSlots, QueuedPlayer player) { 25 | this.id = id; 26 | this.command = command; 27 | this.payment = payment; 28 | this.packageId = packageId; 29 | this.delay = delay; 30 | this.requiredSlots = requiredSlots; 31 | this.player = player; 32 | this.online = false; 33 | } 34 | 35 | public int getId() { 36 | return id; 37 | } 38 | 39 | public String getCommand() { 40 | return command; 41 | } 42 | 43 | public String getParsedCommand() { 44 | String parsedCommand = command; 45 | 46 | if (player != null) { 47 | parsedCommand = parsedCommand.replace("{username}", player.getName()); 48 | parsedCommand = parsedCommand.replace("{name}", player.getName()); 49 | 50 | if (player.getUuid() != null) { // offline servers will return null uuid here as these uuids are not verified 51 | parsedCommand = parsedCommand.replace("{id}", player.getUuid()); 52 | parsedCommand = parsedCommand.replace("{uuid}", player.getUuid()); 53 | } else { // {id} must still be replaced with username if uuid is not present 54 | parsedCommand = parsedCommand.replace("{id}", player.getName()); 55 | } 56 | } 57 | 58 | return parsedCommand; 59 | } 60 | 61 | public int getPayment() { 62 | return payment == null ? 0 : payment; 63 | } 64 | 65 | /** 66 | * The package id relating to this command. 67 | * @return the package id. 0 if not provided 68 | */ 69 | public int getPackageId() { 70 | return packageId == null ? 0 : packageId; 71 | } 72 | 73 | /** 74 | * The execution delay required by command. 75 | * @return the delay in seconds 76 | */ 77 | public int getDelay() { 78 | return delay == null ? 0 : delay; 79 | } 80 | 81 | /** 82 | * The required slots required by this command. 83 | * @return the required slots 84 | */ 85 | public int getRequiredSlots() { 86 | return requiredSlots == null ? 0 : requiredSlots; 87 | } 88 | 89 | public QueuedPlayer getPlayer() { 90 | return player; 91 | } 92 | 93 | public boolean isOnline() { 94 | return online; 95 | } 96 | 97 | 98 | @Override 99 | public String toString() { 100 | return "QueuedCommand{" + 101 | "id=" + id + 102 | ", command='" + command + '\'' + 103 | ", payment=" + payment + 104 | ", packageId=" + packageId + 105 | ", delay=" + delay + 106 | ", requiredSlots=" + requiredSlots + 107 | ", player=" + player + 108 | ", online=" + online + 109 | '}'; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/util/ResourceUtil.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.util; 2 | 3 | import io.tebex.sdk.platform.Platform; 4 | import io.tebex.sdk.platform.PlatformType; 5 | 6 | import java.io.File; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.nio.file.Files; 11 | import java.util.logging.Level; 12 | 13 | public final class ResourceUtil { 14 | private ResourceUtil() { 15 | throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 16 | } 17 | 18 | /** 19 | * Get a file from the resources folder 20 | * @param platform The platform 21 | * @param directory The directory 22 | * @param fileName The file name 23 | * @return The file 24 | */ 25 | public static File getBundledFile(Platform platform, File directory, String fileName) { 26 | File file = new File(directory, fileName); 27 | 28 | if (!file.exists()) { 29 | if (!directory.exists()) { 30 | if (!directory.mkdirs()) { 31 | throw new RuntimeException("Failed to create plugin folder"); 32 | } 33 | } 34 | 35 | try { 36 | String type = "server"; 37 | 38 | if(platform.getType() == PlatformType.BUNGEECORD || platform.getType() == PlatformType.VELOCITY) { 39 | type = "proxy"; 40 | } 41 | 42 | // Copies the file from the resources' folder to the plugin folder 43 | Files.copy(getFile(type, fileName).toPath(), file.toPath()); 44 | } catch (IOException e) { 45 | platform.log(Level.SEVERE, String.format("Failed to copy %s to plugin folder", fileName)); 46 | } 47 | } 48 | 49 | return file; 50 | } 51 | 52 | /** 53 | * Extracts a resource from the jar file to the specified location. 54 | * @param fileName The name of the file to extract. 55 | * @return The file that was extracted. 56 | */ 57 | private static InputStream getFileFromResourceAsStream(String fileName) { 58 | 59 | // The class loader that loaded the class 60 | ClassLoader classLoader = ResourceUtil.class.getClassLoader(); 61 | InputStream inputStream = classLoader.getResourceAsStream(fileName); 62 | 63 | // the stream holding the file content 64 | if (inputStream == null) { 65 | throw new RuntimeException(String.format("File %s not found", fileName)); 66 | } else { 67 | return inputStream; 68 | } 69 | } 70 | 71 | /** 72 | * Extracts a resource from the jar file to the specified location. 73 | * @param platform The platform to extract the resource for. 74 | * @param fileName The name of the file to extract. 75 | * @return The file that was extracted. 76 | * @throws IOException If the file could not be extracted. 77 | */ 78 | private static File getFile(String platform, String fileName) throws IOException { 79 | InputStream inputStream = getFileFromResourceAsStream(String.format("platform/%s/%s", platform, fileName)); 80 | 81 | // Retrieve file name and extension 82 | String[] split = fileName.split("\\."); 83 | String name = split[0]; 84 | String extension = split[1]; 85 | 86 | // Convert the input stream to a File 87 | File tempFile = File.createTempFile(name, extension); 88 | 89 | try (FileOutputStream out = new FileOutputStream(tempFile)) { 90 | // Read the input stream and write it to the output stream 91 | int read; 92 | 93 | byte[] buffer = new byte[8192]; 94 | // Read the input stream using the buffer 95 | while ((read = inputStream.read(buffer)) != -1) { 96 | // Write the buffer to the output stream 97 | out.write(buffer, 0, read); 98 | } 99 | } 100 | 101 | return tempFile; 102 | } 103 | } -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/platform/config/ServerPlatformConfig.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.platform.config; 2 | 3 | import dev.dejvokep.boostedyaml.YamlDocument; 4 | 5 | /** 6 | * The PlatformConfig class holds the configuration for the Tebex SDK. 7 | * It contains settings related to excluded players, minimum playtime, and various other options. 8 | */ 9 | public class ServerPlatformConfig implements IPlatformConfig { 10 | private final int configVersion; 11 | private YamlDocument yamlDocument; 12 | 13 | 14 | private String buyCommandName; 15 | private boolean buyCommandEnabled; 16 | private boolean checkForUpdates; 17 | private boolean verbose; 18 | private boolean proxyMode; 19 | private String secretKey; 20 | 21 | private boolean autoReportEnabled; 22 | 23 | /** 24 | * Creates a PlatformConfig instance with the provided configuration version. 25 | * 26 | * @param configVersion The configuration version. 27 | */ 28 | public ServerPlatformConfig(int configVersion) { 29 | this.configVersion = configVersion; 30 | } 31 | 32 | /** 33 | * Sets the secret key. 34 | * 35 | * @param secretKey The secret key. 36 | */ 37 | public void setSecretKey(String secretKey) { 38 | this.secretKey = secretKey; 39 | } 40 | 41 | public void setBuyCommandName(String buyCommandName) { 42 | this.buyCommandName = buyCommandName; 43 | } 44 | 45 | public void setBuyCommandEnabled(boolean buyCommandEnabled) { 46 | this.buyCommandEnabled = buyCommandEnabled; 47 | } 48 | 49 | public void setCheckForUpdates(boolean checkForUpdates) { 50 | this.checkForUpdates = checkForUpdates; 51 | } 52 | 53 | public void setVerbose(boolean verbose) { 54 | this.verbose = verbose; 55 | } 56 | 57 | public void setProxyMode(boolean proxyMode) { 58 | this.proxyMode = proxyMode; 59 | } 60 | 61 | public void setAutoReportEnabled(boolean autoReportEnabled) { this.autoReportEnabled = autoReportEnabled; } 62 | 63 | /** 64 | * Sets the YAML document for this configuration. 65 | * 66 | * @param yamlDocument The YAML document. 67 | */ 68 | public void setYamlDocument(YamlDocument yamlDocument) { 69 | this.yamlDocument = yamlDocument; 70 | } 71 | 72 | /** 73 | * Returns the configuration version. 74 | * 75 | * @return The configuration version. 76 | */ 77 | @Override 78 | public int getConfigVersion() { 79 | return configVersion; 80 | } 81 | 82 | /** 83 | * Returns the secret key. 84 | * 85 | * @return The secret key. 86 | */ 87 | @Override 88 | public String getSecretKey() { 89 | return secretKey; 90 | } 91 | 92 | public String getBuyCommandName() { 93 | return buyCommandName; 94 | } 95 | 96 | public boolean isBuyCommandEnabled() { 97 | return buyCommandEnabled; 98 | } 99 | 100 | public boolean isCheckForUpdates() { 101 | return checkForUpdates; 102 | } 103 | 104 | @Override 105 | public boolean isVerbose() { 106 | return verbose; 107 | } 108 | 109 | public boolean isProxyMode() { 110 | return proxyMode; 111 | } 112 | 113 | public boolean isAutoReportEnabled() { return autoReportEnabled; } 114 | 115 | /** 116 | * Returns the YAML document for this configuration. 117 | * 118 | * @return The YAML document. 119 | */ 120 | @Override 121 | public YamlDocument getYamlDocument() { 122 | return yamlDocument; 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | return "ServerPlatformConfig{" + 128 | "configVersion=" + configVersion + 129 | ", yamlDocument=" + yamlDocument + 130 | ", buyCommandName='" + buyCommandName + '\'' + 131 | ", buyCommandEnabled=" + buyCommandEnabled + 132 | ", checkForUpdates=" + checkForUpdates + 133 | ", verbose=" + verbose + 134 | ", proxyMode=" + proxyMode + 135 | ", secretKey='" + secretKey + '\'' + 136 | ", autoReportEnabled=" + autoReportEnabled + 137 | '}'; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/builder/CreateCouponRequest.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request.builder; 2 | 3 | import io.tebex.sdk.obj.BasketType; 4 | import io.tebex.sdk.obj.DiscountType; 5 | 6 | import java.time.LocalDate; 7 | import java.util.List; 8 | 9 | public class CreateCouponRequest { 10 | public enum EffectiveOn { 11 | PACKAGE, 12 | CATEGORY, 13 | CART 14 | } 15 | 16 | public enum DiscountMethod { 17 | EACH_PACKAGE(0), 18 | BASKET_BEFORE_SALES(1), 19 | BASKET_AFTER_SALES(2); 20 | 21 | private final int value; 22 | 23 | DiscountMethod(int value) { 24 | this.value = value; 25 | } 26 | 27 | public int getValue() { 28 | return value; 29 | } 30 | } 31 | 32 | private final String code; 33 | private final EffectiveOn effectiveOn; 34 | private final List effectiveIds; 35 | private final DiscountType discountType; 36 | private final int discountValue; 37 | private final LocalDate startDate; 38 | 39 | private int minimum; 40 | private DiscountMethod discountMethod; 41 | private boolean redeemUnlimited; 42 | private boolean canExpire; 43 | private LocalDate expiryDate; 44 | private int expiryLimit; 45 | private final BasketType basketType; 46 | private String username; 47 | private String note; 48 | 49 | public CreateCouponRequest(String code, EffectiveOn effectiveOn, List effectiveIds, DiscountType discountType, int discountValue, LocalDate startDate) { 50 | this.code = code; 51 | this.effectiveOn = effectiveOn; 52 | this.effectiveIds = effectiveIds; 53 | this.discountType = discountType; 54 | this.discountValue = discountValue; 55 | this.startDate = startDate; 56 | 57 | this.minimum = 0; 58 | this.discountMethod = DiscountMethod.EACH_PACKAGE; 59 | this.redeemUnlimited = true; 60 | this.basketType = BasketType.BOTH; 61 | } 62 | 63 | public String getCode() { 64 | return code; 65 | } 66 | 67 | public EffectiveOn getEffectiveOn() { 68 | return effectiveOn; 69 | } 70 | 71 | public List getEffectiveIds() { 72 | return effectiveIds; 73 | } 74 | 75 | public DiscountType getDiscountType() { 76 | return discountType; 77 | } 78 | 79 | public int getDiscountValue() { 80 | return discountValue; 81 | } 82 | 83 | public LocalDate getStartDate() { 84 | return startDate; 85 | } 86 | 87 | public int getMinimum() { 88 | return minimum; 89 | } 90 | 91 | public DiscountMethod getDiscountMethod() { 92 | return discountMethod; 93 | } 94 | 95 | public boolean canRedeemUnlimited() { 96 | return redeemUnlimited; 97 | } 98 | 99 | public boolean canExpire() { 100 | return canExpire; 101 | } 102 | 103 | public LocalDate getExpiryDate() { 104 | return expiryDate; 105 | } 106 | 107 | public int getExpiryLimit() { 108 | return expiryLimit; 109 | } 110 | 111 | public BasketType getBasketType() { 112 | return basketType; 113 | } 114 | 115 | public String getUsername() { 116 | return username; 117 | } 118 | 119 | public String getNote() { 120 | return note; 121 | } 122 | 123 | // Setter methods 124 | public void setMinimumBasketValue(int minimumBasketValue) { 125 | this.minimum = minimumBasketValue; 126 | } 127 | 128 | public void setDiscountMethod(DiscountMethod discountMethod) { 129 | this.discountMethod = discountMethod; 130 | } 131 | 132 | public void setUnlimitedRedeems(boolean redeemUnlimited) { 133 | this.redeemUnlimited = redeemUnlimited; 134 | } 135 | 136 | public void setExpiryDate(LocalDate expiryDate) { 137 | this.expiryDate = expiryDate; 138 | } 139 | 140 | public void setExpiryLimit(int expiryLimit) { 141 | this.expiryLimit = expiryLimit; 142 | } 143 | 144 | public void setCanExpire(boolean canExpire) { 145 | this.canExpire = canExpire; 146 | } 147 | 148 | public void setUsername(String username) { 149 | this.username = username; 150 | } 151 | 152 | public void setNote(String note) { 153 | this.note = note; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Tests all variants of the plugin 4 | 5 | # Test parameters 6 | TEBEX_TEST_SECRET_KEY="" # normal game server secret key to test setting secret 7 | 8 | # URLs to the JAR files we can download 9 | BUKKIT_JAR_URL='https://download.getbukkit.org/craftbukkit/craftbukkit-1.20.4.jar' 10 | BUNGEE_JAR_URL='https://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/BungeeCord.jar' 11 | SPIGOT_JAR_URL='https://download.getbukkit.org/spigot/spigot-1.20.4.jar' 12 | PAPER_JAR_URL='https://api.papermc.io/v2/projects/paper/versions/1.20.4/builds/381/downloads/paper-1.20.4-381.jar' 13 | PURPUR_JAR_URL='https://api.purpurmc.org/v2/purpur/1.20.4/latest/download' 14 | VELOCITY_JAR_URL='https://api.papermc.io/v2/projects/velocity/versions/3.3.0-SNAPSHOT/builds/316/downloads/velocity-3.3.0-SNAPSHOT-316.jar' 15 | 16 | # Record the start time 17 | SECONDS=0 18 | 19 | function downloadJar() { 20 | local folder="$1" 21 | local url="$2" 22 | local filename="${url##*/}" 23 | 24 | # Ensure the folder path is within the current working directory 25 | if [[ "$folder" = /* || "$folder" = ../* ]]; then 26 | echo "Error: The specified folder must be within the current working directory." 27 | return 1 28 | fi 29 | 30 | # Check if the folder exists 31 | if [[ -d "$folder" ]]; then 32 | echo "Deleting existing folder: $folder" 33 | #rm -rf "$folder" 34 | fi 35 | 36 | # Create the folder 37 | mkdir -p "$folder" 38 | 39 | # Download the file into the specified folder 40 | echo "Downloading file to $folder/$filename" 41 | wget -P "$folder" "$url" 42 | } 43 | 44 | function testCommands() { 45 | local javaPid=$1 46 | local pipe=$2 47 | local command="/tebex help" 48 | } 49 | 50 | function testJar() { 51 | local folder="$1" 52 | local jarName="$2" 53 | 54 | echo "Testing .jar '$jarName' in '$folder'" 55 | cd $folder 56 | echo "eula=true" >> "eula.txt" 57 | 58 | java -Xmx2G -jar "$jarName" nogui 59 | javaPid=$! 60 | echo "Waiting for server startup..." 61 | 62 | mkdir -p "./logs/" 63 | touch "./logs/latest.log" 64 | tail -f "./logs/latest.log" | while read LINE; do 65 | echo "$LINE" | grep "Welcome to Tebex" &> /dev/null 66 | if [ $? -eq 0 ]; then 67 | echo "> Tebex loaded successfully, terminating server process..." 68 | #testCommands $javaPid $pipe 69 | sleep 2 70 | kill $javaPid 71 | break 72 | fi 73 | done 74 | 75 | kill $javaPid 76 | echo "Test completed." 77 | cd .. 78 | } 79 | 80 | function installPluginToFolder() { 81 | pluginType="$1" #ex. "bukkit", corresponding with type in build/libs: tebex-{pluginType}-2.0.0.jar 82 | toFolder="$2" # root minecraft server folder containing /plugins 83 | 84 | mkdir -p "$toFolder/plugins" 85 | cp "../build/libs/tebex-$pluginType-2.0.4.jar" "$toFolder/plugins" 86 | } 87 | 88 | function copyWorlds() { 89 | from="./$1" 90 | to="./$2" 91 | 92 | cp -r "$1/world" "$2/world" 93 | cp -r "$1/world_nether" "$2/world_nether" 94 | cp -r "$1/world_the_end" "$2/world_the_end" 95 | } 96 | # Initial setup 97 | rm -rf test 98 | mkdir test 99 | cd test 100 | 101 | # Test paper first as it's the fastest to generate the world. We'll copy the world to all other folders to prevent waiting 102 | # on world generation while testing. 103 | 104 | # Paper 105 | echo "Testing Paper..." 106 | downloadJar "paper" "$PAPER_JAR_URL" 107 | installPluginToFolder "bukkit" "./paper" 108 | testJar "paper" ${PAPER_JAR_URL##*/} # take filename from end of url 109 | 110 | # Bukkit 111 | #echo "Testing Bukkit..." 112 | #downloadJar "bukkit" "$BUKKIT_JAR_URL" 113 | #installPluginToFolder "bukkit" "./bukkit" 114 | #copyWorlds "paper" "bukkit" 115 | #testJar "bukkit" ${BUKKIT_JAR_URL##*/} # take filename from end of url 116 | 117 | # Bungee 118 | #echo "Testing BungeeCord..." 119 | #downloadJar "bungeecord" "$BUNGEE_JAR_URL" 120 | #installPluginToFolder "bungeecord" "./bungeecord" 121 | #testJar "bungeecord" ${BUNGEE_JAR_URL##*/} # take filename from end of url 122 | 123 | # Spigot 124 | #echo "Testing Spigot..." 125 | #downloadJar "spigot" "$SPIGOT_JAR_URL" 126 | #installPluginToFolder "bukkit" "./spigot" 127 | #copyWorlds "paper" "spigot" 128 | #testJar "spigot" ${SPIGOT_JAR_URL##*/} # take filename from end of url 129 | 130 | # Purpur 131 | #echo "Testing Purpur..." 132 | #downloadJar "purpur" "$PURPUR_JAR_URL" 133 | #installPluginToFolder "bukkit" "./purpur" 134 | #copyWorlds "paper" "spigot" 135 | #testJar "purpur" ${PURPUR_JAR_URL##*/} # take filename from end of url 136 | 137 | # Velocity 138 | #echo "Testing Velocity..." 139 | #downloadJar "velocity" "$VELOCITY_JAR_URL" 140 | #installPluginToFolder "velocity" "./velocity" 141 | #testJar "velocity" ${VELOCITY_JAR_URL##*/} # take filename from end of url 142 | 143 | elapsedTime=$SECONDS 144 | echo "Testing completed in $elapsedTime seconds" 145 | exit -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/PlayerLookupInfo.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class PlayerLookupInfo { 13 | public Player player; 14 | public int banCount; 15 | public int chargebackRate; 16 | public List payments; 17 | public Map purchaseTotals; 18 | 19 | // Getters 20 | public Player getLookupPlayer() { 21 | return player; 22 | } 23 | 24 | public int getBanCount() { 25 | return banCount; 26 | } 27 | 28 | public int getChargebackRate() { 29 | return chargebackRate; 30 | } 31 | 32 | public List getPayments() { 33 | return payments; 34 | } 35 | 36 | public Map getPurchaseTotals() { 37 | return purchaseTotals; 38 | } 39 | 40 | // Nested Player class 41 | public static class Player { 42 | public String id; 43 | public String username; 44 | public String meta; 45 | public int pluginUsernameId; 46 | 47 | // Getters 48 | public String getId() { 49 | return id; 50 | } 51 | 52 | public String getUsername() { 53 | return username; 54 | } 55 | 56 | public String getMeta() { 57 | return meta; 58 | } 59 | 60 | public int getPluginUsernameId() { 61 | return pluginUsernameId; 62 | } 63 | } 64 | 65 | // Nested Payment class 66 | public static class Payment { 67 | public String txnId; 68 | public long time; 69 | public double price; 70 | public String currency; 71 | public int status; 72 | 73 | // Getters 74 | public String getTxnId() { 75 | return txnId; 76 | } 77 | 78 | public long getTime() { 79 | return time; 80 | } 81 | 82 | public double getPrice() { 83 | return price; 84 | } 85 | 86 | public String getCurrency() { 87 | return currency; 88 | } 89 | 90 | public int getStatus() { 91 | return status; 92 | } 93 | } 94 | 95 | public static PlayerLookupInfo fromJsonObject(JsonObject jsonObject) { 96 | // Parse Player object 97 | JsonObject playerJson = jsonObject.get("player").getAsJsonObject(); 98 | Player player = new Player(); 99 | player.id = playerJson.get("id").getAsString(); 100 | player.username = playerJson.get("username").getAsString(); 101 | player.meta = playerJson.get("meta").getAsString(); 102 | player.pluginUsernameId = playerJson.get("plugin_username_id").getAsInt(); 103 | 104 | // Parse banCount and chargebackRate 105 | int banCount = jsonObject.get("banCount").getAsInt(); 106 | int chargebackRate = jsonObject.get("chargebackRate").getAsInt(); 107 | 108 | // Parse Payments array 109 | JsonArray paymentsJsonArray = jsonObject.get("payments").getAsJsonArray(); 110 | List payments = new ArrayList<>(); 111 | for (JsonElement paymentElement : paymentsJsonArray) { 112 | JsonObject paymentJson = paymentElement.getAsJsonObject(); 113 | Payment payment = new Payment(); 114 | payment.txnId = paymentJson.get("txn_id").getAsString(); 115 | payment.time = paymentJson.get("time").getAsLong(); 116 | payment.price = paymentJson.get("price").getAsDouble(); 117 | payment.currency = paymentJson.get("currency").getAsString(); 118 | payment.status = paymentJson.get("status").getAsInt(); 119 | payments.add(payment); 120 | } 121 | 122 | // Construct and return the PlayerLookupInfo object 123 | PlayerLookupInfo playerLookupInfo = new PlayerLookupInfo(); 124 | playerLookupInfo.player = player; 125 | playerLookupInfo.banCount = banCount; 126 | playerLookupInfo.chargebackRate = chargebackRate; 127 | playerLookupInfo.payments = payments; 128 | 129 | // Parse purchaseTotals map 130 | JsonElement purchaseTotalsJson = jsonObject.get("purchaseTotals"); 131 | if (purchaseTotalsJson.isJsonObject()) { // empty 132 | JsonObject purchaseTotalsObj = purchaseTotalsJson.getAsJsonObject(); 133 | Map purchaseTotals = new HashMap<>(); 134 | for (Map.Entry entry : purchaseTotalsObj.entrySet()) { 135 | purchaseTotals.put(entry.getKey(), entry.getValue().getAsDouble()); 136 | } 137 | playerLookupInfo.purchaseTotals = purchaseTotals; 138 | } 139 | 140 | return playerLookupInfo; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/obj/CommunityGoal.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.obj; 2 | 3 | import com.google.gson.JsonObject; 4 | import io.tebex.sdk.util.StringUtil; 5 | 6 | import java.time.ZonedDateTime; 7 | 8 | public class CommunityGoal { 9 | private final int id; 10 | private final ZonedDateTime createdAt; 11 | private final ZonedDateTime updatedAt; 12 | private final int accountId; 13 | private final String name; 14 | private final String description; 15 | private final String image; 16 | private final double target; 17 | private final double current; 18 | private final boolean repeatable; 19 | private final ZonedDateTime lastAchieved; 20 | private final int timesAchieved; 21 | private final Status status; 22 | private final boolean sale; 23 | 24 | /** 25 | * Constructs a CommunityGoal instance. 26 | */ 27 | public CommunityGoal(int id, ZonedDateTime createdAt, ZonedDateTime updatedAt, int accountId, String name, String description, String image, double target, double current, boolean repeatable, ZonedDateTime lastAchieved, int timesAchieved, Status status, boolean sale) { 28 | this.id = id; 29 | this.createdAt = createdAt; 30 | this.updatedAt = updatedAt; 31 | this.accountId = accountId; 32 | this.name = name; 33 | this.description = description; 34 | this.image = image; 35 | this.target = target; 36 | this.current = current; 37 | this.repeatable = repeatable; 38 | this.lastAchieved = lastAchieved; 39 | this.timesAchieved = timesAchieved; 40 | this.status = status; 41 | this.sale = sale; 42 | } 43 | 44 | public int getId() { 45 | return id; 46 | } 47 | 48 | public ZonedDateTime getCreatedAt() { 49 | return createdAt; 50 | } 51 | 52 | public ZonedDateTime getUpdatedAt() { 53 | return updatedAt; 54 | } 55 | 56 | public int getAccountId() { 57 | return accountId; 58 | } 59 | 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | public String getDescription() { 65 | return description; 66 | } 67 | 68 | public String getImage() { 69 | return image; 70 | } 71 | 72 | public double getTarget() { 73 | return target; 74 | } 75 | 76 | public double getCurrent() { 77 | return current; 78 | } 79 | 80 | public boolean isRepeatable() { 81 | return repeatable; 82 | } 83 | 84 | public ZonedDateTime getLastAchieved() { 85 | return lastAchieved; 86 | } 87 | 88 | public int getTimesAchieved() { 89 | return timesAchieved; 90 | } 91 | 92 | public Status getStatus() { 93 | return status; 94 | } 95 | 96 | public boolean hasSale() { 97 | return sale; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "CommunityGoal{" + 103 | "id=" + id + 104 | ", createdAt=" + createdAt + 105 | ", updatedAt=" + updatedAt + 106 | ", accountId=" + accountId + 107 | ", name='" + name + '\'' + 108 | ", description='" + description + '\'' + 109 | ", image='" + image + '\'' + 110 | ", target=" + target + 111 | ", current=" + current + 112 | ", repeatable=" + repeatable + 113 | ", lastAchieved=" + lastAchieved + 114 | ", timesAchieved=" + timesAchieved + 115 | ", status='" + status.name() + '\'' + 116 | ", sale=" + sale + 117 | '}'; 118 | } 119 | 120 | public enum Status { 121 | ACTIVE, 122 | COMPLETED, 123 | DISABLED 124 | } 125 | 126 | public static CommunityGoal fromJsonObject(JsonObject jsonObject) { 127 | return new CommunityGoal( 128 | jsonObject.get("id").getAsInt(), 129 | StringUtil.toLegacyDate(jsonObject.get("created_at").getAsString()), 130 | StringUtil.toLegacyDate(jsonObject.get("updated_at").getAsString()), 131 | jsonObject.get("account").getAsInt(), 132 | jsonObject.get("name").getAsString(), 133 | jsonObject.get("description").getAsString(), 134 | !jsonObject.get("image").getAsString().isEmpty() ? jsonObject.get("image").getAsString() : null, 135 | jsonObject.get("target").getAsDouble(), 136 | jsonObject.get("current").getAsDouble(), 137 | jsonObject.get("repeatable").getAsInt() != 0, 138 | !jsonObject.get("last_achieved").isJsonNull() ? StringUtil.toLegacyDate(jsonObject.get("last_achieved").getAsString()) : null, 139 | jsonObject.get("times_achieved").getAsInt(), 140 | CommunityGoal.Status.valueOf(jsonObject.get("status").getAsString().toUpperCase()), 141 | jsonObject.get("sale").getAsBoolean() 142 | ); 143 | } 144 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://www.tebex.io/assets/img/logos/tebex.svg#gh-light-mode-only) 2 | ![Static Badge](https://img.shields.io/badge/spigot-1.8--1.20-brightgreen) 3 | ![Static Badge](https://img.shields.io/badge/fabric-1.16.5+-brightgreen) 4 | ![Static Badge](https://img.shields.io/badge/bungeecord-1.18+-brightgreen) 5 | ![Static Badge](https://img.shields.io/badge/waterfall-1.18+-brightgreen) 6 | ![Static Badge](https://img.shields.io/badge/velocity-1.16.5+-brightgreen) 7 | 8 | ## What Is Tebex? 9 | Tebex is a game monetization engine featuring over 120 payment methods, chargeback protection, fraud protection, and 3-day payouts. Tebex allows you to sell items, subscriptions, and more from an in-game customized shop. 10 | 11 | Players browse your store, select and purchase their items, and Tebex automatically delivers the items when the player is next online. View more at https://tebex.io 12 | 13 | See an interactive Tebex store using one of our free templates at https://example.tebex.io/ 14 | 15 | ## Features 16 | - Simple and Powerful. Create fully customizable themed web stores and start selling in minutes. 17 | - No Hidden Fees. Enjoy flat-rate pricing with no surprises. 18 | - Worldwide Payments Support. Accept over 120+ payment types with Tebex acting as your merchant of record, including PayPal, Paysafecard, Google Pay, and more. 19 | - Chargeback and Fraud Protection. Tebex handles fraud reports and disputes/chargebacks on your behalf while providing 100% insurance. 20 | - Made for Everyone. Whether you’re starting or an established network, Tebex offers a comprehensive set of shop management tools to handle every facet of your game’s economy. 21 | 22 | ## Installation and Setup 23 | 1. Create a free webstore at https://tebex.io/ 24 | 2. Download the latest version of the plugin from the Releases tab of this repository. 25 | 3. Place the downloaded Tebex `.jar` in the `plugins` folder of your relevant platform. 26 | 4. Restart your server / reload your plugins 27 | 5. Run `tebex.secret your-key-here` as a server admin to connect the server to Tebex. 28 | 29 | Your secret key can always be found at: https://creator.tebex.io/game-servers. Click Connect Game Server, and then choose Plugin to view your secret key. 30 | 31 | ## Usage and Commands 32 | Tebex will automatically fulfill any orders from your webstore every two minutes. These are run as server commands, such as giving items or adding groups, which you can define in your store. 33 | 34 | Note: Not all commands are available on all platforms. Proxy servers may have a reduced set of commands. Use `tebex.help` to get the relevant list of commands on any platform. 35 | 36 | Below are a list of commands used to manage the plugin: 37 | 38 | ### Permissions 39 | All commands have a permission node which matches with the exact command name. For example a player must have `tebex.help` as a permission in order to view available 40 | commands. 41 | 42 | ### User Commands 43 | ``` 44 | tebex.help Shows available commands 45 | tebex.secret Sets your store’s secret key 46 | tebex.info Shows store information 47 | tebex.checkout Creates payment link for a package 48 | ``` 49 | 50 | ### Administrator Commands 51 | ``` 52 | tebex.sendlink Sends payment link to player 53 | tebex.report Reports a problem to Tebex 54 | tebex.ban Bans a user from the webstore 55 | tebex.lookup Looks up user transaction info 56 | ``` 57 | 58 | ### Debug Commands 59 | ``` 60 | tebex.debug Enables debug logging 61 | tebex.forcecheck Force runs all time-based events 62 | tebex.refresh Reloads store and package info 63 | ``` 64 | 65 | ## Resources 66 | Here are some additional resources to help you build your Tebex store. 67 | 68 | - ❔ [Frequently Asked Questions](https://docs.tebex.io/creators/faq) - Get answers to common questions fast 69 | - 🧠 [Tebex Academy](https://www.youtube.com/@tebex/videos) - Learn to build a successful Tebex store with video tutorials 70 | - ⚙️ [Technical Support](mailto:support@tebex.io) - Get assistance as a buyer or seller, email us at support@tebex.io 71 | - 🖥️ [Developer Documentation](https://docs.tebex.io/developers/) - Develop custom integrations to suit your needs 72 | - 💬 [Feedback Form](https://wkf.ms/45PQwfE) - Help us build a better product by sharing your feedback 73 | 74 | ## Developer Resources 75 | All of our plugins are open source and welcome contributions from the community. If you wish to make a contribution, please review **CONTRIBUTING.md** for guidelines 76 | and things to know before making your contribution. 77 | 78 | ### Environment Requirements 79 | - JDK 17 80 | - Gradle 8.2 81 | 82 | ### Building 83 | To build all plugins, simply run `./build.sh`. This will execute the Gradle wrapper, build all plugins, and place their `.jar` files in in `build/libs/` 84 | 85 | ## Our Mission 86 | Founded in 2011, our mission has always been the same: helping creators in the gaming industry create new revenue streams without having to invest the time and effort involved in processing and managing global payments. 87 | 88 | Since then, we helped generate over half a billion dollars for our clients, providing them with a full suite of monetization features, handling all taxes, billing, and providing full insurance. Making sure they can focus on what they do best - creating great gaming experiences. -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/triage/TriageEvent.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.triage; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.annotations.SerializedName; 5 | import io.tebex.sdk.platform.Platform; 6 | import io.tebex.sdk.request.TebexRequest; 7 | import io.tebex.sdk.request.response.ServerInformation; 8 | import okhttp3.Response; 9 | import okhttp3.ResponseBody; 10 | 11 | import java.io.IOException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.ExecutionException; 15 | 16 | /** 17 | * A TriageEvent is an indicator of a potential problem that is automatically reported to Tebex. 18 | * Information about the Platform at runtime is provided as well as an error message and/or trace 19 | * of the error if applicable. 20 | */ 21 | public class TriageEvent { 22 | private transient final Platform _platform; 23 | 24 | @SerializedName(value = "game_id") 25 | private String gameId; 26 | @SerializedName(value = "framework_id") 27 | private String frameworkId; 28 | @SerializedName(value = "plugin_version") 29 | private String pluginVersion; 30 | @SerializedName(value = "server_ip") 31 | private String serverIp; 32 | @SerializedName(value = "error_message") 33 | private String errorMessage; 34 | private Map metadata; 35 | @SerializedName(value = "store_name") 36 | private String storeName; 37 | @SerializedName(value = "store_url") 38 | private String storeUrl; 39 | 40 | private TriageEvent(Platform platform){ 41 | this._platform = platform; 42 | } 43 | 44 | public static TriageEvent fromPlatform(Platform platform) { 45 | TriageEvent event = new TriageEvent(platform); 46 | 47 | event.gameId = "Minecraft " + platform.getTelemetry().getServerSoftware(); 48 | event.frameworkId = platform.getTelemetry().getServerSoftware() 49 | + " " + platform.getTelemetry().getServerVersion() 50 | + " " + platform.getTelemetry().getJavaVersion(); 51 | event.pluginVersion = platform.getTelemetry().getPluginVersion(); 52 | 53 | event.serverIp = platform.getServerIp(); 54 | if (event.serverIp == null || event.serverIp.isEmpty()) { 55 | event.serverIp = "0.0.0.0"; 56 | } 57 | 58 | // Assign store info if secret key is set 59 | if (platform.getSDK().getSecretKey() != null) { 60 | ServerInformation serverInfo; 61 | try { 62 | serverInfo = platform.getSDK().getServerInformation().get(); 63 | event.storeName = serverInfo.getStore().getName(); 64 | event.storeUrl = serverInfo.getStore().getDomain(); 65 | } catch (InterruptedException | ExecutionException e) { 66 | // store name and info will remain unfilled 67 | } 68 | } 69 | 70 | return event; 71 | } 72 | 73 | public TriageEvent withGameId(String value) { 74 | this.gameId = value; 75 | return this; 76 | } 77 | 78 | public TriageEvent withFrameworkId(String value) { 79 | this.frameworkId = value; 80 | return this; 81 | } 82 | 83 | public TriageEvent withPluginVersion(String value) { 84 | this.pluginVersion = value; 85 | return this; 86 | } 87 | 88 | public TriageEvent withServerIp(String value) { 89 | this.serverIp = value; 90 | return this; 91 | } 92 | 93 | public TriageEvent withErrorMessage(String value) { 94 | this.errorMessage = value; 95 | return this; 96 | } 97 | 98 | public TriageEvent withMetadata(HashMap value) { 99 | this.metadata = value; 100 | return this; 101 | } 102 | 103 | public TriageEvent withStoreName(String value) { 104 | this.storeName = value; 105 | return this; 106 | } 107 | 108 | public TriageEvent withStoreUrl(String value) { 109 | this.storeUrl = value; 110 | return this; 111 | } 112 | 113 | public String toJsonString() { 114 | Gson gson = new Gson(); 115 | return gson.toJson(this); 116 | } 117 | 118 | public static TriageEvent fromJson(String json) { 119 | Gson gson = new Gson(); 120 | return gson.fromJson(json, TriageEvent.class); 121 | } 122 | 123 | public void send() { 124 | TebexRequest triageEventRequest = _platform.getSDK().request("https://plugin-logs.tebex.io/", false) 125 | .withBody(this.toJsonString(), "POST"); 126 | 127 | // Store name is set automatically by fromPlatform 128 | if (this.storeName != null && this.storeName.equals("")) { 129 | _platform.debug("No store info while sending triage event, ignoring event"); 130 | return; 131 | } 132 | 133 | // Send the event to plugin logs 134 | try { 135 | Response triageResponse = triageEventRequest.send(); 136 | 137 | if (!triageResponse.isSuccessful()) { 138 | _platform.debug("Failed to send triage event!"); 139 | ResponseBody responseBody = triageResponse.body(); 140 | 141 | if (responseBody != null) { 142 | _platform.debug(responseBody.string()); 143 | } else { 144 | _platform.debug("Empty response from plugin logs when sending triage event"); 145 | } 146 | } 147 | 148 | triageResponse.close(); 149 | } catch (IOException e) { 150 | _platform.debug("Unexpected error sending triage event!"); 151 | _platform.debug(e.getMessage()); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sdk/src/main/java/io/tebex/sdk/request/TebexRequest.java: -------------------------------------------------------------------------------- 1 | package io.tebex.sdk.request; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import io.tebex.sdk.Tebex; 5 | import okhttp3.*; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.io.IOException; 9 | import java.util.concurrent.CompletableFuture; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | 13 | /** 14 | * A class for constructing and executing HTTP requests using the OkHttp library. 15 | */ 16 | public class TebexRequest { 17 | private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("analyse-#%1$d").build()); 18 | 19 | private final Request.Builder request; 20 | private final OkHttpClient client; 21 | 22 | /** 23 | * Constructs an TebexRequest instance with a specified endpoint and OkHttpClient. 24 | * 25 | * @param endpoint The URL to send the request to. 26 | * @param client The OkHttpClient instance to be used for executing the request. 27 | */ 28 | public TebexRequest(String endpoint, OkHttpClient client) { 29 | this.request = new Request.Builder().url(endpoint); 30 | this.client = client; 31 | } 32 | 33 | /** 34 | * Adds a header to the request. 35 | * 36 | * @param key The header key. 37 | * @param value The header value. 38 | * @return The TebexRequest instance for chaining. 39 | */ 40 | public TebexRequest withHeader(String key, String value) { 41 | request.addHeader(key, value); 42 | return this; 43 | } 44 | 45 | /** 46 | * Sets the request body and method to POST. 47 | * 48 | * @param body The request body in JSON format. 49 | * @return The TebexRequest instance for chaining. 50 | */ 51 | public TebexRequest withBody(String body) { 52 | return withBody(body, "POST"); 53 | } 54 | 55 | /** 56 | * Sets the request body and method. 57 | * 58 | * @param body The request body in JSON format. 59 | * @return The TebexRequest instance for chaining. 60 | */ 61 | public TebexRequest withBody(String body, String method) { 62 | request.method(method, RequestBody.create(body, MediaType.get("application/json; charset=utf-8"))); 63 | return this; 64 | } 65 | 66 | /** 67 | * Sets the request as a delete request. 68 | * 69 | * @return The TebexRequest instance for chaining. 70 | */ 71 | public TebexRequest delete() { 72 | request.delete(); 73 | return this; 74 | } 75 | 76 | /** 77 | * Adds the secret key as a header to the request. 78 | * 79 | * @param secretKey The secret key. 80 | * @return The TebexRequest instance for chaining. 81 | */ 82 | public TebexRequest withSecretKey(String secretKey) { 83 | return withHeader("X-Tebex-Secret", secretKey); 84 | } 85 | 86 | /** 87 | * Builds the request into a Call object. 88 | * 89 | * @return The Call object representing the request. 90 | */ 91 | public Call build() { 92 | return client.newCall(request.build()); 93 | } 94 | 95 | /** 96 | * Executes the request synchronously. 97 | * 98 | * @return The Response object from the server. 99 | * @throws IOException If there is a problem executing the request. 100 | */ 101 | public Response send() throws IOException { 102 | return build().execute(); 103 | } 104 | 105 | /** 106 | * Executes the request asynchronously and returns a CompletableFuture. 107 | * 108 | * @return The CompletableFuture that will complete with the server Response. 109 | */ 110 | public CompletableFuture sendAsync() { 111 | CompletableFuture future = new CompletableFuture<>(); 112 | 113 | Call call = this.build(); 114 | Request request = call.request(); 115 | 116 | EXECUTOR.submit(() -> call.enqueue(new Callback() { 117 | @Override 118 | public void onFailure(@NotNull Call call, @NotNull IOException e) { 119 | Tebex.get().debug(call.request().toString()); 120 | future.completeExceptionally(e); 121 | } 122 | 123 | @Override 124 | public void onResponse(@NotNull Call call, @NotNull Response response) { 125 | ResponseBody responseBody = response.body(); 126 | Tebex.get().debug(String.format("%1$d <- %2$s %3$s ", response.code(), request.method(), request.url())); 127 | 128 | if (responseBody != null) { 129 | try { 130 | // in debug mode we consume the response body and display it in the logs, then rebuild a new response. 131 | // this is inefficient so only do this when debug mode is enabled 132 | if (Tebex.get().getPlatformConfig().isVerbose()) { 133 | String responseBodyString = responseBody.string(); 134 | Tebex.get().debug(String.format(" | %1$s", responseBodyString)); 135 | ResponseBody clonedBody = ResponseBody.create(responseBodyString, 136 | responseBody.contentType()); 137 | Response clonedResponse = response.newBuilder().body(clonedBody).build(); 138 | future.complete(clonedResponse); 139 | clonedResponse.close(); //close duplicated response 140 | } else { // outside of debug mode we can still use the original response 141 | future.complete(response); 142 | } 143 | } catch (IOException e) { 144 | Tebex.get().debug(" error reading response body: " + e.getMessage()); 145 | future.completeExceptionally(e); 146 | } 147 | } else { // no response body, complete normally 148 | future.complete(response); 149 | } 150 | 151 | // close original response 152 | response.close(); 153 | } 154 | })); 155 | 156 | return future; 157 | } 158 | } 159 | --------------------------------------------------------------------------------