Commit 2e50b907 authored by talljosh's avatar talljosh

Added server options for selecting bot kind to use for HvM and for balance bot.

parent 6fb79c5f726f
......@@ -19,9 +19,10 @@
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="2">
<list size="3">
<item index="0" class="java.lang.String" itemvalue="PySide2" />
<item index="1" class="java.lang.String" itemvalue="contextvars" />
<item index="2" class="java.lang.String" itemvalue="asyncqt" />
</list>
</value>
</option>
......
v1.15.1
* Added a server option for requiring all players to select ‘Ready’ before starting a game instead of 70% of players. This is useful for tournament matches.
* Added a server option to select what kind of bot is used for balance bot and in Humans vs. Machines games.
* The list of available games now only allows 2 seconds to resolve DNS names for trusted servers. This means discovered LAN servers often appear sooner.
* Fixed server bug were the location of replays was not correctly stored and displayed.
* Fixed bug where missing stationary points in the pathfinding database could result in server and clients getting out of sync.
......
......@@ -41,7 +41,7 @@ LOOK_AHEAD_TICKS = 20
MIN_EVASION_TICKS = 5
def listAIs(showAll=False):
def get_available_bot_names(show_all=False):
'''
Returns a list of strings of the available AI classes.
'''
......@@ -60,14 +60,19 @@ def listAIs(showAll=False):
except Exception as e:
log.warning('Error loading AI %r: %s', aiName, e)
continue
if hasattr(c, 'BotClass') and (c.BotClass.generic or showAll):
if hasattr(c, 'BotClass') and (c.BotClass.generic or show_all):
results.append(aiName)
return results
def makeAIAgent(game, aiName, fromLevel=False, nick=None, difficulty=DEFAULT_BOT_DIFFICULTY):
c = __import__('trosnoth.bots.%s' % (aiName,), fromlist=['BotClass'])
return AIAgent(game, c.BotClass, fromLevel=fromLevel, nick=nick, difficulty=difficulty)
def get_bot_class(bot_name):
c = __import__(f'trosnoth.bots.{bot_name}', fromlist=['BotClass'])
return c.BotClass
def makeAIAgent(game, bot_name, fromLevel=False, nick=None, difficulty=DEFAULT_BOT_DIFFICULTY):
bot_class = get_bot_class(bot_name)
return AIAgent(game, bot_class, fromLevel=fromLevel, nick=nick, difficulty=difficulty)
class AIAgent(ConcreteAgent):
......
import functools
from django import forms
from trosnoth.bots.base import get_available_bot_names, get_bot_class
from trosnoth.const import BOT_DIFFICULTY_EASY, BOT_DIFFICULTY_MEDIUM, BOT_DIFFICULTY_HARD
from trosnoth.djangoapp.models import (
TrosnothServerSettings, TrosnothUser, TrosnothArena,
......@@ -74,6 +77,15 @@ class ManagePlayerForm(forms.ModelForm):
}
@functools.lru_cache()
def get_bot_kind_choices():
result = [('', '(Automatic)')]
for bot_name in get_available_bot_names():
bot_class = get_bot_class(bot_name)
result.append((bot_name, bot_class.nick))
return tuple(result)
class ArenaModelForm(forms.ModelForm):
balance_bot_difficulty = forms.TypedChoiceField(
empty_value=None,
......@@ -98,6 +110,12 @@ class ArenaModelForm(forms.ModelForm):
label='Humans vs. Machines bot difficulty',
)
balance_bot_kind = forms.ChoiceField(choices=get_bot_kind_choices())
machines_bot_kind = forms.ChoiceField(
choices=get_bot_kind_choices(),
label='Humans vs. Machines bot kind',
)
class Meta:
model = TrosnothArena
fields = (
......@@ -106,7 +124,9 @@ class ArenaModelForm(forms.ModelForm):
'autoStartCountDown',
'require_everyone_ready',
'balance_bot_difficulty',
'balance_bot_kind',
'machines_difficulty',
'machines_bot_kind',
'force_half_width',
'force_height',
'force_duration',
......
# Generated by Django 2.2.12 on 2021-01-27 11:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('trosnoth', '0030_trosnotharena_require_everyone_ready'),
]
operations = [
migrations.AddField(
model_name='trosnotharena',
name='balance_bot_kind',
field=models.TextField(default=''),
),
migrations.AddField(
model_name='trosnotharena',
name='machines_bot_kind',
field=models.TextField(default=''),
),
]
......@@ -101,6 +101,8 @@ class TrosnothArena(models.Model):
default=BOT_DIFFICULTY_EASY)
machines_difficulty = models.IntegerField(
default=DEFAULT_BOT_DIFFICULTY, verbose_name='Bot difficulty for Humans vs. Machines')
balance_bot_kind = models.TextField(default='')
machines_bot_kind = models.TextField(default='')
force_half_width = models.IntegerField(
null=True, blank=True, verbose_name='Forced map half width (blank disables)')
force_height = models.IntegerField(
......
......@@ -94,15 +94,24 @@ class HumansVsMachinesBotManager(object):
def _addBots(self):
game = self.universe.game
while self.botSurplus < 0:
bot_name = None
if game.serverInterface:
difficulty = game.serverInterface.get_machines_difficulty()
bot_name = game.serverInterface.get_machines_bot_name()
else:
difficulty = DEFAULT_BOT_DIFFICULTY
agent = yield game.addBot('ranger', team=self.botTeam, difficulty=difficulty)
if bot_name is None:
bot_name = self._get_default_bot_name()
agent = yield game.addBot(bot_name, team=self.botTeam, difficulty=difficulty)
self.agents.add(agent)
self.botSurplus += 1
def _get_default_bot_name(self):
# TODO: if it's on a 1v1 map, use SilverBot instead of RangerBot
return 'ranger'
def _removeBots(self):
while self.botSurplus > 0:
if not self.agents:
......
......@@ -86,7 +86,7 @@ def makeOptionParser():
def list_bots():
import trosnoth.bots.base
print('Available bot classes:', file=sys.stderr)
for name in trosnoth.bots.base.listAIs(showAll=True):
for name in trosnoth.bots.base.get_available_bot_names(show_all=True):
print(' %s' % (name,), file=sys.stderr)
return
......
......@@ -81,7 +81,7 @@ def makeOptionParser():
def listAIs():
import trosnoth.bots.base
print('Available AI classes:', file=sys.stderr)
for name in trosnoth.bots.base.listAIs():
for name in trosnoth.bots.base.get_available_bot_names():
print(' %s' % (name,), file=sys.stderr)
return
......
......@@ -276,10 +276,18 @@ class ServerInterface(object):
arena = self._arenaManager.getArenaRecord()
return arena.machines_difficulty
def get_machines_bot_name(self):
arena = self._arenaManager.getArenaRecord()
return arena.machines_bot_kind
def get_balance_bot_difficulty(self):
arena = self._arenaManager.getArenaRecord()
return arena.balance_bot_difficulty
def get_balance_bot_name(self):
arena = self._arenaManager.getArenaRecord()
return arena.balance_bot_kind or 'balance'
class AuthAMPProtocol(amp.AMP):
'''
......
......@@ -15,8 +15,6 @@ class BalanceTeamsTrigger(Trigger):
uneven, adds a bot to the disadvantaged team.
'''
botName = 'balance'
def __init__(self, level, delayAfterPlayerLeaves=30):
super(BalanceTeamsTrigger, self).__init__(level)
self.delayAfterPlayerLeaves = delayAfterPlayerLeaves
......@@ -104,7 +102,7 @@ class BalanceTeamsTrigger(Trigger):
self.addingAgent = True
try:
agent = yield self.world.game.addBot(
self.botName, team, difficulty=self.get_bot_difficulty())
self.get_bot_name(), team, difficulty=self.get_bot_difficulty())
self.agents.add(agent)
if agent.player is None:
yield agent.onPlayerSet.wait()
......@@ -113,6 +111,11 @@ class BalanceTeamsTrigger(Trigger):
self.addingAgent = False
self.world.callLater(0, self.checkTeams)
def get_bot_name(self):
if self.world.game.serverInterface:
return self.world.game.serverInterface.get_balance_bot_name()
return 'balance'
def get_bot_difficulty(self):
if not self.world.game.serverInterface:
return BOT_DIFFICULTY_EASY
......
......@@ -35,14 +35,14 @@ class AddBotsForLobbyTrigger(Trigger):
'''
def __init__(self, level, playerCount=6):
from trosnoth.bots.base import listAIs
from trosnoth.bots.base import get_available_bot_names
super(AddBotsForLobbyTrigger, self).__init__(level)
self.playerCount = playerCount
self.loop = WeakLoopingCall(self, 'recheck')
self.agents = []
self.newAgents = set()
self.aiNames = listAIs()
self.aiNames = get_available_bot_names()
def doActivate(self):
if not self.loop.running:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment