Jukebox + Area abbreviation finetuning.
- An area can now have a custom `abbreviation: XXX` set in `areas.yaml`. - Areas can have jukebox mode on by `jukebox: true` in `areas.yaml`. - When this mode is on, music changing is actually voting for the next music. - If no music is playing, or there is only your vote in there, it behaves as normal music changing. - In case of multiple votes, your vote gets added to a list, and may have a chance of being picked. - Check this list with `/jukebox`. - If not your music is picked, your voting power increases, making your music being picked next more likely. - If yours is picked, your voting power is reset to 0. - No matter how many people select the same song, if the song gets picked, all of them will have their voting power reset to 0. - Leaving an area, or picking a not-really-a-song (like 'PRELUDE', which is a category marker, basically), will remove your vote. - If there are no votes left (because every left, for example), the jukebox stops playing songs. - Mods can force a skip by `/jukebox_skip`. - Mods can also toggle an area's jukebox with `/jukebox_toggle`. - Mods can also still play songs with `/play`, though they might get cucked by the Jukebox.
This commit is contained in:
parent
8c859398f1
commit
9ce1d3fa40
@ -490,18 +490,23 @@ class AOProtocol(asyncio.Protocol):
|
|||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
name, length = self.server.get_song_data(args[0])
|
name, length = self.server.get_song_data(args[0])
|
||||||
if len(args) > 2:
|
|
||||||
showname = args[2]
|
if self.client.area.jukebox:
|
||||||
if len(showname) > 0 and not self.client.area.showname_changes_allowed:
|
self.client.area.add_jukebox_vote(self.client, name, length)
|
||||||
self.client.send_host_message("Showname changes are forbidden in this area!")
|
logger.log_server('[{}][{}]Added a jukebox vote for {}.'.format(self.client.area.id, self.client.get_char_name(), name), self.client)
|
||||||
return
|
|
||||||
self.client.area.play_music_shownamed(name, self.client.char_id, showname, length)
|
|
||||||
self.client.area.add_music_playing_shownamed(self.client, showname, name)
|
|
||||||
else:
|
else:
|
||||||
self.client.area.play_music(name, self.client.char_id, length)
|
if len(args) > 2:
|
||||||
self.client.area.add_music_playing(self.client, name)
|
showname = args[2]
|
||||||
logger.log_server('[{}][{}]Changed music to {}.'
|
if len(showname) > 0 and not self.client.area.showname_changes_allowed:
|
||||||
.format(self.client.area.id, self.client.get_char_name(), name), self.client)
|
self.client.send_host_message("Showname changes are forbidden in this area!")
|
||||||
|
return
|
||||||
|
self.client.area.play_music_shownamed(name, self.client.char_id, showname, length)
|
||||||
|
self.client.area.add_music_playing_shownamed(self.client, showname, name)
|
||||||
|
else:
|
||||||
|
self.client.area.play_music(name, self.client.char_id, length)
|
||||||
|
self.client.area.add_music_playing(self.client, name)
|
||||||
|
logger.log_server('[{}][{}]Changed music to {}.'
|
||||||
|
.format(self.client.area.id, self.client.get_char_name(), name), self.client)
|
||||||
except ServerError:
|
except ServerError:
|
||||||
return
|
return
|
||||||
except ClientError as ex:
|
except ClientError as ex:
|
||||||
|
@ -26,7 +26,7 @@ from server.evidence import EvidenceList
|
|||||||
|
|
||||||
class AreaManager:
|
class AreaManager:
|
||||||
class Area:
|
class Area:
|
||||||
def __init__(self, area_id, server, name, background, bg_lock, evidence_mod = 'FFA', locking_allowed = False, iniswap_allowed = True, showname_changes_allowed = False, shouts_allowed = True):
|
def __init__(self, area_id, server, name, background, bg_lock, evidence_mod = 'FFA', locking_allowed = False, iniswap_allowed = True, showname_changes_allowed = False, shouts_allowed = True, jukebox = False, abbreviation = ''):
|
||||||
self.iniswap_allowed = iniswap_allowed
|
self.iniswap_allowed = iniswap_allowed
|
||||||
self.clients = set()
|
self.clients = set()
|
||||||
self.invite_list = {}
|
self.invite_list = {}
|
||||||
@ -52,6 +52,7 @@ class AreaManager:
|
|||||||
self.locking_allowed = locking_allowed
|
self.locking_allowed = locking_allowed
|
||||||
self.showname_changes_allowed = showname_changes_allowed
|
self.showname_changes_allowed = showname_changes_allowed
|
||||||
self.shouts_allowed = shouts_allowed
|
self.shouts_allowed = shouts_allowed
|
||||||
|
self.abbreviation = abbreviation
|
||||||
self.owned = False
|
self.owned = False
|
||||||
self.cards = dict()
|
self.cards = dict()
|
||||||
|
|
||||||
@ -64,6 +65,9 @@ class AreaManager:
|
|||||||
|
|
||||||
self.is_locked = False
|
self.is_locked = False
|
||||||
|
|
||||||
|
self.jukebox = jukebox
|
||||||
|
self.jukebox_votes = []
|
||||||
|
|
||||||
def new_client(self, client):
|
def new_client(self, client):
|
||||||
self.clients.add(client)
|
self.clients.add(client)
|
||||||
|
|
||||||
@ -110,6 +114,70 @@ class AreaManager:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def add_jukebox_vote(self, client, music_name, length=-1):
|
||||||
|
if length <= 0:
|
||||||
|
self.remove_jukebox_vote(client, False)
|
||||||
|
else:
|
||||||
|
self.remove_jukebox_vote(client, True)
|
||||||
|
self.jukebox_votes.append(self.JukeboxVote(client, music_name, length))
|
||||||
|
client.send_host_message('Your song was added to the jukebox.')
|
||||||
|
if len(self.jukebox_votes) == 1:
|
||||||
|
self.start_jukebox()
|
||||||
|
|
||||||
|
def remove_jukebox_vote(self, client, silent):
|
||||||
|
for current_vote in self.jukebox_votes:
|
||||||
|
if current_vote.client.id == client.id:
|
||||||
|
self.jukebox_votes.remove(current_vote)
|
||||||
|
if not silent:
|
||||||
|
client.send_host_message('You removed your song from the jukebox.')
|
||||||
|
|
||||||
|
def get_jukebox_picked(self):
|
||||||
|
if len(self.jukebox_votes) == 0:
|
||||||
|
return None
|
||||||
|
elif len(self.jukebox_votes) == 1:
|
||||||
|
return self.jukebox_votes[0]
|
||||||
|
else:
|
||||||
|
weighted_votes = []
|
||||||
|
for current_vote in self.jukebox_votes:
|
||||||
|
i = 0
|
||||||
|
while i < current_vote.chance:
|
||||||
|
weighted_votes.append(current_vote)
|
||||||
|
i += 1
|
||||||
|
return random.choice(weighted_votes)
|
||||||
|
|
||||||
|
def start_jukebox(self):
|
||||||
|
# There is a probability that the jukebox feature has been turned off since then,
|
||||||
|
# we should check that.
|
||||||
|
# We also do a check if we were the last to play a song, just in case.
|
||||||
|
if not self.jukebox:
|
||||||
|
if self.current_music_player == 'The Jukebox' and self.current_music_player_ipid == 'has no IPID':
|
||||||
|
self.current_music = ''
|
||||||
|
return
|
||||||
|
|
||||||
|
vote_picked = self.get_jukebox_picked()
|
||||||
|
|
||||||
|
if vote_picked is None:
|
||||||
|
self.current_music = ''
|
||||||
|
return
|
||||||
|
|
||||||
|
self.send_command('MC', vote_picked.name, vote_picked.client.char_id)
|
||||||
|
|
||||||
|
self.current_music_player = 'The Jukebox'
|
||||||
|
self.current_music_player_ipid = 'has no IPID'
|
||||||
|
self.current_music = vote_picked.name
|
||||||
|
|
||||||
|
for current_vote in self.jukebox_votes:
|
||||||
|
# Choosing the same song will get your votes down to 0, too.
|
||||||
|
# Don't want the same song twice in a row!
|
||||||
|
if current_vote.name == vote_picked.name:
|
||||||
|
current_vote.chance = 0
|
||||||
|
else:
|
||||||
|
current_vote.chance += 1
|
||||||
|
|
||||||
|
if self.music_looper:
|
||||||
|
self.music_looper.cancel()
|
||||||
|
self.music_looper = asyncio.get_event_loop().call_later(vote_picked.length, lambda: self.start_jukebox())
|
||||||
|
|
||||||
def play_music(self, name, cid, length=-1):
|
def play_music(self, name, cid, length=-1):
|
||||||
self.send_command('MC', name, cid)
|
self.send_command('MC', name, cid)
|
||||||
if self.music_looper:
|
if self.music_looper:
|
||||||
@ -188,18 +256,12 @@ class AreaManager:
|
|||||||
for client in self.clients:
|
for client in self.clients:
|
||||||
client.send_command('LE', *self.get_evidence_list(client))
|
client.send_command('LE', *self.get_evidence_list(client))
|
||||||
|
|
||||||
def get_abbreviation(self):
|
class JukeboxVote:
|
||||||
if self.name.lower().startswith("courtroom"):
|
def __init__(self, client, name, length):
|
||||||
return "CR" + self.name.split()[-1]
|
self.client = client
|
||||||
elif self.name.lower().startswith("area"):
|
self.name = name
|
||||||
return "A" + self.name.split()[-1]
|
self.length = length
|
||||||
elif len(self.name.split()) > 1:
|
self.chance = 1
|
||||||
return "".join(item[0].upper() for item in self.name.split())
|
|
||||||
elif len(self.name) > 3:
|
|
||||||
return self.name[:3].upper()
|
|
||||||
else:
|
|
||||||
return self.name.upper()
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, server):
|
def __init__(self, server):
|
||||||
self.server = server
|
self.server = server
|
||||||
@ -221,8 +283,12 @@ class AreaManager:
|
|||||||
item['showname_changes_allowed'] = False
|
item['showname_changes_allowed'] = False
|
||||||
if 'shouts_allowed' not in item:
|
if 'shouts_allowed' not in item:
|
||||||
item['shouts_allowed'] = True
|
item['shouts_allowed'] = True
|
||||||
|
if 'jukebox' not in item:
|
||||||
|
item['jukebox'] = False
|
||||||
|
if 'abbreviation' not in item:
|
||||||
|
item['abbreviation'] = self.get_generated_abbreviation(item['area'])
|
||||||
self.areas.append(
|
self.areas.append(
|
||||||
self.Area(self.cur_id, self.server, item['area'], item['background'], item['bglock'], item['evidence_mod'], item['locking_allowed'], item['iniswap_allowed'], item['showname_changes_allowed'], item['shouts_allowed']))
|
self.Area(self.cur_id, self.server, item['area'], item['background'], item['bglock'], item['evidence_mod'], item['locking_allowed'], item['iniswap_allowed'], item['showname_changes_allowed'], item['shouts_allowed'], item['jukebox'], item['abbreviation']))
|
||||||
self.cur_id += 1
|
self.cur_id += 1
|
||||||
|
|
||||||
def default_area(self):
|
def default_area(self):
|
||||||
@ -239,3 +305,15 @@ class AreaManager:
|
|||||||
if area.id == num:
|
if area.id == num:
|
||||||
return area
|
return area
|
||||||
raise AreaError('Area not found.')
|
raise AreaError('Area not found.')
|
||||||
|
|
||||||
|
def get_generated_abbreviation(self, name):
|
||||||
|
if name.lower().startswith("courtroom"):
|
||||||
|
return "CR" + name.split()[-1]
|
||||||
|
elif name.lower().startswith("area"):
|
||||||
|
return "A" + name.split()[-1]
|
||||||
|
elif len(name.split()) > 1:
|
||||||
|
return "".join(item[0].upper() for item in name.split())
|
||||||
|
elif len(name) > 3:
|
||||||
|
return name[:3].upper()
|
||||||
|
else:
|
||||||
|
return name.upper()
|
||||||
|
@ -106,6 +106,8 @@ class ClientManager:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
if self.area.jukebox:
|
||||||
|
self.area.remove_jukebox_vote(self, True)
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
|
|
||||||
def change_character(self, char_id, force=False):
|
def change_character(self, char_id, force=False):
|
||||||
@ -171,6 +173,10 @@ class ClientManager:
|
|||||||
if area.is_locked and not self.is_mod and not self.id in area.invite_list:
|
if area.is_locked and not self.is_mod and not self.id in area.invite_list:
|
||||||
#self.send_host_message('This area is locked - you will be unable to send messages ICly.')
|
#self.send_host_message('This area is locked - you will be unable to send messages ICly.')
|
||||||
raise ClientError("That area is locked!")
|
raise ClientError("That area is locked!")
|
||||||
|
|
||||||
|
if self.area.jukebox:
|
||||||
|
self.area.remove_jukebox_vote(self, True)
|
||||||
|
|
||||||
old_area = self.area
|
old_area = self.area
|
||||||
if not area.is_char_available(self.char_id):
|
if not area.is_char_available(self.char_id):
|
||||||
try:
|
try:
|
||||||
@ -203,7 +209,7 @@ class ClientManager:
|
|||||||
for client in [x for x in area.clients if x.is_cm]:
|
for client in [x for x in area.clients if x.is_cm]:
|
||||||
owner = 'MASTER: {}'.format(client.get_char_name())
|
owner = 'MASTER: {}'.format(client.get_char_name())
|
||||||
break
|
break
|
||||||
msg += '\r\nArea {}: {} (users: {}) [{}][{}]{}'.format(area.get_abbreviation(), area.name, len(area.clients), area.status, owner, lock[area.is_locked])
|
msg += '\r\nArea {}: {} (users: {}) [{}][{}]{}'.format(area.abbreviation, area.name, len(area.clients), area.status, owner, lock[area.is_locked])
|
||||||
if self.area == area:
|
if self.area == area:
|
||||||
msg += ' [*]'
|
msg += ' [*]'
|
||||||
self.send_host_message(msg)
|
self.send_host_message(msg)
|
||||||
|
@ -159,6 +159,76 @@ def ooc_cmd_currentmusic(client, arg):
|
|||||||
client.send_host_message('The current music is {} and was played by {}.'.format(client.area.current_music,
|
client.send_host_message('The current music is {} and was played by {}.'.format(client.area.current_music,
|
||||||
client.area.current_music_player))
|
client.area.current_music_player))
|
||||||
|
|
||||||
|
def ooc_cmd_jukebox_toggle(client, arg):
|
||||||
|
if not client.is_mod:
|
||||||
|
raise ClientError('You must be authorized to do that.')
|
||||||
|
if len(arg) != 0:
|
||||||
|
raise ArgumentError('This command has no arguments.')
|
||||||
|
client.area.jukebox = not client.area.jukebox
|
||||||
|
client.area.send_host_message('A mod has set the jukebox to {}.'.format(client.area.jukebox))
|
||||||
|
|
||||||
|
def ooc_cmd_jukebox_skip(client, arg):
|
||||||
|
if not client.is_mod:
|
||||||
|
raise ClientError('You must be authorized to do that.')
|
||||||
|
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:
|
||||||
|
raise ClientError('There is no song playing right now, skipping is pointless.')
|
||||||
|
client.area.start_jukebox()
|
||||||
|
if len(client.area.jukebox_votes) == 1:
|
||||||
|
client.area.send_host_message('A mod has forced a skip, restarting the only jukebox song.')
|
||||||
|
else:
|
||||||
|
client.area.send_host_message('A mod has forced a skip to the next jukebox song.')
|
||||||
|
logger.log_server('[{}][{}]Skipped the current jukebox song.'.format(client.area.id, client.get_char_name()))
|
||||||
|
|
||||||
|
def ooc_cmd_jukebox(client, arg):
|
||||||
|
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_host_message('The jukebox has no songs in it.')
|
||||||
|
else:
|
||||||
|
total = 0
|
||||||
|
songs = []
|
||||||
|
voters = dict()
|
||||||
|
chance = dict()
|
||||||
|
message = ''
|
||||||
|
|
||||||
|
for current_vote in client.area.jukebox_votes:
|
||||||
|
if songs.count(current_vote.name) == 0:
|
||||||
|
songs.append(current_vote.name)
|
||||||
|
voters[current_vote.name] = [current_vote.client]
|
||||||
|
chance[current_vote.name] = current_vote.chance
|
||||||
|
else:
|
||||||
|
voters[current_vote.name].append(current_vote.client)
|
||||||
|
chance[current_vote.name] += current_vote.chance
|
||||||
|
total += current_vote.chance
|
||||||
|
|
||||||
|
for song in songs:
|
||||||
|
message += '\n- ' + song + '\n'
|
||||||
|
message += '-- VOTERS: '
|
||||||
|
|
||||||
|
first = True
|
||||||
|
for voter in voters[song]:
|
||||||
|
if first:
|
||||||
|
first = False
|
||||||
|
else:
|
||||||
|
message += ', '
|
||||||
|
message += voter.get_char_name() + ' [' + str(voter.id) + ']'
|
||||||
|
if client.is_mod:
|
||||||
|
message += '(' + str(voter.ipid) + ')'
|
||||||
|
message += '\n'
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
message += '-- CHANCE: 100'
|
||||||
|
else:
|
||||||
|
message += '-- CHANCE: ' + str(round(chance[song] / total * 100))
|
||||||
|
|
||||||
|
client.send_host_message('The jukebox has the following songs in it:{}'.format(message))
|
||||||
|
|
||||||
def ooc_cmd_coinflip(client, arg):
|
def ooc_cmd_coinflip(client, arg):
|
||||||
if len(arg) != 0:
|
if len(arg) != 0:
|
||||||
raise ArgumentError('This command has no arguments.')
|
raise ArgumentError('This command has no arguments.')
|
||||||
|
@ -232,7 +232,7 @@ class TsuServer3:
|
|||||||
|
|
||||||
def broadcast_global(self, client, msg, as_mod=False):
|
def broadcast_global(self, client, msg, as_mod=False):
|
||||||
char_name = client.get_char_name()
|
char_name = client.get_char_name()
|
||||||
ooc_name = '{}[{}][{}]'.format('<dollar>G', client.area.get_abbreviation(), char_name)
|
ooc_name = '{}[{}][{}]'.format('<dollar>G', client.area.abbreviation, char_name)
|
||||||
if as_mod:
|
if as_mod:
|
||||||
ooc_name += '[M]'
|
ooc_name += '[M]'
|
||||||
self.send_all_cmd_pred('CT', ooc_name, msg, pred=lambda x: not x.muted_global)
|
self.send_all_cmd_pred('CT', ooc_name, msg, pred=lambda x: not x.muted_global)
|
||||||
@ -242,7 +242,7 @@ class TsuServer3:
|
|||||||
|
|
||||||
def send_modchat(self, client, msg):
|
def send_modchat(self, client, msg):
|
||||||
name = client.name
|
name = client.name
|
||||||
ooc_name = '{}[{}][{}]'.format('<dollar>M', client.area.get_abbreviation(), name)
|
ooc_name = '{}[{}][{}]'.format('<dollar>M', client.area.abbreviation, name)
|
||||||
self.send_all_cmd_pred('CT', ooc_name, msg, pred=lambda x: x.is_mod)
|
self.send_all_cmd_pred('CT', ooc_name, msg, pred=lambda x: x.is_mod)
|
||||||
if self.config['use_district']:
|
if self.config['use_district']:
|
||||||
self.district_client.send_raw_message(
|
self.district_client.send_raw_message(
|
||||||
@ -251,7 +251,7 @@ class TsuServer3:
|
|||||||
def broadcast_need(self, client, msg):
|
def broadcast_need(self, client, msg):
|
||||||
char_name = client.get_char_name()
|
char_name = client.get_char_name()
|
||||||
area_name = client.area.name
|
area_name = client.area.name
|
||||||
area_id = client.area.get_abbreviation()
|
area_id = client.area.abbreviation
|
||||||
self.send_all_cmd_pred('CT', '{}'.format(self.config['hostname']),
|
self.send_all_cmd_pred('CT', '{}'.format(self.config['hostname']),
|
||||||
'=== Advert ===\r\n{} in {} [{}] needs {}\r\n==============='
|
'=== Advert ===\r\n{} in {} [{}] needs {}\r\n==============='
|
||||||
.format(char_name, area_name, area_id, msg), pred=lambda x: not x.muted_adverts)
|
.format(char_name, area_name, area_id, msg), pred=lambda x: not x.muted_adverts)
|
||||||
|
Loading…
Reference in New Issue
Block a user