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,6 +490,11 @@ class AOProtocol(asyncio.Protocol):
 | 
			
		||||
                return
 | 
			
		||||
            try:
 | 
			
		||||
                name, length = self.server.get_song_data(args[0])
 | 
			
		||||
 | 
			
		||||
                if self.client.area.jukebox:
 | 
			
		||||
                    self.client.area.add_jukebox_vote(self.client, name, length)
 | 
			
		||||
                    logger.log_server('[{}][{}]Added a jukebox vote for {}.'.format(self.client.area.id, self.client.get_char_name(), name), self.client)
 | 
			
		||||
                else:
 | 
			
		||||
                    if len(args) > 2:
 | 
			
		||||
                        showname = args[2]
 | 
			
		||||
                        if len(showname) > 0 and not self.client.area.showname_changes_allowed:
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ from server.evidence import EvidenceList
 | 
			
		||||
 | 
			
		||||
class AreaManager:
 | 
			
		||||
    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.clients = set()
 | 
			
		||||
            self.invite_list = {}
 | 
			
		||||
@ -52,6 +52,7 @@ class AreaManager:
 | 
			
		||||
            self.locking_allowed = locking_allowed
 | 
			
		||||
            self.showname_changes_allowed = showname_changes_allowed
 | 
			
		||||
            self.shouts_allowed = shouts_allowed
 | 
			
		||||
            self.abbreviation = abbreviation
 | 
			
		||||
            self.owned = False
 | 
			
		||||
            self.cards = dict()
 | 
			
		||||
 | 
			
		||||
@ -64,6 +65,9 @@ class AreaManager:
 | 
			
		||||
            
 | 
			
		||||
            self.is_locked = False
 | 
			
		||||
 | 
			
		||||
            self.jukebox = jukebox
 | 
			
		||||
            self.jukebox_votes = []
 | 
			
		||||
 | 
			
		||||
        def new_client(self, client):
 | 
			
		||||
            self.clients.add(client)
 | 
			
		||||
 | 
			
		||||
@ -110,6 +114,70 @@ class AreaManager:
 | 
			
		||||
                    return False
 | 
			
		||||
            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):
 | 
			
		||||
            self.send_command('MC', name, cid)
 | 
			
		||||
            if self.music_looper:
 | 
			
		||||
@ -188,18 +256,12 @@ class AreaManager:
 | 
			
		||||
            for client in self.clients:
 | 
			
		||||
                client.send_command('LE', *self.get_evidence_list(client))
 | 
			
		||||
        
 | 
			
		||||
        def get_abbreviation(self):
 | 
			
		||||
            if self.name.lower().startswith("courtroom"):
 | 
			
		||||
                return "CR" + self.name.split()[-1]
 | 
			
		||||
            elif self.name.lower().startswith("area"):
 | 
			
		||||
                return "A" + self.name.split()[-1]
 | 
			
		||||
            elif len(self.name.split()) > 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()
 | 
			
		||||
    
 | 
			
		||||
        class JukeboxVote:
 | 
			
		||||
            def __init__(self, client, name, length):
 | 
			
		||||
                self.client = client
 | 
			
		||||
                self.name = name
 | 
			
		||||
                self.length = length
 | 
			
		||||
                self.chance = 1
 | 
			
		||||
 | 
			
		||||
    def __init__(self, server):
 | 
			
		||||
        self.server = server
 | 
			
		||||
@ -221,8 +283,12 @@ class AreaManager:
 | 
			
		||||
                item['showname_changes_allowed'] = False
 | 
			
		||||
            if 'shouts_allowed' not in item:
 | 
			
		||||
                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.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
 | 
			
		||||
 | 
			
		||||
    def default_area(self):
 | 
			
		||||
@ -239,3 +305,15 @@ class AreaManager:
 | 
			
		||||
            if area.id == num:
 | 
			
		||||
                return area
 | 
			
		||||
        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
 | 
			
		||||
            
 | 
			
		||||
        def disconnect(self):
 | 
			
		||||
            if self.area.jukebox:
 | 
			
		||||
                self.area.remove_jukebox_vote(self, True)
 | 
			
		||||
            self.transport.close()
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
                #self.send_host_message('This area is locked - you will be unable to send messages ICly.')
 | 
			
		||||
                raise ClientError("That area is locked!")
 | 
			
		||||
 | 
			
		||||
            if self.area.jukebox:
 | 
			
		||||
                self.area.remove_jukebox_vote(self, True)
 | 
			
		||||
 | 
			
		||||
            old_area = self.area
 | 
			
		||||
            if not area.is_char_available(self.char_id):
 | 
			
		||||
                try:
 | 
			
		||||
@ -203,7 +209,7 @@ class ClientManager:
 | 
			
		||||
                    for client in [x for x in area.clients if x.is_cm]:
 | 
			
		||||
                        owner = 'MASTER: {}'.format(client.get_char_name())
 | 
			
		||||
                        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:
 | 
			
		||||
                    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.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):
 | 
			
		||||
    if len(arg) != 0:
 | 
			
		||||
        raise ArgumentError('This command has no arguments.')
 | 
			
		||||
 | 
			
		||||
@ -232,7 +232,7 @@ class TsuServer3:
 | 
			
		||||
 | 
			
		||||
    def broadcast_global(self, client, msg, as_mod=False):
 | 
			
		||||
        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:
 | 
			
		||||
            ooc_name += '[M]'
 | 
			
		||||
        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):
 | 
			
		||||
        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)
 | 
			
		||||
        if self.config['use_district']:
 | 
			
		||||
            self.district_client.send_raw_message(
 | 
			
		||||
@ -251,7 +251,7 @@ class TsuServer3:
 | 
			
		||||
    def broadcast_need(self, client, msg):
 | 
			
		||||
        char_name = client.get_char_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']),
 | 
			
		||||
                               '=== Advert ===\r\n{} in {} [{}] needs {}\r\n==============='
 | 
			
		||||
                               .format(char_name, area_name, area_id, msg), pred=lambda x: not x.muted_adverts)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user