Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Music command rework #160

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion config_sample/command_aliases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ maxplayers: max_players
minimap: links
music_list: musiclist
music_lists: musiclists
music: currentmusic
muteooc: ooc_mute
oocmute: ooc_mute
oocunmute: ooc_unmute
Expand Down
24 changes: 24 additions & 0 deletions docs/cmd_music.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Music commands:
/music help - Show this message.
/music showcurrent - Shows the currently playing song.
/music playlast - Play the last song that was played.
/music mode vote/queue/shuffle/normal - Set the music mode.
normal - Songs played are played normally.
queue - Songs are queued instead of played.
vote - Song plays are counted as a vote.

/music skip - Skips the currently playing song if there is another in queue.
/music info - Show info about current queue/votes
/music play <link> - Play a song from a link. Must be a direct link to an mp3 file.
/music playonce <link> - Play a song from a link, but do not loop it.
/music block <id> - Block a player from playing songs.
/music unblock <id> - Unblock a player from playing songs.
/music showlists - Shows all available music lists.
/music loadlist [<path>] - Load a client-side music list. If no argument is passed, reset the list.
/music loadarealist [<path>] - Load an area-wide music list. If no argument is passed, reset the list.
/music loadhublist [<path>] - Load a hub-wide music list. If no argument is passed, reset the list.
/music playrandom - Play a random song from the current list.
/music rebuildlist - Rebuild the current list.
/music savelist local/area/hub [<MusiclistName>] [<read_only>] - Save a musiclist on the server list. If the musiclist you're editing is already in the server list, you don't have to add [MusiclistName]
/music removelist local/area/hub <Category> <MusicName> - Remove a song from a musiclist. Remember to insert a file extension in <MusicName>. For songs without extension, put in .music.
/music addtolist local/area/hub <Category> <MusicName> [Length] [Path] - Add a song in a loaded musiclist. Remember to insert a file extension in <MusicName> unless you are using the optional [Path] (useful for streamed songs). If Length is 0, song will not loop. If Length is -1, song will loop. Any other value will tell the server the length of the song (in seconds).
9 changes: 9 additions & 0 deletions server/area.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from server import database
from server import commands
from server.music_manager import MusicManager
from server.evidence import EvidenceList
from server.exceptions import ClientError, AreaError, ArgumentError, ServerError
from server.constants import MusicEffect, derelative
Expand Down Expand Up @@ -102,6 +103,7 @@ def __init__(self, area_manager, name):
self.clients = set()
self.invite_list = set()
self.area_manager = area_manager
self.music_manager = MusicManager(self)
self._name = name

# Initialize prefs
Expand Down Expand Up @@ -2233,6 +2235,13 @@ def stop_demo(self):
else:
self.send_command("BN", self.background)

def is_owner(self, client):
"""
Check if a client is an owner of the area.
:param client: client
"""
return client in self.owners

class JukeboxVote:
"""Represents a single vote cast for the jukebox."""

Expand Down
80 changes: 30 additions & 50 deletions server/commands/music.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from . import mod_only

__all__ = [
"ooc_cmd_music",
"ooc_cmd_currentmusic",
"ooc_cmd_getmusic",
"ooc_cmd_jukebox_toggle",
Expand All @@ -30,54 +31,23 @@
]


def ooc_cmd_music(client, arg):
client.area.music_manager.handle_music_cmd(client, arg)


def ooc_cmd_currentmusic(client, arg):
"""
Show the current music playing.
Usage: /currentmusic
"""
if len(arg) != 0:
raise ArgumentError("This command has no arguments.")
if client.area.music == "":
raise ClientError("There is no music currently playing.")
if client.is_mod:
client.send_ooc(
"The current music is '{}' and was played by {} ({}).".format(
client.area.music,
client.area.music_player,
client.area.music_player_ipid,
)
)
else:
client.send_ooc(
"The current music is '{}' and was played by {}.".format(
client.area.music, client.area.music_player
)
)
# New command: /music showcurrent
ooc_cmd_music(client, arg)


def ooc_cmd_getmusic(client, arg):
"""
Grab the last played track in this area.
Usage: /getmusic
"""
if len(arg) != 0:
raise ArgumentError("This command has no arguments.")
if client.area.music == "":
raise ClientError("There is no music currently playing.")
client.send_command(
"MC",
client.area.music,
-1,
"",
client.area.music_looping,
0,
client.area.music_effects,
)
client.send_ooc(f"Playing track '{client.area.music}'.")
# New command: /music playlast
ooc_cmd_music(client, arg)


@mod_only(area_owners=True)
def ooc_cmd_jukebox_toggle(client, arg):
# New command: /music mode
"""
Toggle jukebox mode. While jukebox mode is on, all music changes become
votes for the next track, rather than changing the track immediately.
Expand Down Expand Up @@ -128,16 +98,20 @@ def ooc_cmd_jukebox_skip(client, arg):

def ooc_cmd_jukebox(client, arg):
"""
Show information about the jukebox's queue and votes.
Usage: /jukebox
Command to control the jukebox.
"""
if len(arg) != 0:
raise ArgumentError("This command has no arguments.")
if not client.area.jukebox:
raise ClientError("This area does not have a jukebox.")
if len(client.area.jukebox_votes) == 0:
client.send_ooc("The jukebox has no songs in it.")
else:

def jukebox_help() -> str:
return ''

def jukebox_info() -> str:
"""
Returns information about the jukebox's queue and votes.
"""

if len(client.area.jukebox_votes) == 0:
return "The jukebox has no songs in it."

total = 0
songs = []
voters = dict()
Expand Down Expand Up @@ -175,7 +149,13 @@ def ooc_cmd_jukebox(client, arg):
message += "-- CHANCE: " + \
str(round(chance[song] / total * 100))

client.send_ooc(f"The jukebox has the following songs in it:{message}")
return f"The jukebox has the following songs in it:{message}"

if len(arg) == 0:
client.send_ooc(jukebox_help())
return

args = shlex.split(arg)


def ooc_cmd_play(client, arg):
Expand Down
97 changes: 97 additions & 0 deletions server/music_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import os
import shlex
from pathlib import Path
from enum import Enum

from server.exceptions import ClientError, ServerError, ArgumentError, AreaError


script_dir = Path(os.path.dirname(os.path.realpath(__file__)))


class MusicMode(Enum):
NORMAL = "normal"
QUEUE = "queue"
SHUFFLE = "shuffle"
VOTE = "vote"


class MusicManager:
def __init__(self, area):
# This is the area the music manager belongs to
self.area = area
self.music = []
self.helptext = MusicManager.load_help()
self.musicmode = MusicMode.NORMAL

def handle_music_cmd(self, client, arg):
args = shlex.split(arg)

if len(args) < 1 or args[0] == "help":
client.send_ooc(self.helptext)
return

subcmd = args[0]
cmd_function_name = f"cmd_{subcmd}"
if not hasattr(self, cmd_function_name):
raise ClientError(f"Unknown subcommand {subcmd}. Use /music help for a list of commands.")

cmd_function = getattr(self, cmd_function_name)
cmd_function(client, args[1:])

def cmd_showcurrent(self, client, arg):
if len(arg) != 0:
raise ArgumentError("This command has no arguments.")
if client.area.music == "":
raise ClientError("There is no music currently playing.")
if client.is_mod:
client.send_ooc(
"The current music is '{}' and was played by {} ({}).".format(
client.area.music,
client.area.music_player,
client.area.music_player_ipid,
)
)
else:
client.send_ooc(
"The current music is '{}' and was played by {}.".format(
client.area.music, client.area.music_player
)
)

def cmd_playlast(self, client, arg):
if len(arg) != 0:
raise ArgumentError("This command has no arguments.")
if client.area.music == "":
raise ClientError("There is no music currently playing.")
client.send_command(
"MC",
client.area.music,
-1,
"",
client.area.music_looping,
0,
client.area.music_effects,
)
client.send_ooc(f"Playing track '{client.area.music}'.")

def cmd_mode(self, client, arg):
if not client.is_mod or self.area.is_owner(client):
raise ClientError("You need to be a moderator or area owner to change the music mode.")

if len(arg) != 1 or arg[0] not in (item.value for item in MusicMode):
raise ArgumentError("Usage: /music mode normal/queue/shuffle/vote. See /music help for more information.")

new_mode = MusicMode(arg[0])
self.musicmode = new_mode

client.send_ooc(f"Music mode set to '{self.musicmode.value}'.")

@staticmethod
def load_help():
helptext_path = script_dir / '../docs/cmd_music.txt'

with open(helptext_path, 'r') as file:
helptext = file.read()

return helptext
Loading