17 Commits
Author SHA1 Message Date
MrTransistor 6f3b9bf1b3 1.19.2 update (changed spigot version in gradle build script, changed CraftBukkit version, changed chat.Component.getContents() to .getString())
Added e.printStackTrace when CommandDispatcher is failing to find a method
2022-08-19 19:25:20 +03:00
MrTransistor 6c44745317 Added more WIP pathfinding stuff 2022-04-15 22:45:03 +03:00
MrTransistor 85884dfb42 Merge remote-tracking branch 'main/master' 2022-04-07 01:10:22 +03:00
MrTransistor 4ab5b3319f [+] Added locale field to prevent 3rd party plugins that are using aikar`s ACF library from throwing NoSuchFieldException when a bot joins.
[+] Added pathfinding test (commented out)
[+] Bots now don't shoot at endermen and dead entitied
[+] Shooting range increased from 35 to 50 blocks
[+] /afkutils controlBot changed to /afkutils control
2022-04-07 01:07:53 +03:00
MrTransistor 3fcfacd74e [+] Added locale field to prevent 3rd party plugins that are using aikar`s ACF library from throwing NoSuchFieldException when a bot joins.
[+] Added pathfinding test (commented out)
[+] Bots now don`t shoot at endermen
[+] /afkutils controlBot changed to /afkutils control
2022-04-07 01:04:25 +03:00
MrTransistor 677a6ee101 Added ranged attacks!!! (finally) Bots can now use bows, crossbows, tridents and snowballs/eggs to attack nearest mob with command.
Fixed some bugs to do with occupied names.
2022-04-06 01:11:49 +03:00
MrTransistor 585696552a [-] move xp calculation to DummyPlayer#die() - can`t do but fixed some problems with xp drops
[+] use kick instead of force disconnecting when removing with specified command
[+] Prevent dummies from overwriting online players
[+] test if dummy can be auto-kicked by a player for duplicate login
[+] test if player cache works in offline mode - works but players somehow get different uuids from what they had with online mode
[+] remove dropping inventory and xp on disconnect
[+] reimplement saving bots (maybe with manually refreshed skins if there`s no native way to do that)
[+] add a method to rejoin bots without resetting position, gamemode, etc.
[+] remove command for removing bots by name
[+] delete messages for spawning/removing bots
[+] fix bots teleporting to their disconnect coords if being removed after kill
[+] fix bots rejoining while server is running delayed init tasks
[+] fix bot disconnect not removing them from bot list
[+] move event listeners into separate class
[+] fix a bug that newly added players that are not actual accounts will be invincible and don`t load all skin layers
[+] fix a bug with bot names using registered account names
[+] add a system for storing active server players to prevent bots using their names
[+] 1.18.2 update
[+] bump the version number
2022-04-03 01:41:51 +03:00
MrTransistor 38a359c950 Moving to native PlayerEntity handling (no more packet manipulation). Added fix for PaperMC's post-chunk-load player join handling. Added WIP bot saving. Added automatic resource processing to gradle build script.
Switched to reflection-based command dispatcher. Removed unnecessary stuff
2022-03-03 03:57:10 +03:00
MrTransistor 79e1ab20f3 NMS 1.18.1 update. Moved to Gradle project. Added remapping script for Mojang mappings. 2022-02-03 11:38:47 +03:00
BuildTools 187efe22bd Updated NMS to 1.17.1 2021-07-29 16:11:12 +03:00
BuildTools 6bf6cbab8f API version update in plugin.yml 2021-06-29 19:26:27 +03:00
BuildTools 7da67a8d19 Removed unnecessary stuff 2021-06-29 19:24:43 +03:00
BuildTools 93a6a3437c Version update 2021-06-29 19:22:07 +03:00
BuildTools cb394b6b46 1.17 update 2021-06-29 19:21:44 +03:00
BuildTools 9f7947154d Merge remote-tracking branch 'main/master'
# Conflicts:
#	src/plugin.yml
2021-06-29 19:21:32 +03:00
BuildTools 37efb669e9 1.17 update 2021-06-29 19:21:05 +03:00
Mr Transistor 2d0d338600 Update plugin.yml 2021-06-27 20:55:05 +03:00
25 changed files with 1290 additions and 818 deletions

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
/out
/.idea
/.gradle
/build
/AFK.iml
+57
View File
@@ -0,0 +1,57 @@
plugins {
id 'java-library'
}
apply from: rootProject.file('specialSource.gradle')
version = '3.0.0-MC1.19.2'
group = 'plugin.mrtransistor.AFK'
ext {
author = 'MrTransistor'
spigotVersion = '1.19.2-R0.1-SNAPSHOT'
remapServerCode = true
mainClass = group + '.' + rootProject.getName()
apiVersion = spigotVersion.split('\\.')[0] + '.' + spigotVersion.split('\\.')[1]
}
repositories {
mavenCentral()
mavenLocal()
maven { url = 'https://libraries.minecraft.net/' }
}
dependencies {
afterEvaluate {
if (remapServerCode) {
implementation "org.spigotmc:spigot:${spigotVersion}:remapped-mojang"
} else {
implementation "org.spigotmc:spigot:${spigotVersion}"
}
}
}
afterEvaluate {
if (remapServerCode) {
jar {
doLast {
def outputFiles = outputs.files.files
for (inputFile in outputFiles) {
File intermediateFile = new File(temporaryDir, inputFile.name.replace('.jar', '-obf.jar'))
File outputFile = inputFile
remapMojangToSpigot(inputFile, intermediateFile, outputFile, spigotVersion)
}
}
}
}
}
processResources {
def props = [name : rootProject.name, version: version, author: author,
mainClassPath: mainClass, apiVersion: apiVersion]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
}
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
Vendored
+89
View File
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+2
View File
@@ -0,0 +1,2 @@
rootProject.name = 'AFK'
+71
View File
@@ -0,0 +1,71 @@
buildscript {
repositories {
mavenCentral()
}
configurations.create('specialSource')
dependencies {
specialSource 'net.md-5:SpecialSource:1.11.0:shaded'
}
}
// Spigot server code remapping: https://www.spigotmc.org/threads/spigot-bungeecord-1-17-1-17-1.510208/
// These dependencies and mappings are installed into the local Maven repository when BuildTools builds the Spigot dependencies.
def m2Repo = new File(repositories.mavenLocal().url.path)
def remappedMojang = new File(m2Repo, 'org/spigotmc/spigot/{spigotVersion}/spigot-{spigotVersion}-remapped-mojang.jar').path
def remappedObf = new File(m2Repo, 'org/spigotmc/spigot/{spigotVersion}/spigot-{spigotVersion}-remapped-obf.jar').path
def mojangMappings = new File(m2Repo, 'org/spigotmc/minecraft-server/{spigotVersion}/minecraft-server-{spigotVersion}-maps-mojang.txt').path
def spigotMappings = new File(m2Repo, 'org/spigotmc/minecraft-server/{spigotVersion}/minecraft-server-{spigotVersion}-maps-spigot.csrg').path
def specialSource = buildscript.configurations.specialSource.resolvedConfiguration.getFirstLevelModuleDependencies {
it.group == 'net.md-5' && it.name == 'SpecialSource'
}.first().moduleArtifacts.first().file.path
// Converts from Mojang's mappings to Minecraft's obfuscated mappings.
ext.remapMojangToObfuscated = { inputFile, outputFile, spigotVersion ->
println '> remapMojangToObfuscated'
println ' Input: ' + inputFile.path
println ' Output: ' + outputFile.path
println ' Spigot version: ' + spigotVersion
def classpathSeparator = System.properties['path.separator']
exec {
commandLine 'java',
'-cp', "${specialSource}${classpathSeparator}${remappedMojang}".replace('{spigotVersion}', spigotVersion),
'net.md_5.specialsource.SpecialSource',
'--live',
'-i', inputFile.path,
'-o', outputFile.path,
'-m', mojangMappings.replace('{spigotVersion}', spigotVersion),
'--reverse'
}
}
// Converts from Minecraft's obfuscated mappings to Spigot's mappings.
ext.remapObfuscatedToSpigot = { inputFile, outputFile, spigotVersion ->
println '> remapObfuscatedToSpigot'
println ' Input: ' + inputFile.path
println ' Output: ' + outputFile.path
println ' Spigot version: ' + spigotVersion
def classpathSeparator = System.properties['path.separator']
exec {
commandLine 'java',
'-cp', "${specialSource}${classpathSeparator}${remappedObf}".replace('{spigotVersion}', spigotVersion),
'net.md_5.specialsource.SpecialSource',
'--live',
'-i', inputFile.path,
'-o', outputFile.path,
'-m', spigotMappings.replace('{spigotVersion}', spigotVersion)
}
}
// Converts from Mojang's mappings to Spigot's mappings.
ext.remapMojangToSpigot = { inputFile, intermediateFile, outputFile, spigotVersion ->
println '> remapMojangToSpigot'
println ' Input: ' + inputFile.path
println ' Intermediate: ' + intermediateFile.path
println ' Output: ' + outputFile.path
println ' Spigot version: ' + spigotVersion
remapMojangToObfuscated(inputFile, intermediateFile, spigotVersion)
remapObfuscatedToSpigot(intermediateFile, outputFile, spigotVersion)
}
-17
View File
@@ -1,17 +0,0 @@
# Nothing here yet
# Except for this cat
# _
# \`*-.
# ) _`-.
# . : `. .
# : _ ' \
# ; *` _. `*-._
# `-.-' `-.
# ; ` `.
# :. . \
# . \ . : .-' .
# ' `+.; ; ' :
# : ' | ; ;-.
# ; ' : :`-: _.`* ;
# .*' / .*' ; .*`- +' `*'
# `*-* `*-* `*-*'
@@ -0,0 +1,128 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import plugin.mrtransistor.AFK.commands.CommandDispatcher;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class AFK extends JavaPlugin {
private File botSaveFile;
private FileConfiguration botSaveYml;
@Override
public void onEnable() {
saveDefaultConfig();
loadBotSaveFile();
CommandDispatcher commandDispatcher = new CommandDispatcher(this);
EventListener eventListener = new EventListener();
Bukkit.getPluginManager().registerEvents(eventListener, this);
getCommand("spawnBot").setExecutor(commandDispatcher);
getCommand("spawnBot").setTabCompleter(commandDispatcher);
getCommand("removeAllBots").setExecutor(commandDispatcher);
getCommand("removeAllBots").setTabCompleter(commandDispatcher);
getCommand("afkutils").setExecutor(commandDispatcher);
getCommand("afkutils").setTabCompleter(commandDispatcher);
Bukkit.getScheduler().runTaskLater(this, this::loadBots, 20);
getLogger().info("AFK plugin started!");
}
@Override
public void onDisable() {
saveBots();
getLogger().info("AFK plugin stopped!");
}
private void loadBots() {
ConfigurationSection section = botSaveYml.getConfigurationSection("botsToReload");
if (section == null) return;
List<String> botsToLoad = new ArrayList<>(botSaveYml.getConfigurationSection("botsToReload").getKeys(false));
if (botsToLoad.isEmpty()) return;
getLogger().info(ChatColor.GREEN + "Loading " + botsToLoad.size()
+ (botsToLoad.size() == 1 ? " bot" : " bots"));
for (String bot : botsToLoad) {
DummyPlayer.spawnBot(bot, null, null, false);
}
}
private void saveBots() {
if (!DummyPlayer.dummies.isEmpty())
getLogger().info(ChatColor.GREEN + "Saving " + DummyPlayer.dummies.size()
+ (DummyPlayer.dummies.size() == 1 ? " bot" : " bots"));
botSaveYml.set("botsToReload", null);
botSaveYml.createSection("botsToReload");
while (!DummyPlayer.dummies.isEmpty()) {
DummyPlayer botToSave = DummyPlayer.dummies.get(0);
botSaveYml.createSection("botsToReload." + botToSave.getScoreboardName());
GameProfile gameProfile = botToSave.getGameProfile();
if (gameProfile.getProperties().containsKey("textures")) {
Property textures = gameProfile.getProperties().get("textures").iterator().next();
botSaveYml.set("botsToReload." + botToSave.getScoreboardName() + ".texture", textures.getValue().toString());
botSaveYml.set("botsToReload." + botToSave.getScoreboardName() + ".signature", textures.getSignature().toString());
}
botToSave.disconnect("Plugin unload");
}
saveBotSaveFile();
}
private void loadBotSaveFile() {
botSaveFile = new File(getDataFolder(), "botSave.yml");
if (!botSaveFile.exists()) {
botSaveFile.getParentFile().mkdirs();
saveResource("botSave.yml", false);
}
botSaveYml = new YamlConfiguration();
try {
botSaveYml.load(botSaveFile);
} catch (IOException | InvalidConfigurationException e) {
e.printStackTrace();
}
}
public void saveBotSaveFile() {
try {
botSaveYml.save(botSaveFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public FileConfiguration getBotSaveYml() {
return botSaveYml;
}
}
@@ -0,0 +1,43 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.PacketFlow;
import java.net.SocketAddress;
public class DummyConnection extends Connection {
public DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new EmbeddedChannel();
this.address = new SocketAddress() {
};
}
@Override
public void setReadOnly() {
}
@Override
public void handleDisconnection() {
}
}
@@ -0,0 +1,438 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import com.google.common.collect.ImmutableSet;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.TickTask;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.monster.Zombie;
import net.minecraft.world.item.*;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.PathNavigationRegion;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.level.pathfinder.PathFinder;
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
import net.minecraft.world.phys.Vec3;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftLivingEntity;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
import org.bukkit.entity.Enderman;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
public class DummyPlayer extends ServerPlayer {
private static double ARROW_DIST_COEFF = 0.16;
private static double ARROW_HEIGHT_COEFF = 0.03;
private static double SNOWBALL_DIST_COEFF = 0.45;
private static double SNOWBALL_HEIGHT_COEFF = 0.1;
private static double TRIDENT_DIST_COEFF = 0.26;
private static double TRIDENT_HEIGHT_COEFF = 0.055;
private LivingEntity target;
//WIP
//private Mob pathfindingMob;
//private PathFinder pathFinder = new PathFinder(new WalkNodeEvaluator(), 300);
public static ArrayList<DummyPlayer> dummies = new ArrayList<>();
public static ArrayList<String> names = new ArrayList<>();
public String locale = "en_us"; // Needed to prevent plugins using aikar`s ACF library from throwing NoSuchFieldException
public boolean isFullyConnected = false;
private boolean isSelfDefending = false;
private boolean isAttackingContinuous = false;
private boolean isForcePoI = false;
private boolean aware = false;
private boolean isTicking = true;
private Location PoI_loc;
private final float[] PoI_yawpitch = new float[2];
private String[] getSkin(Player spawner) {
if (spawner != null) {
GameProfile gameProfile = ((CraftPlayer) spawner).getHandle().getGameProfile();
if (!gameProfile.getProperties().containsKey("textures")) return null;
Property property = gameProfile.getProperties().get("textures").iterator().next();
String texture = property.getValue();
String signature = property.getSignature();
return new String[]{texture, signature};
} else {
ConfigurationSection botsToReload = ((AFK) server.server.getPluginManager().getPlugin("AFK")).getBotSaveYml()
.getConfigurationSection("botsToReload");
if (botsToReload.getKeys(false).contains(getScoreboardName())) {
String texture = botsToReload.getConfigurationSection(getScoreboardName()).getString("texture");
String signature = botsToReload.getConfigurationSection(getScoreboardName()).getString("signature");
if (texture != null && signature != null)
return new String[]{texture, signature};
}
return null;
}
}
public DummyPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) {
super(server, world, profile, null);
dummies.add(this);
names.add(getScoreboardName());
}
public static DummyPlayer spawnBot(String name, Location location, org.bukkit.entity.Player spawner, boolean updateStats) { // some of this crap is redundant but it works
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel world = (location != null && location.getWorld() != null) ? ((CraftWorld) location.getWorld()).getHandle()
: server.getLevel(Level.OVERWORLD);
GameProfile gameProfile = server.getProfileCache().get(name).orElse(new GameProfile(UUID.randomUUID(), name)); // maybe needs something else?
DummyPlayer dummy = new DummyPlayer(server, world, gameProfile);
String[] texSign;// maybe simplify this?
texSign = dummy.getSkin(spawner);
if (texSign != null)
gameProfile.getProperties().put("textures", new Property("textures", texSign[0], texSign[1]));
/* spawning entity */
server.getPlayerList().placeNewPlayer(new DummyConnection(), dummy);
new BukkitRunnable() {
@Override
public void run() { // ensuring that the bot has been connected and started ticking
if (!dummy.isFullyConnected) {
dummy.connection.tick();
return;
} else if (location != null && location.getWorld() != null)
dummy.teleportTo(world, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
//WIP
//dummy.pathfindingMob = new Zombie(dummy.getLevel()); // Initialising pathfinding mob
this.cancel();
}
}.runTaskTimer(server.server.getPluginManager().getPlugin("AFK"), 0, 10);
if (updateStats) {
dummy.setHealth(20.0F);
dummy.unsetRemoved();
dummy.setGameMode(GameType.SURVIVAL);
}
dummy.getEntityData().set(DATA_PLAYER_MODE_CUSTOMISATION, (byte) 0xf7);
return dummy;
}
@Override
public void tick() { // still can`t figure out how to process knockback from players
isFullyConnected = true;
if (!isTicking) return;
super.tick(); // handle gamemode and inventory
doTick(); // update health, air and other levels
if (getServer().getTickCount() % 10 == 0) { // update position in chunks
connection.resetPosition(); // maybe not necessary?
getLevel().getChunkSource().move(this);
setSprinting(getDeltaMovement().x() > 0 || getDeltaMovement().z() > 0); // sets sprinting disabled when bot
} // doesn't move to prevent particles
//WIP
/*if (pathfindingMob != null) {
Vec3 vecPos = new Vec3(getX(), getY(), getZ()); // updating position of pathfinder mob
pathfindingMob.setPos(vecPos);
pathfindingMob.setOnGround(true); // maybe not needed?
}*/
if (getServer().getTickCount() % 20 == 0) // prevents hunger
getBukkitEntity().setFoodLevel(20);
if (isForcePoI) goToPoI();
selfDefence();
if (isAttackingContinuous) attackContinuous();
rangeAttackTarget();
//WIP
/*
if (pathfindingMob == null) return;
Player targetPlayer = Bukkit.getServer().getPlayer("MrTransistor_");
if (targetPlayer == null) return;
Location targetLoc = targetPlayer.getLocation();
BlockPos targetPos = new BlockPos(targetLoc.getBlockX(), targetLoc.getBlockY(), targetLoc.getBlockZ());
Path pathToTarget = pathToTarget(targetPos, 120);
pathToTarget.advance();
if (pathToTarget.isDone()) return;
if (targetPos.distSqr(new Vec3i(getBlockX(), getBlockY(), getBlockZ())) < 16) {
lookAt(EntityAnchorArgument.Anchor.EYES, ((CraftPlayer) targetPlayer).getHandle(), EntityAnchorArgument.Anchor.EYES);
return;
}
Vec3 toTarget = getEyePosition().subtract(pathToTarget.getNextNode().asVec3().add(0.5, 1.6, 0.5)).normalize();
float yaw = (float) Math.atan2(toTarget.x(), -toTarget.z());
float pitch = (float) Math.atan2(toTarget.y(), Math.sqrt(toTarget.x() * toTarget.x() + toTarget.z() * toTarget.z()));
yaw *= 180 / Math.PI;
pitch *= 180 / Math.PI;
setRot(yaw, pitch);
setYHeadRot(yaw);
travel(new Vec3(0, 0, 1));
*/
}
public void attackOnce() {
CraftLivingEntity target = getTargetedLivingEntity();
if (getAttackStrengthScale(0.5f) == 1 && target != null) {
attack(target);
}
swing(InteractionHand.MAIN_HAND);
}
public void shootNearestLivingEntity() {
CraftPlayer bukkitEntity = getBukkitEntity();
List<Entity> nearbyEntities = bukkitEntity.getNearbyEntities(50, 50, 50);
Entity nearest = null;
double nearestDistance = 0;
for (Entity e : nearbyEntities) {
if (!(e instanceof CraftLivingEntity)
|| e instanceof Player || e instanceof Enderman || e.isDead()) continue;
if ((nearest == null || e.getLocation().distanceSquared(getBukkitEntity().getLocation()) < nearestDistance)
&& bukkitEntity.hasLineOfSight(e)) {
nearest = e;
nearestDistance = e.getLocation().distanceSquared(getBukkitEntity().getLocation());
}
}
if (nearest != null) {
target = (LivingEntity) ((CraftEntity) nearest).getHandle();
}
}
//WIP
/*private Path pathToTarget(BlockPos target, int range) {
BlockPos pos = new BlockPos(getBlockX(), getBlockY(), getBlockZ());
PathNavigationRegion region = new PathNavigationRegion(getLevel(), pos.offset(-range, -range, -range),
pos.offset(range, range, range));
return pathFinder.findPath(region, pathfindingMob, ImmutableSet.of(target), range, 1, 1.f);
}*/
public void setTicking(boolean state) {
isTicking = state;
}
public boolean isTicking() {
return isTicking;
}
public void setAttackingContinuous(boolean state) {
isAttackingContinuous = state;
}
public boolean isAttackingContinuous() {
return isAttackingContinuous;
}
public void setSelfDefending(boolean state) {
isSelfDefending = state;
}
public boolean isSelfDefending() {
return isSelfDefending;
}
public void setForcePoI(boolean state) {
isForcePoI = state;
if (isForcePoI) {
PoI_loc = getBukkitEntity().getEyeLocation();
PoI_yawpitch[0] = PoI_loc.getYaw();
PoI_yawpitch[1] = PoI_loc.getPitch();
}
}
public boolean isForcePoI() {
return isForcePoI;
}
private void attackContinuous() {
if (!aware) {
CraftLivingEntity target = getTargetedLivingEntity();
if (getAttackStrengthScale(0.5f) == 1 && target != null) {
attack(target);
swing(InteractionHand.MAIN_HAND);
}
}
}
private void selfDefence() {
LivingEntity damager = getLastHurtByMob();
if (damager == null || !isSelfDefending) {
if (aware) {
Vector toTarget = getBukkitEntity().getEyeLocation().toVector().subtract(PoI_loc.toVector()).normalize();
float yaw = (float) Math.atan2(toTarget.getX(), -toTarget.getZ());
float pitch = (float) Math.atan2(toTarget.getY(), Math.sqrt(toTarget.getX() * toTarget.getX() + toTarget.getZ() * toTarget.getZ()));
yaw *= 180 / Math.PI;
pitch *= 180 / Math.PI;
setRot(yaw, pitch);
setYHeadRot(yaw);
travel(new Vec3(0, 0, 1));
if (PoI_loc.distance(getBukkitEntity().getEyeLocation()) < 0.1) {
getBukkitEntity().setVelocity(new Vector(0, 0, 0));
setRot(PoI_yawpitch[0], PoI_yawpitch[1]);
setYHeadRot(PoI_yawpitch[0]);
aware = false;
}
}
return;
}
if (!aware) {
if (!isForcePoI) {
PoI_loc = getBukkitEntity().getEyeLocation();
PoI_yawpitch[0] = PoI_loc.getYaw();
PoI_yawpitch[1] = PoI_loc.getPitch();
}
aware = true;
}
Vector toTarget = getBukkitEntity().getEyeLocation().toVector().subtract(((CraftLivingEntity) damager.getBukkitEntity()).getEyeLocation().toVector()).normalize();
float yaw = (float) Math.atan2(toTarget.getX(), -toTarget.getZ());
float pitch = (float) Math.atan2(toTarget.getY(), Math.sqrt(toTarget.getX() * toTarget.getX() + toTarget.getZ() * toTarget.getZ()));
yaw *= 180 / Math.PI;
pitch *= 180 / Math.PI;
setRot(yaw, pitch);
setYHeadRot(yaw);
travel(new Vec3(0, 0, 1));
CraftLivingEntity target = getTargetedLivingEntity();
if (getAttackStrengthScale(0.5f) == 1 && target != null && target.getUniqueId().equals(damager.getUUID())) {
attack(target);
swing(InteractionHand.MAIN_HAND);
}
}
private void goToPoI() {
if (PoI_loc.distance(getBukkitEntity().getEyeLocation()) > 0.1) {
aware = true;
}
}
private void rangeAttackTarget() {
if (target == null) return;
lookAt(EntityAnchorArgument.Anchor.EYES, target, EntityAnchorArgument.Anchor.EYES);
double x = getX() - target.getX();
double y = getY() - target.getY();
double z = getZ() - target.getZ();
double r = Math.sqrt(x * x + z * z); // distance in X-Z plane
ItemStack itemStackInHand = getItemInHand(InteractionHand.MAIN_HAND);
Item itemInHand = itemStackInHand.getItem();
float pitch = getXRot(); // correcting pitch for distance to target, totally simplified, but it has enough accuracy
if (itemInHand instanceof BowItem || itemInHand instanceof CrossbowItem) {
setXRot((float) (pitch - r * ARROW_DIST_COEFF + y * ARROW_HEIGHT_COEFF));
if (itemInHand instanceof BowItem) {
if (!isUsingItem()) {
itemInHand.use(getLevel(), this, InteractionHand.MAIN_HAND);
}
if (BowItem.getPowerForTime(getTicksUsingItem()) == 1.0) {
itemStackInHand.releaseUsing(getLevel(), this, 0);
stopUsingItem();
target = null;
}
} else {
if (!isUsingItem() || CrossbowItem.isCharged(itemStackInHand)) {
if (CrossbowItem.isCharged(itemStackInHand))
target = null;
itemInHand.use(getLevel(), this, InteractionHand.MAIN_HAND);
}
if (getTicksUsingItem() >= CrossbowItem.getChargeDuration(itemStackInHand)) {
itemInHand.releaseUsing(itemStackInHand, getLevel(), this, 0);
stopUsingItem();
}
}
} else if (itemInHand instanceof SnowballItem || itemInHand instanceof EggItem) {
setXRot((float) (pitch - r * SNOWBALL_DIST_COEFF + y * SNOWBALL_HEIGHT_COEFF));
itemInHand.use(getLevel(), this, InteractionHand.MAIN_HAND);
target = null;
} else if (itemInHand instanceof TridentItem) {
setXRot((float) (pitch - r * TRIDENT_DIST_COEFF + y * TRIDENT_HEIGHT_COEFF));
if (!isUsingItem())
itemInHand.use(getLevel(), this, InteractionHand.MAIN_HAND);
if ((float) getTicksUsingItem() >= 10 && !isAutoSpinAttack()) {
itemInHand.releaseUsing(itemStackInHand, getLevel(), this, 0);
target = null;
}
} else {
setXRot(pitch);
target = null;
}
}
public CraftLivingEntity getTargetedLivingEntity() {
Player bukkitPlayer = getBukkitEntity();
World world = bukkitPlayer.getWorld();
Location eyePos = bukkitPlayer.getEyeLocation();
Vector eyeDirection = bukkitPlayer.getEyeLocation().getDirection();
Predicate<Entity> filter = entity -> !entity.equals(bukkitPlayer) && entity instanceof CraftLivingEntity;
double reach = getBukkitEntity().getGameMode() == GameMode.CREATIVE ? 4.5 : 3;
RayTraceResult trace = world.rayTrace(eyePos, eyeDirection, reach, FluidCollisionMode.NEVER, true, 0, filter);
if (trace == null) return null;
Entity hitEntity = trace.getHitEntity();
if (hitEntity instanceof CraftLivingEntity)
return (CraftLivingEntity) hitEntity;
else return null;
}
public void attack(Entity entity) {
attack(((CraftEntity) entity).getHandle());
}
public void disconnect(String reason) {
connection.disconnect(reason);
dummies.remove(this);
names.remove(getScoreboardName());
}
@Override
public void die(DamageSource damagesource) {
super.die(damagesource);
setExperienceLevels(0); // idk why we need this but without this bot does not clear xp on death
setExperiencePoints(0);
removeAllEffects();
getBukkitEntity().setTotalExperience(0); // needed to simplify xp reward calculation
getServer().execute(new TickTask(getServer().getTickCount(), () -> disconnect("Died")));
}
}
@@ -0,0 +1,43 @@
package plugin.mrtransistor.AFK;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.ArrayList;
import java.util.List;
public class EventListener implements Listener {
@EventHandler // handles removing of player exp cap on death for bots
public void onPlayerDeath(PlayerDeathEvent e) {
if (((CraftEntity) e.getEntity()).getHandle() instanceof DummyPlayer)
e.setDroppedExp(e.getEntity().getTotalExperience());
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
if (!(((CraftEntity) e.getPlayer()).getHandle() instanceof DummyPlayer)) {
FileConfiguration botSaveYml = Bukkit.getPluginManager().getPlugin("AFK").getConfig();
List<String> blacklistedNames = new ArrayList<>();
if (botSaveYml.contains("blacklistedNames"))
blacklistedNames = botSaveYml.getStringList("blacklistedNames");
if (!blacklistedNames.contains(e.getPlayer().getName()))
blacklistedNames.add(e.getPlayer().getName());
botSaveYml.set("blacklistedNames", blacklistedNames);
Bukkit.getPluginManager().getPlugin("AFK").saveConfig();
}
}
@EventHandler // handles removing bot from list if lost connection not by DummyPlayer#die() or #disconnect()
public void onPlayerDisconnect(PlayerQuitEvent e) {
if (((CraftEntity) e.getPlayer()).getHandle() instanceof DummyPlayer) {
DummyPlayer.dummies.remove((DummyPlayer) ((CraftEntity) e.getPlayer()).getHandle());
DummyPlayer.names.remove(e.getPlayer().getName());
}
}
}
@@ -0,0 +1,176 @@
package plugin.mrtransistor.AFK.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import plugin.mrtransistor.AFK.AFK;
import plugin.mrtransistor.AFK.DummyPlayer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class CommandDispatcher implements TabExecutor {
static AFK _plg;
PluginDescriptionFile pluginDotYml;
HashMap<String, String> aliasMap = new HashMap<>();
public CommandDispatcher(AFK plg) {
pluginDotYml = plg.getDescription();
_plg = plg;
for (String cmd : pluginDotYml.getCommands().keySet()) {
if (!pluginDotYml.getCommands().get(cmd).containsKey("aliases")) continue;
for (String alias : (List<String>) pluginDotYml.getCommands().get(cmd).get("aliases"))
aliasMap.put(alias, cmd);
}
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (aliasMap.containsKey(label)) label = aliasMap.get(label);
if (pluginDotYml.getCommands().get(label) == null) return false;
if (args.length == 0) {
if (pluginDotYml.getCommands().get(label).get("handler") == null) return false;
try {
getClass().getMethod(
pluginDotYml.getCommands().get(label).get("handler").toString(),
CommandSender.class, String[].class)
.invoke(null, sender, args);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
Map<String, Object> _args = (Map<String, Object>) pluginDotYml.getCommands().get(label).get("args");
Pattern numeric = Pattern.compile("~?-?\\d+(?:\\.\\d+)?|~");
for (String arg : args) {
Map<String, Object> _args_bc = _args;
if (numeric.matcher(arg).matches())
_args = (Map<String, Object>) _args.get("__numeric__");
else
_args = (Map<String, Object>) _args.get(arg);
if (_args == null) {
if (_args_bc.containsKey("__text__")) _args = (Map<String, Object>) _args_bc.get("__text__");
else return false;
}
}
try {
getClass().getMethod(_args.get("handler").toString(), CommandSender.class, String[].class)
.invoke(null, sender, args);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
if (aliasMap.containsKey(label)) label = aliasMap.get(label);
if (pluginDotYml.getCommands().get(label) == null) return null;
if (args.length == 1) {
if (pluginDotYml.getCommands().get(label).get("tabCompletion") == null) return null;
return (List<String>) pluginDotYml.getCommands().get(label).get("tabCompletion");
} else {
Map<String, Object> _args = (Map<String, Object>) pluginDotYml.getCommands().get(label).get("args");
Pattern numeric = Pattern.compile("~?-?\\d+(?:\\.\\d+)?|~");
for (int i = 0; i < args.length - 1; i++) {
Map<String, Object> _args_old = _args;
if (numeric.matcher(args[i]).matches()) {
_args = (Map<String, Object>) _args.get("__numeric__");
} else {
_args = (Map<String, Object>) _args.get(args[i]);
}
if (_args == null) {
if (_args_old.containsKey("__text__")) {
_args = (Map<String, Object>) _args_old.get("__text__");
} else return new ArrayList<>();
}
}
return (List<String>) _args.get("tabCompletion");
}
}
public static void attackOnce(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1]))
dummy.attackOnce();
}
}
public static void shootNearest(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1]))
dummy.shootNearestLivingEntity();
}
}
public static void attackContinuous(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1])) {
dummy.setAttackingContinuous(!dummy.isAttackingContinuous());
sender.sendMessage(ChatColor.GREEN + "Continuous attacking is toggled " + ChatColor.DARK_GREEN
+ (dummy.isAttackingContinuous() ? "ON" : "OFF"));
}
}
}
public static void selfDefence(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1])) {
dummy.setSelfDefending(!dummy.isSelfDefending());
sender.sendMessage(ChatColor.GREEN + "Self defence is toggled " + ChatColor.DARK_GREEN
+ (dummy.isSelfDefending() ? "ON" : "OFF"));
}
}
}
public static void setForcePoI(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1])) {
dummy.setForcePoI(!dummy.isForcePoI());
sender.sendMessage(ChatColor.GREEN + "Force point of interest is toggled " + ChatColor.DARK_GREEN
+ (dummy.isForcePoI() ? "ON" : "OFF"));
}
}
}
public static void ticking(CommandSender sender, String[] args) {
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().getString().equals(args[1])) {
dummy.setTicking(!dummy.isTicking());
sender.sendMessage(ChatColor.GREEN + "Entity ticking is " + ChatColor.DARK_GREEN
+ (dummy.isTicking() ? "enabled" : "disabled"));
}
}
}
public static void spawnBot(CommandSender sender, String[] args) {
if (sender instanceof Player) {
if (args[0].length() > 16) {
sender.sendMessage(ChatColor.RED + "Player names can`t be longer than 16 symbols");
return;
}
if (!_plg.getConfig().getStringList("blacklistedNames").contains(args[0]) && !DummyPlayer.names.contains(args[0]))
DummyPlayer.spawnBot(args[0], ((Player) sender).getLocation(), (Player) sender, true);
else
sender.sendMessage(ChatColor.RED + "There`s already a player with that name");
return;
}
sender.sendMessage("Sorry, this command is only for players");
}
public static void removeAllBots(CommandSender sender, String[] args) {
if (DummyPlayer.dummies.isEmpty()) sender.sendMessage(ChatColor.RED + "There are no bots");
while (!DummyPlayer.dummies.isEmpty())
DummyPlayer.dummies.get(0).disconnect("Removed using command");
}
}
+1
View File
@@ -0,0 +1 @@
botsToReload:
+1
View File
@@ -0,0 +1 @@
blacklistedNames: []
+49
View File
@@ -0,0 +1,49 @@
name: ${name}
version: ${version}
author: ${author}
main: ${mainClassPath}
commands:
afkutils:
usage: "Usage: /afkutils controlBot <name>"
description: "Allows players to control bots"
tabCompletion: [ control ]
args:
control:
tabCompletion: [ ]
__text__:
tabCompletion: [ attackOnce, shootNearest, toggle ]
attackOnce:
tabCompletion: [ ]
handler: attackOnce
shootNearest:
tabCompletion: [ ]
handler: shootNearest
toggle:
tabCompletion: [ attackContinuous, selfDefence, setForcePoI, ticking ]
attackContinuous:
tabCompletion: [ ]
handler: attackContinuous
selfDefence:
tabCompletion: [ ]
handler: selfDefence
setForcePoI:
tabCompletion: [ ]
handler: setForcePoI
ticking:
tabCompletion: [ ]
handler: ticking
spawnbot:
usage: "Usage: /spawnbot <name>"
description: "Spawns an NPC, that can be controlled from within the plugin, which acts as a player, allowing loading mobs and random ticking"
tabCompletion: [ ]
args:
__text__:
tabCompletion: [ ]
handler: spawnBot
removeallbots:
usage: "Usage: /removeallbots"
description: "Removes all bots"
tabCompletion: [ ]
handler: removeAllBots
api-version: ${apiVersion}
-18
View File
@@ -1,18 +0,0 @@
name: AFK
version: 2.3
author: MrTransistor
main: plugin.mrtransistor.AFK.AFK
commands:
AFKUtils:
usage: "Usage: /afkutils controlbot <name>"
description: "Allows players to control bots"
spawnBot:
usage: "Usage: /spawnbot <name>"
description: "Spawns an EntityPlayer, that can be controlled from within the plugin and acts as a player, allowing loading mobs and random ticking"
removeBot:
usage: "Usage: /removebot <name>"
description: "Removes all bots with specified name"
removeAllBots:
usage: "Usage: /removeallbots"
description: "Removes all bots"
api-version: 1.16
-116
View File
@@ -1,116 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import net.minecraft.server.v1_16_R3.*;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import plugin.mrtransistor.AFK.commands.AFKUtils;
import plugin.mrtransistor.AFK.commands.RemoveAllBots;
import plugin.mrtransistor.AFK.commands.RemoveBot;
import plugin.mrtransistor.AFK.commands.SpawnBot;
public class AFK extends JavaPlugin implements Listener {
@Override
public void onEnable() {
saveDefaultConfig();
Bukkit.getPluginManager().registerEvents(this, this);
getCommand("spawnBot").setExecutor(new SpawnBot());
getCommand("spawnBot").setTabCompleter(new SpawnBot());
getCommand("removeBot").setExecutor(new RemoveBot());
getCommand("removeBot").setTabCompleter(new RemoveBot());
getCommand("removeAllBots").setExecutor(new RemoveAllBots());
getCommand("removeAllBots").setTabCompleter(new RemoveAllBots());
getCommand("afkutils").setExecutor(new AFKUtils());
getCommand("afkutils").setTabCompleter(new AFKUtils());
// Attack test
/*new BukkitRunnable() {
@Override
public void run() {
for (DummyPlayer dummy : DummyPlayer.dummies) {
LivingEntity target = dummy.getTargetedLivingEntity();
if (target != null) {
dummy.swingHand(EnumHand.MAIN_HAND, true);
dummy.attack(target);
}
}
}
}.runTaskTimer(this, 0, 15);*/
getLogger().info("AFK plugin started!");
}
@Override
public void onDisable() {
int botsRemoved = 0;
int numBots = DummyPlayer.dummies.size();
for (int i = 0; i < numBots; i++) {
DummyPlayer dummy = DummyPlayer.dummies.get(0);
getLogger().info("Bot '" + ChatColor.DARK_GREEN + dummy.getName() + ChatColor.RESET + "' with UUID:["
+ ChatColor.GOLD + dummy.getUniqueIDString() + ChatColor.RESET + "] was removed");
dummy.remove("Plugin stopped");
botsRemoved++;
}
if (botsRemoved == 0) getLogger().info(ChatColor.GREEN + "No bots were removed");
else if (botsRemoved == 1)
getLogger().info(ChatColor.GREEN + "1 bot was removed");
else getLogger().info(ChatColor.GREEN + Integer.toString(botsRemoved) + " bots were removed");
getLogger().info("AFK plugin stopped!");
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
PlayerConnection plc = ((CraftPlayer) e.getPlayer()).getHandle().playerConnection;
for (DummyPlayer dummy : DummyPlayer.dummies) {
plc.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER,
dummy));
plc.sendPacket(new PacketPlayOutNamedEntitySpawn(dummy));
plc.sendPacket(new PacketPlayOutEntityMetadata(dummy.getId(),
dummy.getDataWatcher(), true));
}
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent e){
if(((CraftEntity)e.getEntity()).getHandle() instanceof DummyPlayer){
e.setDroppedExp(e.getEntity().getTotalExperience());
}
}
@EventHandler
public void onFoodLevelChange(FoodLevelChangeEvent e){
if(((CraftEntity)e.getEntity()).getHandle() instanceof DummyPlayer){
e.setCancelled(true);
e.getEntity().setFoodLevel(20);
}
}
}
@@ -1,124 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import io.netty.channel.*;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.server.v1_16_R3.EnumProtocolDirection;
import net.minecraft.server.v1_16_R3.NetworkManager;
import net.minecraft.server.v1_16_R3.Packet;
import java.net.SocketAddress;
public class DummyNetworkManager extends NetworkManager {
public DummyNetworkManager() {
super(EnumProtocolDirection.SERVERBOUND);
this.channel = new EmptyNettyChannel(null);
this.socketAddress = new SocketAddress() {
};
}
@Override
public boolean isConnected() {
return super.isConnected();
}
@Override
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
/* do nothing */
}
@Override
public void handleDisconnection() {
/* do nothing */
}
public class EmptyNettyChannel extends AbstractChannel {
private final ChannelConfig config = new DefaultChannelConfig(this);
public EmptyNettyChannel(Channel parent) {
super(parent);
}
@Override
public ChannelConfig config() {
config.setAutoRead(true);
return config;
}
@Override
protected void doBeginRead() {
}
@Override
protected void doBind(SocketAddress address) {
}
@Override
protected void doClose() {
}
@Override
protected void doDisconnect() {
}
@Override
protected void doWrite(ChannelOutboundBuffer buffer) {
}
@Override
public boolean isActive() {
return false;
}
@Override
protected boolean isCompatible(EventLoop loop) {
return true;
}
@Override
public boolean isOpen() {
return false;
}
@Override
protected SocketAddress localAddress0() {
return null;
}
@Override
public ChannelMetadata metadata() {
return new ChannelMetadata(true);
}
@Override
protected AbstractUnsafe newUnsafe() {
return null;
}
@Override
protected SocketAddress remoteAddress0() {
return null;
}
}
}
@@ -1,253 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.minecraft.server.v1_16_R3.*;
import org.bukkit.*;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.UUID;
import java.util.function.Predicate;
public class DummyPlayer extends EntityPlayer {
public static ArrayList<DummyPlayer> dummies = new ArrayList<>();
public static ArrayList<String> dummyNames = new ArrayList<>();
private boolean isSelfDefending = false;
private boolean isAttackingContinuous = false;
private boolean isForcePoI = false;
private boolean aware = false;
private boolean isTicking = true;
private Location PoI_loc;
private float[] PoI_yawpitch = new float[2];
private static String[] getSkin(EntityPlayer entityPlayer) {
GameProfile gameProfile = entityPlayer.getProfile();
Property property = gameProfile.getProperties().get("textures").iterator().next();
String texture = property.getValue();
String signature = property.getSignature();
return new String[]{texture, signature};
}
public DummyPlayer(MinecraftServer server, WorldServer world, GameProfile profile, PlayerInteractManager interactManager) {
super(server, world, profile, interactManager);
dummyNames.add(getName());
dummies.add(this);
}
public static DummyPlayer spawnBot(String name, Location location, Player spawner) {
MinecraftServer server = ((CraftServer) (Bukkit.getServer())).getServer();
WorldServer worldServer = ((CraftWorld) location.getWorld()).getHandle();
NetworkManager networkManager = new DummyNetworkManager();
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), name);
DummyPlayer dummy = new DummyPlayer(server, worldServer, gameProfile, new PlayerInteractManager(worldServer));
String[] texSign = getSkin(((CraftPlayer) spawner).getHandle());
gameProfile.getProperties().put("textures", new Property("textures", texSign[0], texSign[1]));
dummy.getDataWatcher().set(new DataWatcherObject<>(16, DataWatcherRegistry.a), (byte) 0x7f); // show all model layers
dummy.playerConnection = new PlayerConnection(server, networkManager, dummy);
worldServer.addPlayerJoin(dummy);
dummy.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
dummy.setHeadRotation(location.getYaw());
dummy.setYawPitch(location.getYaw(), location.getPitch());
worldServer.getChunkProvider().movePlayer(dummy);
server.getPlayerList().sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, dummy));
server.getPlayerList().sendAll(new PacketPlayOutNamedEntitySpawn(dummy));
server.getPlayerList().sendAll(new PacketPlayOutEntityMetadata(dummy.getId(), dummy.getDataWatcher(), true));
return dummy;
}
@Override
public void tick() {
if(!isTicking) return;
if(isForcePoI) goToPoI();
selfDefence();
if(isAttackingContinuous) attackContinuous();
//getBukkitEntity().getServer().getLogger().info("");
if (server.ai() % 10 == 0) {
playerConnection.syncPosition();
getWorldServer().getChunkProvider().movePlayer(this);
}
super.tick();
playerTick();
}
public void attackOnce() {
LivingEntity target = getTargetedLivingEntity();
if (getAttackCooldown(0.5f) == 1 && target != null) {
attack(target);
}
swingHand(EnumHand.MAIN_HAND);
}
public void setTicking(boolean state){
isTicking = state;
}
public boolean isTicking(){
return isTicking;
}
public void setAttackingContinuous(boolean state){
isAttackingContinuous = state;
}
public boolean isAttackingContinuous(){
return isAttackingContinuous;
}
public void setSelfDefending(boolean state){
isSelfDefending = state;
}
public boolean isSelfDefending(){
return isSelfDefending;
}
public void setForcePoI(boolean state){
isForcePoI = state;
if(isForcePoI) {
PoI_loc = getBukkitEntity().getEyeLocation();
PoI_yawpitch[0] = PoI_loc.getYaw();
PoI_yawpitch[1] = PoI_loc.getPitch();
}
}
public boolean isForcePoI(){
return isForcePoI;
}
private void attackContinuous(){
if(!aware) {
LivingEntity target = getTargetedLivingEntity();
if (getAttackCooldown(0.5f) == 1 && target != null) {
attack(target);
swingHand(EnumHand.MAIN_HAND);
}
}
}
private void selfDefence() {
EntityLiving damager = getLastDamager();
if (damager == null || !isSelfDefending){
if(aware){
Vector toTarget = getBukkitEntity().getEyeLocation().toVector().subtract(PoI_loc.toVector()).normalize();
float yaw = (float) Math.atan2(toTarget.getX(), -toTarget.getZ());
float pitch = (float) Math.atan2(toTarget.getY(), Math.sqrt(toTarget.getX() * toTarget.getX() + toTarget.getZ() * toTarget.getZ()));
yaw *= 180 / Math.PI;
pitch *= 180 / Math.PI;
setYawPitch(yaw, pitch);
setHeadRotation(yaw);
g(new Vec3D(0, 0, 1));
if(PoI_loc.distance(getBukkitEntity().getEyeLocation()) < 0.1){
getBukkitEntity().setVelocity(new Vector(0, 0, 0));
setYawPitch(PoI_yawpitch[0], PoI_yawpitch[1]);
setHeadRotation(PoI_yawpitch[0]);
aware = false;
}
}
return;
}
if(!aware){
if(!isForcePoI) {
PoI_loc = getBukkitEntity().getEyeLocation();
PoI_yawpitch[0] = PoI_loc.getYaw();
PoI_yawpitch[1] = PoI_loc.getPitch();
}
aware = true;
}
Vector toTarget = getBukkitEntity().getEyeLocation().toVector().subtract(((LivingEntity) damager.getBukkitEntity()).getEyeLocation().toVector()).normalize();
float yaw = (float) Math.atan2(toTarget.getX(), -toTarget.getZ());
float pitch = (float) Math.atan2(toTarget.getY(), Math.sqrt(toTarget.getX() * toTarget.getX() + toTarget.getZ() * toTarget.getZ()));
yaw *= 180 / Math.PI;
pitch *= 180 / Math.PI;
setYawPitch(yaw, pitch);
setHeadRotation(yaw);
g(new Vec3D(0, 0, 1));
LivingEntity target = getTargetedLivingEntity();
if (getAttackCooldown(0.5f) == 1 && target != null && target.getUniqueId().equals(damager.getUniqueID())) {
attack(target);
swingHand(EnumHand.MAIN_HAND);
}
}
private void goToPoI(){
if(PoI_loc.distance(getBukkitEntity().getEyeLocation()) > 0.1){
aware = true;
}
}
public LivingEntity getTargetedLivingEntity() {
Player bukkitPlayer = getBukkitEntity();
World world = bukkitPlayer.getWorld();
Location eyePos = bukkitPlayer.getEyeLocation();
Vector eyeDirection = bukkitPlayer.getEyeLocation().getDirection();
Predicate<org.bukkit.entity.Entity> filter = entity -> !entity.equals(bukkitPlayer) && entity instanceof LivingEntity;
double reach = playerConnection.getPlayer().getGameMode() == GameMode.CREATIVE ? 4.5 : 3;
RayTraceResult trace = world.rayTrace(eyePos, eyeDirection, reach, FluidCollisionMode.NEVER, true, 0, filter);
if (trace == null) return null;
Entity hitEntity = trace.getHitEntity();
if (hitEntity instanceof LivingEntity)
return (LivingEntity) hitEntity;
else return null;
}
public void attack(Entity entity) {
attack(((CraftEntity) entity).getHandle());
}
public void remove(String reason) {
dropInventory();
playerConnection.disconnect(reason);
dummyNames.remove(getName());
dummies.remove(this);
}
@Override
public void die(DamageSource damagesource) {
super.die(damagesource);
server.a(new TickTask(server.ai(), () -> remove("Died")));
}
}
@@ -1,108 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import plugin.mrtransistor.AFK.DummyPlayer;
import java.util.ArrayList;
import java.util.List;
public class AFKUtils implements TabExecutor {
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if(args.length >= 1 && args[0].equalsIgnoreCase("controlBot")){
if(args.length >= 2) {
boolean botFound = false;
for (DummyPlayer dummy : DummyPlayer.dummies) {
if (dummy.getName().equals(args[1])) {
botFound = true;
if(args.length >= 3) {
if (args[2].equalsIgnoreCase("attackOnce")) {
dummy.attackOnce();
}
else if(args[2].equalsIgnoreCase("toggle")) {
if(args.length >= 4){
if(args[3].equalsIgnoreCase("selfDefence")){
dummy.setSelfDefending(!dummy.isSelfDefending());
sender.sendMessage(ChatColor.GREEN + "Self defence is toggled " + ChatColor.DARK_GREEN + (dummy.isSelfDefending() ? "ON" : "OFF"));
}
else if(args[3].equalsIgnoreCase("attackContinuous")){
dummy.setAttackingContinuous(!dummy.isAttackingContinuous());
sender.sendMessage(ChatColor.GREEN + "Continuous attacking is toggled " + ChatColor.DARK_GREEN + (dummy.isAttackingContinuous() ? "ON" : "OFF"));
}
else if(args[3].equalsIgnoreCase("setForcePoI")){
dummy.setForcePoI(!dummy.isForcePoI());
sender.sendMessage(ChatColor.GREEN + "Force point of interest is toggled " + ChatColor.DARK_GREEN + (dummy.isForcePoI() ? "ON" : "OFF"));
}
else if(args[3].equalsIgnoreCase("ticking")){
dummy.setTicking(!dummy.isTicking());
sender.sendMessage(ChatColor.GREEN + "Entity ticking is " + ChatColor.DARK_GREEN + (dummy.isTicking() ? "enabled" : "disabled"));
}
return true;
}
else return false;
}
return true;
}
else return false;
}
}
sender.sendMessage(ChatColor.RED + "There is no bot with name '" + ChatColor.DARK_GREEN + args[1] + ChatColor.RED + "'");
return true;
}
else return false;
}
return false;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
ArrayList<String> tabCompletion = new ArrayList<>();
switch(args.length){
case 1:
tabCompletion.add("controlBot");
break;
case 2:
if(args[0].equalsIgnoreCase("controlBot")){
for(DummyPlayer dummy : DummyPlayer.dummies) tabCompletion.add(dummy.getName());
}
break;
case 3:
if(args[0].equalsIgnoreCase("controlBot")){
tabCompletion.add("attackOnce");
tabCompletion.add("toggle");
}
break;
case 4:
if(args[0].equalsIgnoreCase("controlBot")){
if(args[2].equalsIgnoreCase("toggle")){
tabCompletion.add("selfDefence");
tabCompletion.add("attackContinuous");
tabCompletion.add("setForcePoI");
tabCompletion.add("ticking");
}
}
}
return tabCompletion;
}
}
@@ -1,53 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import plugin.mrtransistor.AFK.DummyPlayer;
import java.util.ArrayList;
import java.util.List;
public class RemoveAllBots implements TabExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
int botsRemoved = 0;
int numBots = DummyPlayer.dummies.size();
for (int i = 0; i < numBots; i++) {
DummyPlayer dummy = DummyPlayer.dummies.get(0);
sender.sendMessage("Bot '" + ChatColor.DARK_GREEN + dummy.getName() + ChatColor.RESET + "' with UUID:["
+ ChatColor.GOLD + dummy.getUniqueIDString() + ChatColor.RESET + "] was removed");
dummy.remove("Removed using command");
botsRemoved++;
}
if (botsRemoved == 0) sender.sendMessage(ChatColor.RED + "There are no bots");
else if (botsRemoved == 1)
sender.sendMessage(ChatColor.GREEN + "1 bot was removed");
else sender.sendMessage(ChatColor.GREEN + Integer.toString(botsRemoved) + " bots were removed");
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return new ArrayList<>();
}
}
@@ -1,61 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import plugin.mrtransistor.AFK.DummyPlayer;
import java.util.ArrayList;
import java.util.List;
public class RemoveBot implements TabExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length != 1) return false;
int botsRemoved = 0;
int numBots = DummyPlayer.dummies.size();
int iter = 0;
for (int i = 0; i < numBots; i++) {
DummyPlayer dummy = DummyPlayer.dummies.get(iter);
if (dummy.getName().equals(args[0])) {
sender.sendMessage("Bot '" + ChatColor.DARK_GREEN + args[0] + ChatColor.RESET + "' with UUID:["
+ ChatColor.GOLD + dummy.getUniqueIDString() + ChatColor.RESET + "] was removed");
dummy.remove("Removed using command");
botsRemoved++;
} else iter++;
}
if (botsRemoved == 0) sender.sendMessage(ChatColor.RED + "There are no bots with specified name");
else if (botsRemoved == 1)
sender.sendMessage(ChatColor.GREEN + "1 bot was removed");
else sender.sendMessage(ChatColor.GREEN + Integer.toString(botsRemoved) + " bots were removed");
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return new ArrayList<>();
}
}
@@ -1,68 +0,0 @@
/*
* Plugin that adds server-side bots
*
* Copyright (C) 2021 MrTransistor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package plugin.mrtransistor.AFK.commands;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import plugin.mrtransistor.AFK.DummyPlayer;
import java.util.ArrayList;
import java.util.List;
public class SpawnBot implements TabExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length != 1) return false;
if (sender instanceof Player) {
if (args[0].length() > 16) {
sender.sendMessage(ChatColor.RED + "Player names can`t be longer than 16 symbols");
return true;
}
if (!DummyPlayer.dummyNames.contains(args[0])) {
DummyPlayer dummy = DummyPlayer.spawnBot(args[0], ((Player) sender).getLocation(), (Player) sender);
String json = "[{\"text\":\"Bot '\", \"color\":\"white\"}," +
"{\"text\":\"" + dummy.getName() + "\", \"color\":\"dark_green\"}," +
"{\"text\":\"' with UUID:[\", \"color\":\"white\"}," +
"{\"text\":\"" + dummy.getUniqueIDString() + "\", \"color\":\"gold\"," +
" \"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"" + dummy.getUniqueIDString() + "\"}," +
" \"hoverEvent\":{\"action\":\"show_text\", \"value\":\"Click to copy to clipboard\"}}," +
"{\"text\":\"] spawned\", \"color\":\"white\"}]";
Bukkit.dispatchCommand(sender, "tellraw " + sender.getName() + " " + json);
} else {
sender.sendMessage(ChatColor.RED + "Bot with name '" + ChatColor.DARK_GREEN + args[0] + ChatColor.RED + "' already exists");
}
return true;
}
sender.sendMessage("Sorry, this command is only for players");
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return new ArrayList<>();
}
}