diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ab36b49 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "endstone-home-pilot" +version = "0.0.1" +description = "A Home Pilot for endstone servers!" + +[project.entry-points."endstone"] +home_pilot = "endstone_home_pilot:Main" \ No newline at end of file diff --git a/src/endstone_home_pilot/__init__.py b/src/endstone_home_pilot/__init__.py new file mode 100644 index 0000000..81fcc0c --- /dev/null +++ b/src/endstone_home_pilot/__init__.py @@ -0,0 +1,3 @@ +from endstone_home_pilot.main import Main + +__all__ = ['Main'] \ No newline at end of file diff --git a/src/endstone_home_pilot/__pycache__/__init__.cpython-312.pyc b/src/endstone_home_pilot/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8b195a7 Binary files /dev/null and b/src/endstone_home_pilot/__pycache__/__init__.cpython-312.pyc differ diff --git a/src/endstone_home_pilot/__pycache__/config.cpython-312.pyc b/src/endstone_home_pilot/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..d6ee973 Binary files /dev/null and b/src/endstone_home_pilot/__pycache__/config.cpython-312.pyc differ diff --git a/src/endstone_home_pilot/__pycache__/database_controller.cpython-312.pyc b/src/endstone_home_pilot/__pycache__/database_controller.cpython-312.pyc new file mode 100644 index 0000000..d37cb4a Binary files /dev/null and b/src/endstone_home_pilot/__pycache__/database_controller.cpython-312.pyc differ diff --git a/src/endstone_home_pilot/__pycache__/main.cpython-312.pyc b/src/endstone_home_pilot/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..063ee2c Binary files /dev/null and b/src/endstone_home_pilot/__pycache__/main.cpython-312.pyc differ diff --git a/src/endstone_home_pilot/config.py b/src/endstone_home_pilot/config.py new file mode 100644 index 0000000..21b48c1 --- /dev/null +++ b/src/endstone_home_pilot/config.py @@ -0,0 +1,65 @@ +import tomllib +import tomlkit +import os + +def check_config(): + config = """ +# DO NOT TOUCH +config_version = "0.0.1" + +# [MAIN] +# sets the home command timeout in seconds +home_timeout = 10 + +# [ECONOMY] + +# if you have the Economy Pilot plugin, you can enable it here +economy_enabled = false + +# sets the minimum warp price, before the distance calculation, 0 to disable +min_home_teleport_price = 100 +# sets the distance calculation price multiplier, set to 0.0 to disable +home_teleport_price_multiplier = 1.0 + """ + + directory = 'config' + filename = 'home-pilot.toml' + file_path = os.path.join(directory, filename) + + os.makedirs(directory, exist_ok=True) + + if not os.path.isfile(file_path): + with open(file_path, 'w') as file: + file.write(config) + + +def load_config(): + directory = 'config' + filename = 'home-pilot.toml' + file_path = os.path.join(directory, filename) + if not os.path.isfile(file_path): + raise FileNotFoundError(f"The file {file_path} does not exist.") + + with open(file_path, 'rb') as file: + toml_data = tomllib.load(file) + home_timeout = toml_data.get("home_timeout") + + economy_enabled = toml_data.get("economy_enabled") + minimum_home_warp_price = toml_data.get("min_home_teleport_price") + home_teleport_price_multiplier = toml_data.get("home_teleport_price_multiplier") + + return home_timeout, economy_enabled, minimum_home_warp_price, home_teleport_price_multiplier + +### ECONOMY PILOT INTEGRATION + +def load_config_eco(): + directory = 'config' + filename = 'economy-pilot-lite.toml' + file_path = os.path.join(directory, filename) + if not os.path.isfile(file_path): + raise FileNotFoundError(f"The file {file_path} does not exist.") + + with open(file_path, 'rb') as file: + toml_data = tomllib.load(file) + currency_symbol = toml_data.get("currency_symbol") + return currency_symbol \ No newline at end of file diff --git a/src/endstone_home_pilot/database_controller.py b/src/endstone_home_pilot/database_controller.py new file mode 100644 index 0000000..dc8da66 --- /dev/null +++ b/src/endstone_home_pilot/database_controller.py @@ -0,0 +1,184 @@ +import sqlite3 + +from endstone_home_pilot.config import check_config, load_config +from endstone import ColorFormat +from pathlib import Path +from datetime import datetime + +check_config() + + +directory_path = Path('databases/home-pilot/') + +if not directory_path.exists(): + directory_path.mkdir(parents=True, exist_ok=True) + +def check_main_table(): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + command = """ + CREATE TABLE IF NOT EXISTS database( + uuid TEXT PRIMARY KEY, + username TEXT, + coordinate_x INTEGER, + coordinate_y INTEGER, + coordinate_z INTEGER, + last_used INTEGER + ); + """ + cursor.execute(command) + connection.commit() + connection.close() + +def check_user_data(uuid, username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + command = """ + INSERT OR IGNORE INTO database (uuid, username) + VALUES (?, ?); + """ + cursor.execute(command, (str(uuid), str(username))) + connection.commit() + connection.close() + +def check_player_username_for_change(uuid, unchecked_username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + cursor.execute("SELECT username FROM database WHERE uuid = ?;", (str(uuid),)) + + database_username = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + if database_username != unchecked_username: + cursor.execute(""" + UPDATE database + SET username = ? + WHERE uuid = ?; + """, (str(unchecked_username), str(uuid))) + + connection.commit() + connection.close() + +def set_home(username, coordinate_x, coordinate_y, coordinate_z) -> str: + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + + cursor.execute("SELECT EXISTS(SELECT 1 FROM database WHERE username = ?)", (str(username),)) + reciever_exists = int(cursor.fetchone()[0]) + if reciever_exists == 0: + return_string = f"{ColorFormat.RED}This user isnt logged in the database{ColorFormat.RESET}" + connection.close() + return return_string + + return_message = f"{ColorFormat.GOLD}your home coordinates were set to {ColorFormat.AQUA}x:{coordinate_x} y:{coordinate_y} z:{coordinate_z}{ColorFormat.RESET}" + + dateof2000 = datetime(2000, 1, 1, 0, 0, 0) + dateofnow = datetime.now() + + time_difference = dateofnow - dateof2000 + total_seconds = time_difference.total_seconds() + + cursor.execute("UPDATE database SET coordinate_x = ? WHERE username = ?;", (int(coordinate_x), str(username))) + cursor.execute("UPDATE database SET coordinate_y = ? WHERE username = ?;", (int(coordinate_y), str(username))) + cursor.execute("UPDATE database SET coordinate_z = ? WHERE username = ?;", (int(coordinate_z), str(username))) + cursor.execute("UPDATE database SET last_used = ? WHERE username = ?;", (int(total_seconds), str(username))) + + return_message = f"{ColorFormat.GOLD}Your home coordinates were set to {ColorFormat.AQUA}x:{int(coordinate_x)} y:{int(coordinate_y)} z:{int(coordinate_z)}{ColorFormat.RESET}" + + + connection.commit() + connection.close() + return return_message + +def get_home(username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + + cursor.execute("SELECT coordinate_x FROM database WHERE username = ?;", (str(username),)) + coordinate_x = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + cursor.execute("SELECT coordinate_y FROM database WHERE username = ?;", (str(username),)) + coordinate_y = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + cursor.execute("SELECT coordinate_z FROM database WHERE username = ?;", (str(username),)) + coordinate_z = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + + dateof2000 = datetime(2000, 1, 1, 0, 0, 0) + dateofnow = datetime.now() + + time_difference = dateofnow - dateof2000 + total_seconds = time_difference.total_seconds() + + cursor.execute("UPDATE database SET last_used = ? WHERE username = ?;", (int(total_seconds), str(username))) + + + connection.commit() + connection.close() + + return coordinate_x, coordinate_y, coordinate_z + +def get_home_no_cooldown(username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + + cursor.execute("SELECT coordinate_x FROM database WHERE username = ?;", (str(username),)) + coordinate_x = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + cursor.execute("SELECT coordinate_y FROM database WHERE username = ?;", (str(username),)) + coordinate_y = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + cursor.execute("SELECT coordinate_z FROM database WHERE username = ?;", (str(username),)) + coordinate_z = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + + connection.commit() + connection.close() + + return coordinate_x, coordinate_y, coordinate_z + +def get_last_home_usage(username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + + cursor.execute("SELECT last_used FROM database WHERE username = ?;", (str(username),)) + last_time_used = int(str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "")) + + connection.commit() + connection.close() + return last_time_used + +def delete_home(username): + connection = sqlite3.connect(f'{directory_path}/home_database.db') + cursor = connection.cursor() + + return_message = f"{ColorFormat.GOLD}home of the player {ColorFormat.GREEN}{username}{ColorFormat.RESET}{ColorFormat.GOLD} has been removed from the database{ColorFormat.RESET}" + + cursor.execute("SELECT EXISTS(SELECT 1 FROM database WHERE username = ?)", (str(username),)) + reciever_exists = int(cursor.fetchone()[0]) + if reciever_exists == 0: + return_string = f"{ColorFormat.RED}This user isnt logged in the database{ColorFormat.RESET}" + connection.close() + return return_string + + cursor.execute("UPDATE database SET coordinate_x = '' WHERE username = ?;", (str(username),)) + cursor.execute("UPDATE database SET coordinate_y = '' WHERE username = ?;", (str(username),)) + cursor.execute("UPDATE database SET coordinate_z = '' WHERE username = ?;", (str(username),)) + + connection.commit() + connection.close() + return return_message + + +### ECONOMY PILOT FUNCTIONS + +directory_path_eco = Path('databases/economy-pilot/') +def server_balance_fetch(username) -> str: + connection = sqlite3.connect(f'{directory_path_eco}/database.db') + cursor = connection.cursor() + + cursor.execute("SELECT EXISTS(SELECT 1 FROM database WHERE username = ?)", (str(username),)) + reciever_exists = int(cursor.fetchone()[0]) + if reciever_exists == 0: + return_string = f"{ColorFormat.RED}This user isnt logged in the database{ColorFormat.RESET}" + connection.close() + return return_string + + cursor.execute("SELECT money FROM database WHERE username = ?;", (str(username),)) + return_string = str(cursor.fetchone()).replace("(", "").replace(")", "").replace(",", "") + connection.commit() + connection.close() + + return return_string \ No newline at end of file diff --git a/src/endstone_home_pilot/main.py b/src/endstone_home_pilot/main.py new file mode 100644 index 0000000..bdaf7be --- /dev/null +++ b/src/endstone_home_pilot/main.py @@ -0,0 +1,171 @@ +from locale import currency +from pathlib import Path + +from endstone.plugin import Plugin +from endstone.event import event_handler, PlayerJoinEvent +from endstone.event import PlayerTeleportEvent +from endstone.command import Command, CommandSender +from endstone import ColorFormat, Player +from datetime import datetime +import math + +from endstone_home_pilot.config import load_config, check_config, load_config_eco +from endstone_home_pilot.database_controller import check_main_table, set_home, get_home, get_last_home_usage, \ + delete_home, server_balance_fetch, get_home_no_cooldown +from endstone_home_pilot.database_controller import check_user_data, check_player_username_for_change +version = "0.0.1" + +check_config() +config = load_config() +home_timeout = config[0] +economy_enabled = config[1] +min_home_teleport_price = config[2] +home_teleport_price_multiplier = config[3] +currency = load_config_eco() + +class Main(Plugin): + api_version = "0.5" + + commands = { + "sethome": { + "description": "This command sets the home of the user", + "usages": ["/sethome"], + "permissions": ["home_pilot.command.sethome"] + }, + "home": { + "description": "This command teleports the user to his home", + "usages": ["/home"], + "permissions": ["home_pilot.command.home"] + }, + "tprice": { + "description": "This command tells the user how much money will his teleportation cost", + "usages": ["/tprice"], + "permissions": ["home_pilot.command.tprice"] + }, + "delhome": { + "description" : "This command removes the home, meant for administrator use", + "usages": ["/delhome "], + "permissions": ["home_pilot.command.delhome"] + } + } + permissions = { + "home_pilot.command.sethome": { + "description": "Allows the users to use the sethome command", + "default": True + }, + "home_pilot.command.home": { + "description": "Allows the users to use the home command", + "default": True + }, + "home_pilot.command.delhome": { + "description": "Allows the users to use the delhome command", + "default": "op" + }, + "home_pilot.command.tprice": { + "description": "Allows the users to use the tprice command", + "default": True + } + } + + def on_enable(self): + self.register_events(self) + + def on_load(self): + self.logger.info(f""" + {ColorFormat.GOLD} + ╱|、 + (` - 7 + |、⁻〵 + じしˍ,)ノ + HOME PILOT, version - {version} + By Lunatechnika studios + {ColorFormat.RESET} + """) + self.logger.info(f"{ColorFormat.GOLD}Home Pilot has been loaded :3{ColorFormat.RESET}") + self.logger.info(f"{ColorFormat.GOLD}Checking Database...{ColorFormat.RESET}") + data_location = self.data_folder + + check_main_table() + + @event_handler + def on_player_join(self, event: PlayerJoinEvent): + player = event.player + self.logger.info(f"{ColorFormat.GOLD}Home Pilot is checking user's {ColorFormat.GREEN}{player.name}{ColorFormat.RESET} {ColorFormat.GOLD}records on the database{ColorFormat.RESET}") + check_user_data(player.unique_id, player.name) + check_player_username_for_change(player.unique_id, player.name) + + def on_command(self, sender: Player, command: Command, args: list[str]): + match command.name: + case "sethome": + if sender.name == "Server": + sender.send_message(f"{ColorFormat.RED}This command can only be ran by a player{ColorFormat.RESET}") + else: + player = sender.name + player_location_x = sender.location.x + player_location_y = sender.location.y + player_location_z = sender.location.z + sender.send_message(f"{set_home(player, player_location_x, player_location_y, player_location_z)}") + case "home": + if sender.name == "Server": + sender.send_message(f"{ColorFormat.RED}This command can only be ran by a player{ColorFormat.RESET}") + else: + player = sender.name + + dateof2000 = datetime(2000, 1, 1, 0, 0, 0) + dateofnow = datetime.now() + time_difference = dateofnow - dateof2000 + time_seconds_now = time_difference.total_seconds() + + last_used_seconds = get_last_home_usage(player) + + if int(time_seconds_now - last_used_seconds) < home_timeout: + sender.send_message(f"{ColorFormat.GOLD}You still have to wait for {ColorFormat.LIGHT_PURPLE}{home_timeout - int(time_seconds_now - last_used_seconds)}{ColorFormat.GOLD} seconds until you can use this command again{ColorFormat.RESET}") + + else: + home_location = get_home(player) + if home_location[0] == "''" or home_location[1] == "''" or home_location[2] == "''": + sender.send_message(f"{ColorFormat.RED}You haven't set your home yet! do that by using /sethome{ColorFormat.RESET}") + else: + if economy_enabled == False: + self.server.dispatch_command(self.server.command_sender,f"effect {player} blindness 3 100 true") + self.server.dispatch_command(self.server.command_sender, f"teleport {player} {int(home_location[0])} {int(home_location[1])} {int(home_location[2])}") + sender.send_message(f"{ColorFormat.GOLD}You have been teleported to home{ColorFormat.RESET}") + if economy_enabled == True: + location_x = int(sender.location.x) + location_z = int(sender.location.z) + + home_location = get_home_no_cooldown(sender.name) + home_location_x = int(home_location[0]) + home_location_z = int(home_location[2]) + + distance = int(math.sqrt(math.pow(location_x - home_location_x, 2)+ pow(location_z - home_location_z, 2))) + + player_balance = int(server_balance_fetch(sender.name)) + if player_balance >= int(min_home_teleport_price + distance * home_teleport_price_multiplier): + self.server.dispatch_command(self.server.command_sender,f"serverdeduct {player} {int(min_home_teleport_price + distance * home_teleport_price_multiplier)}") + self.server.dispatch_command(self.server.command_sender,f"effect {player} blindness 3 100 true") + self.server.dispatch_command(self.server.command_sender,f"teleport {player} {int(home_location[0])} {int(home_location[1])} {int(home_location[2])}") + sender.send_message(f"{ColorFormat.GOLD}You have been teleported to home for {ColorFormat.AQUA}{int(min_home_teleport_price + distance * home_teleport_price_multiplier)}{currency}{ColorFormat.RESET}") + else: + sender.send_message(f"{ColorFormat.RED}You need at least {ColorFormat.AQUA}{int(min_home_teleport_price + distance * home_teleport_price_multiplier)}{currency}{ColorFormat.RESET} to teleport back to home!{ColorFormat.RESET}") + case "delhome": + sender.send_message(f"{delete_home(str(args[0]))}") + case "tprice": + if economy_enabled: + location_x = int(sender.location.x) + location_z = int(sender.location.z) + + home_location = get_home_no_cooldown(sender.name) + home_location_x = int(home_location[0]) + home_location_z = int(home_location[2]) + + if home_location[0] == "''" or home_location[1] == "''" or home_location[2] == "''": + sender.send_message(f"{ColorFormat.RED}You haven't set your home yet! do that by using /sethome{ColorFormat.RESET}") + else: + distance = int(math.sqrt(math.pow(location_x - home_location_x, 2)+ pow(location_z - home_location_z, 2))) + sender.send_message(f"{ColorFormat.GOLD}You can be teleported home for {ColorFormat.AQUA}{int(min_home_teleport_price + distance * home_teleport_price_multiplier)}{currency}{ColorFormat.RESET}") + else: + sender.send_message(f"{ColorFormat.RED}You cannot use this feature until Economy Pilot support is enabled!{ColorFormat.RESET}") + + +