Added Docker Support
This commit is contained in:
parent
47d44643c3
commit
04c5db2232
@ -5,4 +5,4 @@ RUN pip install --upgrade pip
|
|||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
ADD . .
|
ADD . .
|
||||||
|
|
||||||
CMD ["python", "main.py"]
|
CMD ["python", "bot.py"]
|
||||||
|
@ -3,6 +3,7 @@ from discord.ext import commands
|
|||||||
|
|
||||||
from cogs.minecraft import Minecraft
|
from cogs.minecraft import Minecraft
|
||||||
from cogs.user_management import UserManager
|
from cogs.user_management import UserManager
|
||||||
|
from cogs.spawner import Spawner
|
||||||
|
|
||||||
# Setup Environment
|
# Setup Environment
|
||||||
import os
|
import os
|
||||||
@ -12,11 +13,6 @@ load_dotenv()
|
|||||||
# Discord Stuff
|
# Discord Stuff
|
||||||
TOKEN = os.environ['TOKEN']
|
TOKEN = os.environ['TOKEN']
|
||||||
|
|
||||||
# Server Stuff
|
|
||||||
RCON_SERVER = os.environ['RCON_SERVER']
|
|
||||||
RCON_PASS = os.environ['RCON_PASS']
|
|
||||||
RCON_PORT = int(os.environ['RCON_PORT'])
|
|
||||||
|
|
||||||
# Setup Basic Permission
|
# Setup Basic Permission
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
intents.message_content = True
|
intents.message_content = True
|
||||||
@ -25,8 +21,9 @@ intents.message_content = True
|
|||||||
bot = commands.Bot(command_prefix='-', intents=intents)
|
bot = commands.Bot(command_prefix='-', intents=intents)
|
||||||
|
|
||||||
async def setup():
|
async def setup():
|
||||||
await bot.add_cog(Minecraft(bot, RCON_SERVER, RCON_PASS, RCON_PORT))
|
await bot.add_cog(Minecraft(bot))
|
||||||
await bot.add_cog(UserManager(bot))
|
await bot.add_cog(UserManager(bot))
|
||||||
|
await bot.add_cog(Spawner(bot))
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
|
@ -2,79 +2,14 @@ import discord
|
|||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from mcrcon import MCRcon
|
from mcrcon import MCRcon
|
||||||
import enum
|
import enum
|
||||||
from statemachine import StateMachine, State
|
from transitions import Machine
|
||||||
from statemachine.states import States
|
from cogs.spawner import containers
|
||||||
|
|
||||||
class BorderWarsSession(StateMachine):
|
class Whitelist:
|
||||||
"A workflow maschine for managing InGame States"
|
|
||||||
Nothing = State(initial=True)
|
|
||||||
Initialization = State()
|
|
||||||
Safe = State()
|
|
||||||
Fight = State()
|
|
||||||
SuddenDeath = State()
|
|
||||||
End = State()
|
|
||||||
|
|
||||||
init_game = Nothing.to(Initialization)
|
|
||||||
start_game = Initialization.to(Safe)
|
|
||||||
start_fight = Safe.to(Fight)
|
|
||||||
start_last_round = Fight.to(SuddenDeath)
|
|
||||||
end_game = Fight.to(End) | SuddenDeath.to(End)
|
|
||||||
|
|
||||||
abort = Initialization.to(Nothing) | Safe.to(Nothing) | Fight.to(Nothing) | SuddenDeath.to(Nothing) | End.to(Nothing) | Nothing.to(Nothing)
|
|
||||||
reset = End.to(Initialization)
|
|
||||||
|
|
||||||
@Initialization.enter
|
|
||||||
def initialization(self) -> list:
|
|
||||||
|
|
||||||
|
|
||||||
@Safe.enter
|
|
||||||
def safe(self) -> list:
|
|
||||||
# Chat countdown
|
|
||||||
return [
|
|
||||||
'''/title @a subtitle ["",{"text":"bei ","color":"blue"},{"text":"BORDER WARS!","bold":true,"color":"red"}]''',
|
|
||||||
'''/title @a title {"text":"Viel Glück!","bold":true,"color":"blue"}''',
|
|
||||||
"playsound minecraft:entity.wither.spawn ambient @a 0 64 080",
|
|
||||||
"/worldborder set 1000",
|
|
||||||
"/gamerule keepInventory true"
|
|
||||||
]
|
|
||||||
|
|
||||||
@Fight.enter
|
|
||||||
def fight(self) -> list:
|
|
||||||
# Timer Starten
|
|
||||||
return [
|
|
||||||
'''/title @a subtitle {"text":"ÜBERLEBEN!","bold":true,"color":"red"}''',
|
|
||||||
'''/title @a title {"text":"Möge der beste","color":"blue"}''',
|
|
||||||
"/playsound minecraft:item.totem.use ambient @a 0 64 0 80",
|
|
||||||
"/worldborder set 75 3600",
|
|
||||||
"/gamerule keepInventory false"
|
|
||||||
]
|
|
||||||
|
|
||||||
@SuddenDeath.enter
|
|
||||||
def death(self) -> list:
|
|
||||||
# Timer Starten
|
|
||||||
return [
|
|
||||||
'''/title @a title ["",{"text":"Sudden ","color":"dark_blue"},{"text":"DEATH!","bold":true,"color":"red"}]''',
|
|
||||||
"/playsound minecraft:entity.ender_dragon.growl ambient @a 0 64 0 80",
|
|
||||||
"/worldborder set 5 600"
|
|
||||||
]
|
|
||||||
|
|
||||||
@End.enter
|
|
||||||
def end(self, playername: str) -> list:
|
|
||||||
return [
|
|
||||||
"/worldborder center 0 0",
|
|
||||||
"/worldborder set 75",
|
|
||||||
"/gamerule keepInventory true",
|
|
||||||
'''/title @a subtitle ["",{"text":"''' + playername + '''","bold":true,"color":"red"},{"text":" gewinnt","color":"dark_blue"}]''',
|
|
||||||
'''/title @a title {"text":"ENDE!","color":"dark_blue"}''',
|
|
||||||
"/playsound minecraft:entity.ender_dragon_death ambient @a 0 64 0 80",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Whitelist(StateMachine):
|
|
||||||
"A workflow machine for managing Whitelist states"
|
"A workflow machine for managing Whitelist states"
|
||||||
On = State(initial=True)
|
On = None
|
||||||
Off = State()
|
Off = None
|
||||||
toggle = On.to(Off) | Off.to(On)
|
toggle = None
|
||||||
|
|
||||||
class RCON(MCRcon):
|
class RCON(MCRcon):
|
||||||
def __init__(self, ip: str, secret: str, port: int = 31066):
|
def __init__(self, ip: str, secret: str, port: int = 31066):
|
||||||
@ -86,50 +21,43 @@ class RCON(MCRcon):
|
|||||||
cmds = "/whitelist {}".format(self.whitelist.current_state.id)
|
cmds = "/whitelist {}".format(self.whitelist.current_state.id)
|
||||||
print(cmds)
|
print(cmds)
|
||||||
|
|
||||||
def _sendcmd(self, cmds: str | list) -> None:
|
def sendcmd(self, cmds) -> None:
|
||||||
if isinstance(cmds, str):
|
if isinstance(cmds, str):
|
||||||
return self.server.command(str)
|
return self.command(str)
|
||||||
if isinstance(cmds, list):
|
if isinstance(cmds, list):
|
||||||
return [self.server.commands(cmd) for cmd in cmds]
|
return [self.command(cmd) for cmd in cmds]
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
|
|
||||||
|
class States(enum.Enum):
|
||||||
|
NOTHING = 0
|
||||||
|
INIT = 1
|
||||||
|
SAFE = 2
|
||||||
|
FIGHT = 3
|
||||||
|
SUDDENDEATH = 4
|
||||||
|
END = 5
|
||||||
|
|
||||||
class Minecraft(commands.Cog):
|
class Minecraft(commands.Cog):
|
||||||
def __init__(self, bot: commands.Bot, ip: str, secret: str, port: int = 31066):
|
def __init__(self, bot: commands.Bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.server = RCON(ip, secret, port)
|
self.servers = dict()
|
||||||
self.session = BorderWarsSession()
|
|
||||||
self.whitelist = Whitelist()
|
|
||||||
|
|
||||||
@commands.hybrid_command(name='whitelist')
|
transitions = [
|
||||||
async def whitelist(self, ctx: commands.Context):
|
['init_game', States.NOTHING, States.INIT],
|
||||||
"""
|
['start_game', States.INIT, States.SAFE],
|
||||||
Toggles Servers Whitelist
|
['start_fight', States.SAFE, States.FIGHT],
|
||||||
|
['start_last_round', States.FIGHT, States.SUDDENDEATH],
|
||||||
|
['end_game', States.FIGHT, States.END],
|
||||||
|
['end_game', States.SUDDENDEATH, States.END],
|
||||||
|
['reset', States.END, States.INIT],
|
||||||
|
['abort', '*', States.NOTHING]
|
||||||
|
]
|
||||||
|
|
||||||
Parameters
|
self.machine = Machine(states=States, transitions=transitions, initial=States.NOTHING)
|
||||||
----------
|
|
||||||
ctx: commands.Context
|
|
||||||
The context of the command invocation
|
|
||||||
"""
|
|
||||||
await self.whitelist.activate_initial_state()
|
|
||||||
await ctx.send("Whitelist")
|
|
||||||
|
|
||||||
@commands.hybrid_command(name='start')
|
|
||||||
async def start(self, ctx: commands.Context):
|
|
||||||
"""
|
|
||||||
Starts a Border Wars Session
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
ctx: commands.Context
|
|
||||||
The context of the command invocation
|
|
||||||
"""
|
|
||||||
cmds =
|
|
||||||
await ctx.send("Start")
|
|
||||||
|
|
||||||
@commands.hybrid_command(name='init')
|
@commands.hybrid_command(name='init')
|
||||||
async def init(self, ctx: commands.Context):
|
async def init(self, ctx: commands.Context, server_name: str):
|
||||||
"""
|
"""
|
||||||
Initialize a Border Wars session
|
Initialize a Border Wars session
|
||||||
|
|
||||||
@ -137,18 +65,165 @@ class Minecraft(commands.Cog):
|
|||||||
----------
|
----------
|
||||||
ctx: commands.Context
|
ctx: commands.Context
|
||||||
The context of the command invocation
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Server on which the Session should be initialized
|
||||||
"""
|
"""
|
||||||
|
server_name = server_name.title()
|
||||||
|
|
||||||
|
c = None
|
||||||
|
for container in containers:
|
||||||
|
if server_name == container.name:
|
||||||
|
c = container
|
||||||
|
break
|
||||||
|
|
||||||
|
if not c:
|
||||||
|
await ctx.send("---The server doesn't run---")
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = RCON(str(c.ip), c.rcon_pass, c.rcon_port)
|
||||||
|
self.servers[server_name] = conn
|
||||||
|
|
||||||
cmds = [
|
cmds = [
|
||||||
|
"/effect give @a minecraft:resistance infinite 255 true",
|
||||||
|
"/effect give @a minecraft:saturation infinite 4 true",
|
||||||
|
"/tp @a 0 200 0",
|
||||||
|
"/gamemode adventure @a",
|
||||||
"/worldborder center 0 0",
|
"/worldborder center 0 0",
|
||||||
"/worldborder set 5",
|
"/worldborder set 5",
|
||||||
"/whitelist off"
|
"/whitelist off"
|
||||||
]
|
]
|
||||||
await self.session.activate_initial_state()
|
|
||||||
await self.session.init_game()
|
conn.sendcmd(cmds)
|
||||||
await ctx.send(self.session.current_state)
|
await ctx.send("init Border Wars Game")
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='safe')
|
||||||
|
async def safe(self, ctx: commands.Context, server_name: str):
|
||||||
|
"""
|
||||||
|
Switches to Safe Phase on a Border Wars session
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Server on which the Session should be initialized
|
||||||
|
"""
|
||||||
|
server_name = server_name.title()
|
||||||
|
|
||||||
|
c = None
|
||||||
|
for container in containers:
|
||||||
|
if server_name == container.name:
|
||||||
|
c = container
|
||||||
|
break
|
||||||
|
|
||||||
|
if not c:
|
||||||
|
await ctx.send("---The server doesn't run---")
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = self.servers.get(server_name)
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
await ctx.send("---Border Wars Session not Initialized---")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
cmds = [
|
||||||
|
'''/title @a subtitle ["",{"text":"bei ","color":"blue"},{"text":"BORDER WARS!","bold":true,"color":"red"}]''',
|
||||||
|
'''/title @a title {"text":"Viel Glück!","bold":true,"color":"blue"}''',
|
||||||
|
"playsound minecraft:entity.wither.spawn ambient @a 0 64 080",
|
||||||
|
"/worldborder set 1000",
|
||||||
|
"/gamerule keepInventory true",
|
||||||
|
"/gamemode survival @a",
|
||||||
|
"/effect clear @a"
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.sendcmd(cmds)
|
||||||
|
await ctx.send("Switched to Safe Phase")
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='fight')
|
||||||
|
async def fight(self, ctx: commands.Context, server_name: str):
|
||||||
|
"""
|
||||||
|
Switches to Fight Phase on a Border Wars session
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Server on which the Session should be initialized
|
||||||
|
"""
|
||||||
|
server_name = server_name.title()
|
||||||
|
|
||||||
|
c = None
|
||||||
|
for container in containers:
|
||||||
|
if server_name == container.name:
|
||||||
|
c = container
|
||||||
|
break
|
||||||
|
|
||||||
|
if not c:
|
||||||
|
await ctx.send("---The server doesn't run---")
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = self.servers.get(server_name)
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
await ctx.send("---Border Wars Session not Initialized---")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
cmds = [
|
||||||
|
'''/title @a subtitle {"text":"ÜBERLEBEN!","bold":true,"color":"red"}''',
|
||||||
|
'''/title @a title {"text":"Möge der beste","color":"blue"}''',
|
||||||
|
"/playsound minecraft:item.totem.use ambient @a 0 64 0 80",
|
||||||
|
"/worldborder set 75 3600",
|
||||||
|
"/gamerule keepInventory false"
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.sendcmd(cmds)
|
||||||
|
await ctx.send("Switched to Fight Phase")
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='death')
|
||||||
|
async def death(self, ctx: commands.Context, server_name: str):
|
||||||
|
"""
|
||||||
|
Switches to Sudden Death Phase on a Border Wars session
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Server on which the Session should be initialized
|
||||||
|
"""
|
||||||
|
server_name = server_name.title()
|
||||||
|
|
||||||
|
c = None
|
||||||
|
for container in containers:
|
||||||
|
if server_name == container.name:
|
||||||
|
c = container
|
||||||
|
break
|
||||||
|
|
||||||
|
if not c:
|
||||||
|
await ctx.send("---The server doesn't run---")
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = self.servers.get(server_name)
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
await ctx.send("---Border Wars Session not Initialized---")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
cmds = [
|
||||||
|
'''/title @a title ["",{"text":"Sudden ","color":"dark_blue"},{"text":"DEATH!","bold":true,"color":"red"}]''',
|
||||||
|
"/playsound minecraft:entity.ender_dragon.growl ambient @a 0 64 0 80",
|
||||||
|
"/worldborder set 5 600"
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.sendcmd(cmds)
|
||||||
|
await ctx.send("Switched to Sudden Death Phase")
|
||||||
|
|
||||||
@commands.hybrid_command(name='end')
|
@commands.hybrid_command(name='end')
|
||||||
async def end(self, ctx: commands.Context):
|
async def end(self, ctx: commands.Context, server_name: str, playername: str):
|
||||||
"""
|
"""
|
||||||
Ends a Border Wars session
|
Ends a Border Wars session
|
||||||
|
|
||||||
@ -156,30 +231,41 @@ class Minecraft(commands.Cog):
|
|||||||
----------
|
----------
|
||||||
ctx: commands.Context
|
ctx: commands.Context
|
||||||
The context of the command invocation
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Server on which the Session should be initialized
|
||||||
|
playername: str
|
||||||
|
Player which is announced as the Winner
|
||||||
"""
|
"""
|
||||||
await ctx.send("End")
|
server_name = server_name.title()
|
||||||
|
|
||||||
@commands.hybrid_command(name='rules')
|
c = None
|
||||||
async def rules(self, ctx: commands.Context):
|
for container in containers:
|
||||||
"""
|
if server_name == container.name:
|
||||||
Displays the Border Wars rules
|
c = container
|
||||||
|
break
|
||||||
|
|
||||||
Parameters
|
if not c:
|
||||||
----------
|
await ctx.send("---The server doesn't run---")
|
||||||
ctx: commands.Context
|
return
|
||||||
The context of the command invocation
|
|
||||||
"""
|
conn = self.servers.get(server_name)
|
||||||
await ctx.send("Rules")
|
|
||||||
|
if not conn:
|
||||||
|
await ctx.send("---Border Wars Session not Initialized---")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
cmds = [
|
||||||
|
"/worldborder center 0 0",
|
||||||
|
"/worldborder set 75",
|
||||||
|
"/gamerule keepInventory true",
|
||||||
|
'''/title @a subtitle ["",{"text":"''' + playername + '''","bold":true,"color":"red"},{"text":" gewinnt","color":"dark_blue"}]''',
|
||||||
|
'''/title @a title {"text":"ENDE!","color":"dark_blue"}''',
|
||||||
|
"/playsound minecraft:entity.ender_dragon_death ambient @a 0 64 0 80",
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.sendcmd(cmds)
|
||||||
|
await ctx.send("Ended Border Wars Session")
|
||||||
|
|
||||||
@commands.hybrid_command(name='custom')
|
|
||||||
async def custom(self, ctx: commands.Context):
|
|
||||||
"""
|
|
||||||
Register a custom command
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
ctx: commands.Context
|
|
||||||
The context of the command invocation
|
|
||||||
"""
|
|
||||||
await ctx.send("Custom")
|
|
||||||
|
|
||||||
|
242
bot/cogs/spawner.py
Normal file
242
bot/cogs/spawner.py
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import docker
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
from contextlib import closing
|
||||||
|
from datetime import datetime
|
||||||
|
import pytz
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from ipaddress import IPv4Address
|
||||||
|
import secrets
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Server:
|
||||||
|
container: None
|
||||||
|
name: str
|
||||||
|
ip: IPv4Address
|
||||||
|
port: int
|
||||||
|
players: int
|
||||||
|
rcon_pass: str
|
||||||
|
rcon_port: int
|
||||||
|
|
||||||
|
# Global List of all running Containers
|
||||||
|
containers = list()
|
||||||
|
|
||||||
|
def seed_generator():
|
||||||
|
seed = random.randrange(1_000_000_000, 100_000_000_000_000)
|
||||||
|
if random.randrange(0,2) == 0:
|
||||||
|
seed *= -1
|
||||||
|
return str(seed)
|
||||||
|
|
||||||
|
def find_free_port():
|
||||||
|
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||||
|
s.bind(('', 0))
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
return s.getsockname()[1]
|
||||||
|
|
||||||
|
class Spawner(commands.Cog):
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.client = docker.from_env()
|
||||||
|
self.client.images.pull('itzg/minecraft-server:latest')
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='spawn')
|
||||||
|
async def spawn(self,
|
||||||
|
ctx: commands.Context,
|
||||||
|
server_name: str,
|
||||||
|
world_url: str = None,
|
||||||
|
seed: str = None,
|
||||||
|
enable_command_blocks: bool = False,
|
||||||
|
max_players: int = 10,
|
||||||
|
enable_hardcore: bool = False,
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Spawns a standard defined Minecraft Server
|
||||||
|
Either from a World Download Link or a Seed
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Name of the Server
|
||||||
|
world_url: str
|
||||||
|
Download link of a minecraft world (Should be a downloadable ZIPP Archive
|
||||||
|
seed: str
|
||||||
|
Seed to generate a World from
|
||||||
|
enable_command_blocks: bool
|
||||||
|
Enable or disable command Block
|
||||||
|
max_players: int
|
||||||
|
Maximum Number of Players who can join the Server
|
||||||
|
enable_hardcore: bool
|
||||||
|
Enables Hardcore Minecraft
|
||||||
|
'''
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="Starting Server",
|
||||||
|
description=f'''
|
||||||
|
Setting up {server_name}
|
||||||
|
|
||||||
|
This could take up to **5 minutes**
|
||||||
|
''',
|
||||||
|
color=discord.Color.random(),
|
||||||
|
timestamp=datetime.now(pytz.timezone('Europe/Berlin'))
|
||||||
|
)
|
||||||
|
|
||||||
|
start = await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
port = find_free_port()
|
||||||
|
server_name = server_name.title()
|
||||||
|
passwd = secrets.token_hex(32)
|
||||||
|
rcon_port = find_free_port()
|
||||||
|
|
||||||
|
env = {
|
||||||
|
"EULA": "true",
|
||||||
|
"TYPE": "FABRIC",
|
||||||
|
"VERSION": "1.21.1",
|
||||||
|
"SERVER_NAME": server_name,
|
||||||
|
"LEVEL": server_name,
|
||||||
|
"ONLINE_MODE": "true",
|
||||||
|
"TZ": "Europe/Berlin",
|
||||||
|
"MOTD": "\u00a7d\u00a7khhh\u00a76Powered by\u00a7b Garde Studios\u00a76!\u00a7d\u00a7khhh",
|
||||||
|
|
||||||
|
"OVERRIDE_SERVER_PROPERTIES": "true",
|
||||||
|
"ENABLE_COMMAND_BLOCK": enable_command_blocks,
|
||||||
|
"GAMEMODE": "survival",
|
||||||
|
"FORCE_GAMEMODE": "true",
|
||||||
|
|
||||||
|
"RCON_PASSWORD": passwd,
|
||||||
|
"RCON_PORT": rcon_port,
|
||||||
|
"BROADCAST_CONSOLE_TO_OPS": "false",
|
||||||
|
"BROADCAST_RCON_TO_OPS": "false",
|
||||||
|
"SERVER_PORT": port,
|
||||||
|
|
||||||
|
"FORCE_REDOWNLOAD": "true",
|
||||||
|
"INIT_MEMORY": "500M",
|
||||||
|
"MAX_MEMORY": "2G",
|
||||||
|
"USE_AIKAR_FLAGS": "true",
|
||||||
|
|
||||||
|
#"MODS_FILE": "/extras/mods.txt",
|
||||||
|
"OPS_FILE": "https://git.cyperpunk.de/Garde-Studios/Uno-MC/raw/branch/main/ops.json",
|
||||||
|
"SYNC_SKIP_NEWER_IN_DESTINATION": "false",
|
||||||
|
"MAX_PLAYERS": max_players,
|
||||||
|
|
||||||
|
"ANNOUNCE_PLAYER_ACHIEVMENTS": "true",
|
||||||
|
"HARDCORE": enable_hardcore,
|
||||||
|
|
||||||
|
"SNOOPER_ENABLED": "false",
|
||||||
|
"SPAWN_PROTECTION": 0,
|
||||||
|
"VIEW_DISTANCE": 12,
|
||||||
|
"ALLOW_FLIGHT": "false",
|
||||||
|
|
||||||
|
# "RESOURCE_PACK": "",
|
||||||
|
# "RESOURCE_PACK_SHA1": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if not seed and not world_url:
|
||||||
|
seed = seed_generator()
|
||||||
|
if seed:
|
||||||
|
env["SEED"] = seed
|
||||||
|
if world_url:
|
||||||
|
env["WORLD"] = world_url
|
||||||
|
|
||||||
|
container = self.client.containers.run(
|
||||||
|
image='itzg/minecraft-server:latest',
|
||||||
|
environment=env,
|
||||||
|
detach=True,
|
||||||
|
hostname=server_name,
|
||||||
|
name=server_name,
|
||||||
|
network_mode='bridge',
|
||||||
|
ports={port:port, rcon_port:rcon_port},
|
||||||
|
restart_policy={"Name": "always"},
|
||||||
|
volumes={'mods.txt': {'bind': '/extras/mods.txt', 'mode': 'ro'}}
|
||||||
|
)
|
||||||
|
|
||||||
|
net = self.client.networks.get('bot_rcon')
|
||||||
|
net.connect(container)
|
||||||
|
|
||||||
|
ip = self.client.containers.get(server_name).attrs['NetworkSettings']['Networks']['bot_rcon']['IPAddress']
|
||||||
|
server = Server(container, server_name, IPv4Address(ip), port, max_players, passwd, rcon_port)
|
||||||
|
|
||||||
|
containers.append(server)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="Success",
|
||||||
|
description=f'''
|
||||||
|
**{server_name}** has started!
|
||||||
|
|
||||||
|
**Connection URL**:
|
||||||
|
garde-studios.de:{port}
|
||||||
|
''',
|
||||||
|
color=discord.Color.random(),
|
||||||
|
timestamp=datetime.now(pytz.timezone('Europe/Berlin'))
|
||||||
|
)
|
||||||
|
await start.delete()
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='servers')
|
||||||
|
async def servers(self, ctx: commands.Context):
|
||||||
|
'''
|
||||||
|
List all currently Running Servers
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
'''
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="Currently Running Servers",
|
||||||
|
description="List of all currently running Minecraft Servers",
|
||||||
|
color=discord.Color.random(),
|
||||||
|
timestamp=datetime.now(pytz.timezone('Europe/Berlin'))
|
||||||
|
)
|
||||||
|
|
||||||
|
for container in containers:
|
||||||
|
desc = f'''
|
||||||
|
*Status*: {container.container.status}
|
||||||
|
*URL*: garde-studios.de:{container.port}
|
||||||
|
'''
|
||||||
|
embed.add_field(name=f'{container.name} 0/{container.players}', value=desc)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@commands.hybrid_command(name='kill')
|
||||||
|
async def kill(self, ctx: commands.Context, server_name: str):
|
||||||
|
'''
|
||||||
|
Kill & remove currently Running Servers
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: commands.Context
|
||||||
|
The context of the command invocation
|
||||||
|
server_name: str
|
||||||
|
Name of the server that should be removed
|
||||||
|
'''
|
||||||
|
|
||||||
|
server_name = server_name.title()
|
||||||
|
|
||||||
|
# Check if Server exist
|
||||||
|
rm = str()
|
||||||
|
conn = None
|
||||||
|
for container in containers:
|
||||||
|
if container.name == server_name:
|
||||||
|
rm = server_name
|
||||||
|
conn = container
|
||||||
|
break
|
||||||
|
|
||||||
|
if not rm:
|
||||||
|
await ctx.send("---Server not found---")
|
||||||
|
return
|
||||||
|
|
||||||
|
conn.container.remove(force=True)
|
||||||
|
#self.client.volumes.get(server_name).remove()
|
||||||
|
containers.remove(conn)
|
||||||
|
|
||||||
|
await ctx.send(f"Server {server_name} killed successfully")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for _ in range(10):
|
||||||
|
print("|", seed_generator(), "|")
|
||||||
|
print("Port:", find_free_port())
|
@ -1,16 +1,15 @@
|
|||||||
services:
|
services:
|
||||||
bot:
|
bot:
|
||||||
|
container_name: bot
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
# volumes:
|
|
||||||
# - bot_data:/home
|
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
bot_data:
|
- ./bot_data:/home
|
||||||
driver: local
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
driver_opts:
|
networks:
|
||||||
type: none
|
- rcon
|
||||||
device: ./data
|
|
||||||
o: bind
|
|
||||||
|
networks:
|
||||||
|
rcon: {}
|
||||||
|
58
bot/mods.txt
Normal file
58
bot/mods.txt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Fabric API
|
||||||
|
https://cdn.modrinth.com/data/P7dR8mSH/versions/bK6OgzFj/fabric-api-0.102.1%2B1.21.1.jar
|
||||||
|
|
||||||
|
# Cloth Config API
|
||||||
|
https://cdn.modrinth.com/data/9s6osm5g/versions/7jtvrmVP/cloth-config-15.0.130-fabric.jar
|
||||||
|
|
||||||
|
# Moonlight Lib
|
||||||
|
#https://cdn.modrinth.com/data/twkfQtEc/versions/tP7HsFBI/moonlight-1.21-2.14.12-fabric.jar
|
||||||
|
|
||||||
|
# Yungs API
|
||||||
|
https://cdn.modrinth.com/data/Ua7DFN59/versions/Nx7XHO30/YungsApi-1.21-Fabric-5.0.0.jar
|
||||||
|
|
||||||
|
# Performance
|
||||||
|
https://cdn.modrinth.com/data/gvQqBUqZ/versions/5szYtenV/lithium-fabric-mc1.21.1-0.13.0.jar
|
||||||
|
https://cdn.modrinth.com/data/fALzjamp/versions/dPliWter/Chunky-1.4.16.jar
|
||||||
|
https://cdn.modrinth.com/data/s86X568j/versions/uT1cdd3k/ChunkyBorder-1.2.18.jar
|
||||||
|
https://cdn.modrinth.com/data/LFJf0Klb/versions/7e8Rxgsk/ce-2.1.1.jar
|
||||||
|
|
||||||
|
# Proxy
|
||||||
|
#https://cdn.modrinth.com/data/8dI2tmqs/versions/AQhF7kvw/FabricProxy-Lite-2.9.0.jar
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
#https://cdn.modrinth.com/data/dbVXHSlv/versions/YcE9H1C5/fabricexporter-1.0.11.jar
|
||||||
|
#https://cdn.modrinth.com/data/l6YH9Als/versions/qTSaozEL/spark-1.10.97-fabric.jar
|
||||||
|
|
||||||
|
# World Edit
|
||||||
|
https://cdn.modrinth.com/data/1u6JkXh5/versions/vBzkrSYP/worldedit-mod-7.3.6.jar
|
||||||
|
|
||||||
|
# Dynmap
|
||||||
|
# https://cdn.modrinth.com/data/fRQREgAc/versions/ipBhc6VW/Dynmap-3.7-beta-6-fabric-1.21.jar
|
||||||
|
|
||||||
|
# World Guard
|
||||||
|
https://cdn.modrinth.com/data/py6EMmAJ/versions/xpvSS4oW/yawp-0.0.2.10-alpha2.jar
|
||||||
|
https://cdn.modrinth.com/data/ohNO6lps/versions/gtorYSGm/ForgeConfigAPIPort-v21.1.0-1.21.1-Fabric.jar
|
||||||
|
|
||||||
|
# Permission Management
|
||||||
|
#https://cdn.modrinth.com/data/Vebnzrzj/versions/oLykW1F8/LuckPerms-Fabric-5.4.139.jar
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
#https://cdn.modrinth.com/data/HjmxVlSr/versions/2Z4xpeH5/YungsBetterMineshafts-1.20.4-Fabric-4.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/o1C1Dkj5/versions/4RpKnxDR/YungsBetterDungeons-1.20.4-Fabric-4.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/3dT9sgt4/versions/V46v23Uz/YungsBetterOceanMonuments-1.20.4-Fabric-3.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/kidLKymU/versions/Y05JQWx3/YungsBetterStrongholds-1.20.4-Fabric-4.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/Z2mXHnxP/versions/QplnGAIz/YungsBetterNetherFortresses-1.20.4-Fabric-2.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/t5FRdP87/versions/3CEVoaSN/YungsBetterWitchHuts-1.20.4-Fabric-3.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/XNlO7sBv/versions/Rnvv7pHS/YungsBetterDesertTemples-1.20.4-Fabric-3.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/Ht4BfYp6/versions/tx2e5Fjp/YungsBridges-1.20.4-Fabric-4.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/ZYgyPyfq/versions/F1adMKW8/YungsExtras-1.20.4-Fabric-4.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/z9Ve58Ih/versions/DIG3Vtjv/YungsBetterJungleTemples-1.20.4-Fabric-2.4.0.jar
|
||||||
|
#https://cdn.modrinth.com/data/2BwBOmBQ/versions/mRCm0pL5/YungsBetterEndIsland-1.20.4-Fabric-2.4.0.jar
|
||||||
|
https://cdn.modrinth.com/data/klXONLDA/versions/FQrJz3KA/villagesandpillages-fabric-mc1.21.1-1.0.1.jar
|
||||||
|
https://cdn.modrinth.com/data/tpehi7ww/versions/QmeQn0Mp/dungeons-and-taverns-v4.3.jar
|
||||||
|
#https://cdn.modrinth.com/data/fgmhI8kH/versions/5D3oDlLM/%5BFabric%5DCTOV-3.5.1.jar
|
||||||
|
|
||||||
|
#https://cdn.modrinth.com/data/7tKn1fLd/versions/65K7sgmM/MobCaptains-v3.2.1.jar
|
||||||
|
|
||||||
|
https://cdn.modrinth.com/data/cnIatHrN/versions/BfXSBkjs/universal_shops-1.7.1%2B1.21.jar
|
||||||
|
|
@ -1,14 +1,24 @@
|
|||||||
aiohttp==3.9.5
|
aiohappyeyeballs==2.4.0
|
||||||
|
aiohttp==3.10.5
|
||||||
aiosignal==1.3.1
|
aiosignal==1.3.1
|
||||||
attrs==23.2.0
|
async-timeout==4.0.3
|
||||||
|
attrs==24.2.0
|
||||||
|
certifi==2024.8.30
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
discord==2.3.2
|
||||||
discord.py==2.4.0
|
discord.py==2.4.0
|
||||||
|
docker==7.1.0
|
||||||
frozenlist==1.4.1
|
frozenlist==1.4.1
|
||||||
greenlet==3.0.3
|
greenlet==3.1.1
|
||||||
idna==3.7
|
idna==3.10
|
||||||
mcrcon==0.7.0
|
mcrcon==0.7.0
|
||||||
multidict==6.0.5
|
multidict==6.1.0
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
python-statemachine==2.3.1
|
pytz==2024.2
|
||||||
SQLAlchemy==2.0.31
|
requests==2.32.3
|
||||||
|
six==1.16.0
|
||||||
|
SQLAlchemy==2.0.35
|
||||||
|
transitions==0.9.2
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
yarl==1.9.4
|
urllib3==2.2.3
|
||||||
|
yarl==1.12.1
|
||||||
|
7
bot/spawner.py
Normal file
7
bot/spawner.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
|
||||||
|
print(client.containers.run("itzg/minecraft-server", detach=True))
|
||||||
|
|
||||||
|
|
@ -7,12 +7,5 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/postgresql/data
|
- ./db_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
volumes:
|
|
||||||
db_data:
|
|
||||||
driver: local
|
|
||||||
driver_opts:
|
|
||||||
type: none
|
|
||||||
device: ./data
|
|
||||||
o: bind
|
|
||||||
|
Loading…
Reference in New Issue
Block a user