MultiServer: fix case sensitivity in server commands (#1156)
* fix case sensitivity in server commands * improve ambiguous match breakout * worried about accidentally swapping team and slot * Remove now unused import
This commit is contained in:
		
							parent
							
								
									ed23a426ec
								
							
						
					
					
						commit
						09d8c4b912
					
				|  | @ -1802,14 +1802,32 @@ class ServerCommandProcessor(CommonCommandProcessor): | |||
|             self.output(response) | ||||
|             return False | ||||
| 
 | ||||
|     def resolve_player(self, input_name: str) -> typing.Optional[typing.Tuple[int, int, str]]: | ||||
|         """ returns (team, slot, player name) """ | ||||
|         # first match case | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             if name == input_name: | ||||
|                 return team, slot, name | ||||
| 
 | ||||
|         # if no case-sensitive match, then match without case only if there's only 1 match | ||||
|         input_lower = input_name.lower() | ||||
|         match: typing.Optional[typing.Tuple[int, int, str]] = None | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             lowered = name.lower() | ||||
|             if lowered == input_lower: | ||||
|                 if match: | ||||
|                     return None  # ambiguous input_name | ||||
|                 match = (team, slot, name) | ||||
|         return match | ||||
| 
 | ||||
|     @mark_raw | ||||
|     def _cmd_collect(self, player_name: str) -> bool: | ||||
|         """Send out the remaining items to player.""" | ||||
|         seeked_player = player_name.lower() | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             if name.lower() == seeked_player: | ||||
|                 collect_player(self.ctx, team, slot) | ||||
|                 return True | ||||
|         player = self.resolve_player(player_name) | ||||
|         if player: | ||||
|             team, slot, _ = player | ||||
|             collect_player(self.ctx, team, slot) | ||||
|             return True | ||||
| 
 | ||||
|         self.output(f"Could not find player {player_name} to collect") | ||||
|         return False | ||||
|  | @ -1822,11 +1840,11 @@ class ServerCommandProcessor(CommonCommandProcessor): | |||
|     @mark_raw | ||||
|     def _cmd_forfeit(self, player_name: str) -> bool: | ||||
|         """Send out the remaining items from a player to their intended recipients.""" | ||||
|         seeked_player = player_name.lower() | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             if name.lower() == seeked_player: | ||||
|                 forfeit_player(self.ctx, team, slot) | ||||
|                 return True | ||||
|         player = self.resolve_player(player_name) | ||||
|         if player: | ||||
|             team, slot, _ = player | ||||
|             forfeit_player(self.ctx, team, slot) | ||||
|             return True | ||||
| 
 | ||||
|         self.output(f"Could not find player {player_name} to release") | ||||
|         return False | ||||
|  | @ -1834,12 +1852,12 @@ class ServerCommandProcessor(CommonCommandProcessor): | |||
|     @mark_raw | ||||
|     def _cmd_allow_forfeit(self, player_name: str) -> bool: | ||||
|         """Allow the specified player to use the !release command.""" | ||||
|         seeked_player = player_name.lower() | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             if name.lower() == seeked_player: | ||||
|                 self.ctx.allow_forfeits[(team, slot)] = True | ||||
|                 self.output(f"Player {player_name} is now allowed to use the !release command at any time.") | ||||
|                 return True | ||||
|         player = self.resolve_player(player_name) | ||||
|         if player: | ||||
|             team, slot, name = player | ||||
|             self.ctx.allow_forfeits[(team, slot)] = True | ||||
|             self.output(f"Player {name} is now allowed to use the !release command at any time.") | ||||
|             return True | ||||
| 
 | ||||
|         self.output(f"Could not find player {player_name} to allow the !release command for.") | ||||
|         return False | ||||
|  | @ -1847,13 +1865,12 @@ class ServerCommandProcessor(CommonCommandProcessor): | |||
|     @mark_raw | ||||
|     def _cmd_forbid_forfeit(self, player_name: str) -> bool: | ||||
|         """"Disallow the specified player from using the !release command.""" | ||||
|         seeked_player = player_name.lower() | ||||
|         for (team, slot), name in self.ctx.player_names.items(): | ||||
|             if name.lower() == seeked_player: | ||||
|                 self.ctx.allow_forfeits[(team, slot)] = False | ||||
|                 self.output( | ||||
|                     f"Player {player_name} has to follow the server restrictions on use of the !release command.") | ||||
|                 return True | ||||
|         player = self.resolve_player(player_name) | ||||
|         if player: | ||||
|             team, slot, name = player | ||||
|             self.ctx.allow_forfeits[(team, slot)] = False | ||||
|             self.output(f"Player {name} has to follow the server restrictions on use of the !release command.") | ||||
|             return True | ||||
| 
 | ||||
|         self.output(f"Could not find player {player_name} to forbid the !release command for.") | ||||
|         return False | ||||
|  |  | |||
|  | @ -0,0 +1,40 @@ | |||
| import unittest | ||||
| from MultiServer import Context, ServerCommandProcessor | ||||
| 
 | ||||
| 
 | ||||
| class TestResolvePlayerName(unittest.TestCase): | ||||
|     def test_resolve(self) -> None: | ||||
|         p = ServerCommandProcessor(Context("", 0, "", "", 0, 0, False)) | ||||
|         p.ctx.player_names = { | ||||
|             (1, 1): "AAA", | ||||
|             (1, 2): "aBc", | ||||
|             (1, 3): "abC", | ||||
|         } | ||||
|         assert not p.resolve_player("abc"), "ambiguous name entry shouldn't resolve to player" | ||||
|         assert not p.resolve_player("Abc"), "ambiguous name entry shouldn't resolve to player" | ||||
|         assert p.resolve_player("aBc") == (1, 2, "aBc"), "matching case resolve" | ||||
|         assert p.resolve_player("abC") == (1, 3, "abC"), "matching case resolve" | ||||
|         assert not p.resolve_player("aB"), "partial name shouldn't resolve to player" | ||||
|         assert not p.resolve_player("abCD"), "incorrect name shouldn't resolve to player" | ||||
| 
 | ||||
|         p.ctx.player_names = { | ||||
|             (1, 1): "aaa", | ||||
|             (1, 2): "abc", | ||||
|             (1, 3): "abC", | ||||
|         } | ||||
|         assert p.resolve_player("abc") == (1, 2, "abc"), "matching case resolve" | ||||
|         assert not p.resolve_player("Abc"), "ambiguous name entry shouldn't resolve to player" | ||||
|         assert not p.resolve_player("aBc"), "ambiguous name entry shouldn't resolve to player" | ||||
|         assert p.resolve_player("abC") == (1, 3, "abC"), "matching case resolve" | ||||
| 
 | ||||
|         p.ctx.player_names = { | ||||
|             (1, 1): "AbcdE", | ||||
|             (1, 2): "abc", | ||||
|             (1, 3): "abCD", | ||||
|         } | ||||
|         assert p.resolve_player("abc") == (1, 2, "abc"), "matching case resolve" | ||||
|         assert p.resolve_player("abC") == (1, 2, "abc"), "case insensitive resolves when 1 match" | ||||
|         assert p.resolve_player("Abc") == (1, 2, "abc"), "case insensitive resolves when 1 match" | ||||
|         assert p.resolve_player("ABC") == (1, 2, "abc"), "case insensitive resolves when 1 match" | ||||
|         assert p.resolve_player("abcd") == (1, 3, "abCD"), "case insensitive resolves when 1 match" | ||||
|         assert not p.resolve_player("aB"), "partial name shouldn't resolve to player" | ||||
		Loading…
	
		Reference in New Issue