Skip to content

Commit

Permalink
Merge pull request #178 from noskill/br3
Browse files Browse the repository at this point in the history
initial support for multiagent experiments
  • Loading branch information
noskill authored Dec 4, 2024
2 parents 0ee3f15 + b55d07e commit 20dd998
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 92 deletions.
31 changes: 18 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.

---
name: build and test
on: [pull_request, push]
on:
- pull_request
- push
defaults:
run:
shell: bash -l {0}

jobs:
build:
name: build
runs-on: self-hosted
strategy:
matrix:
# Use these Java versions
java: [ 21, ] # Current Java LTS & minimum supported by Minecraft
java:
- 21
steps:
- name: lock file
run: lockfile /tmp/minecraft-test-lock
- name: checkout vereya
uses: actions/checkout@v3
with:
repository: trueagi-io/Vereya
repository: noSkill/Vereya
path: Vereya
ref: server
clean: false
- name: install vereya
run: rm /home/tester/.minecraft/mods/* ; ls && cd $GITHUB_WORKSPACE/Vereya/ && ./gradlew build && cp $GITHUB_WORKSPACE/Vereya/build/libs/* /home/tester/.minecraft/mods/
run: rm /home/tester/.minecraft/mods/* ; ls && cd $GITHUB_WORKSPACE/Vereya/ &&
./gradlew build && cp $GITHUB_WORKSPACE/Vereya/build/libs/* /home/tester/.minecraft/mods/ &&
mkdir $GITHUB_WORKSPACE/Vereya/server/mods/ ;
cp $GITHUB_WORKSPACE/Vereya/build/libs/* $GITHUB_WORKSPACE/Vereya/server/mods/
- name: install fabric
run: rsync -v $GITHUB_WORKSPACE/Vereya/fabric/* /home/tester/.minecraft/mods/
run: rsync -v $GITHUB_WORKSPACE/Vereya/fabric/* /home/tester/.minecraft/mods/ &&
cp $GITHUB_WORKSPACE/Vereya/fabric/* $GITHUB_WORKSPACE/Vereya/server/mods/
- name: checkout tagilmo
uses: actions/checkout@v3
with:
Expand All @@ -42,12 +44,15 @@ jobs:
env:
DISPLAY: :99
GITHUB_WORKSPACE: $GITHUB_WORKSPACE
- name: start minecraft server
run: cd Vereya/server && ./launch.sh &
env:
GITHUB_WORKSPACE: $GITHUB_WORKSPACE
- name: run test
run: |
ps a|grep [j]ava &&
conda activate py31 && cd $GITHUB_WORKSPACE/tests/vereya &&
python run_tests.py
- name: save java logs
if: always()
uses: actions/upload-artifact@v3
Expand Down
64 changes: 64 additions & 0 deletions examples/8_manyagents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from time import sleep
from tagilmo.utils.vereya_wrapper import MCConnector
from tagilmo.utils.mission_builder import AgentSection
import tagilmo.utils.mission_builder as mb
from examples.log import setup_logger


setup_logger()
agents = [AgentSection(name='Cristina'), AgentSection(name='Crestina')]
miss = mb.MissionXML(agentSections=agents)
# https://www.chunkbase.com/apps/superflat-generator
# flat world not working currently
# miss.setWorld(mb.flatworld("3;7,25*1,3*3,2;1;stronghold,biome_1,village,decoration,dungeon,lake,mineshaft,lava_lake"))
#miss.addAgent(1)
world = mb.defaultworld(
seed='5',
forceReset="false",
forceReuse="true")
miss.setWorld(world)

mc = MCConnector(miss)
mc.safeStart()


fullStatKeys = ['XPos', 'YPos', 'ZPos', 'Pitch', 'Yaw']
stats_old = [0]*len(fullStatKeys)
seenObjects = []
nearEntities = []
gridObjects = []

for i in range(600):
mc.observeProc()

stats_new = [mc.getFullStat(key) for key in fullStatKeys]
if stats_new != stats_old and stats_new[0] != None:
print(' '.join(['%s: %.2f' % (fullStatKeys[n], stats_new[n]) for n in range(len(stats_new))]))
stats_old = stats_new

crossObj = mc.getLineOfSight('type')
if crossObj is not None:
crossObj += ' ' + mc.getLineOfSight('hitType')
if not crossObj in seenObjects:
seenObjects += [crossObj]
print('******** Novel object in line-of-sight : ', crossObj)

nearEnt = mc.getNearEntities()
if nearEnt != None:
for e in nearEnt:
if e['name'] != 'Agent-0':
if not e['name'] in nearEntities:
nearEntities += [e['name']]
print('++++++++ Novel nearby entity: ', e['name'])
elif abs(e['x'] - mc.getFullStat('XPos')) + abs(e['y'] - mc.getFullStat('YPos')) < 1:
print('!!!!!!!! Very close entity ', e['name'])

grid = mc.getNearGrid()
for o in (grid if grid is not None else []):
if not o in gridObjects:
gridObjects += [o]
print('-------- Novel grid object: ', o)

sleep(0.5)

# run the script and control the agent manually to see updates
65 changes: 65 additions & 0 deletions examples/8_manyagents1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from examples.log import setup_logger
from time import sleep
from tagilmo.utils.vereya_wrapper import MCConnector
from tagilmo.utils.mission_builder import AgentSection
import tagilmo.utils.mission_builder as mb



setup_logger('app1.log')
agents = [AgentSection(name='Cristina'), AgentSection(name='Crestina')]
miss = mb.MissionXML(agentSections=agents)
# https://www.chunkbase.com/apps/superflat-generator
# flat world not working currently
# miss.setWorld(mb.flatworld("3;7,25*1,3*3,2;1;stronghold,biome_1,village,decoration,dungeon,lake,mineshaft,lava_lake"))
#miss.addAgent(1)
world = mb.defaultworld(
seed='5',
forceReset="false",
forceReuse="true")
miss.setWorld(world)

mc = MCConnector(miss, agentId=1)
mc.safeStart()


fullStatKeys = ['XPos', 'YPos', 'ZPos', 'Pitch', 'Yaw']
stats_old = [0]*len(fullStatKeys)
seenObjects = []
nearEntities = []
gridObjects = []

for i in range(600):
mc.observeProc()

stats_new = [mc.getFullStat(key) for key in fullStatKeys]
if stats_new != stats_old and stats_new[0] != None:
print(' '.join(['%s: %.2f' % (fullStatKeys[n], stats_new[n]) for n in range(len(stats_new))]))
stats_old = stats_new

crossObj = mc.getLineOfSight('type')
if crossObj is not None:
crossObj += ' ' + mc.getLineOfSight('hitType')
if not crossObj in seenObjects:
seenObjects += [crossObj]
print('******** Novel object in line-of-sight : ', crossObj)

nearEnt = mc.getNearEntities()
if nearEnt != None:
for e in nearEnt:
if e['name'] != 'Agent-0':
if not e['name'] in nearEntities:
nearEntities += [e['name']]
print('++++++++ Novel nearby entity: ', e['name'])
elif abs(e['x'] - mc.getFullStat('XPos')) + abs(e['y'] - mc.getFullStat('YPos')) < 1:
print('!!!!!!!! Very close entity ', e['name'])

grid = mc.getNearGrid()
for o in (grid if grid is not None else []):
if not o in gridObjects:
gridObjects += [o]
print('-------- Novel grid object: ', o)

sleep(0.5)

# run the script and control the agent manually to see updates
64 changes: 64 additions & 0 deletions examples/9_connect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from time import sleep
from tagilmo.utils.vereya_wrapper import MCConnector
from tagilmo.utils.mission_builder import AgentSection
import tagilmo.utils.mission_builder as mb
from examples.log import setup_logger


setup_logger()
agents = [AgentSection(name='Cristina')]
miss = mb.MissionXML(agentSections=agents)
# https://www.chunkbase.com/apps/superflat-generator
# flat world not working currently
# miss.setWorld(mb.flatworld("3;7,25*1,3*3,2;1;stronghold,biome_1,village,decoration,dungeon,lake,mineshaft,lava_lake"))
#miss.addAgent(1)
world = mb.defaultworld(
seed='5',
forceReset="false",
forceReuse="true")
miss.setWorld(world)

mc = MCConnector(miss, serverIp='127.0.0.1', serverPort=25565)
mc.safeStart()


fullStatKeys = ['XPos', 'YPos', 'ZPos', 'Pitch', 'Yaw']
stats_old = [0]*len(fullStatKeys)
seenObjects = []
nearEntities = []
gridObjects = []

for i in range(600):
mc.observeProc()

stats_new = [mc.getFullStat(key) for key in fullStatKeys]
if stats_new != stats_old and stats_new[0] != None:
print(' '.join(['%s: %.2f' % (fullStatKeys[n], stats_new[n]) for n in range(len(stats_new))]))
stats_old = stats_new

crossObj = mc.getLineOfSight('type')
if crossObj is not None:
crossObj += ' ' + mc.getLineOfSight('hitType')
if not crossObj in seenObjects:
seenObjects += [crossObj]
print('******** Novel object in line-of-sight : ', crossObj)

nearEnt = mc.getNearEntities()
if nearEnt != None:
for e in nearEnt:
if e['name'] != 'Agent-0':
if not e['name'] in nearEntities:
nearEntities += [e['name']]
print('++++++++ Novel nearby entity: ', e['name'])
elif abs(e['x'] - mc.getFullStat('XPos')) + abs(e['y'] - mc.getFullStat('YPos')) < 1:
print('!!!!!!!! Very close entity ', e['name'])

grid = mc.getNearGrid()
for o in (grid if grid is not None else []):
if not o in gridObjects:
gridObjects += [o]
print('-------- Novel grid object: ', o)

sleep(0.5)

# run the script and control the agent manually to see updates
4 changes: 2 additions & 2 deletions examples/log.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging

def setup_logger():
def setup_logger(log_file='app.log'):
# create logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
Expand All @@ -14,7 +14,7 @@ def setup_logger():
logger.addHandler(ch)


f = logging.handlers.RotatingFileHandler('app.log')
f = logging.handlers.RotatingFileHandler(log_file)
f.setFormatter(formatter)
f.setLevel(logging.DEBUG)
logger.addHandler(f)
Expand Down
53 changes: 26 additions & 27 deletions tagilmo/VereyaPython/agent_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __init__(self) -> None:

def startMission(self, mission: MissionSpec, client_pool: List[ClientInfo],
mission_record: MissionRecordSpec, role: int,
unique_experiment_id: str):
unique_experiment_id: str, serverIp=None, serverPort=0):
logger.debug('startMission')
self.world_state.clear()
self.testSchemasCompatible()
Expand All @@ -86,32 +86,30 @@ def startMission(self, mission: MissionSpec, client_pool: List[ClientInfo],

if self.world_state.is_mission_running:
raise MissionException("A mission is already running.", MissionErrorCode.MISSION_ALREADY_RUNNING)
self.initializeOurServers( mission, mission_record, role, unique_experiment_id )
self.initializeOurServers( mission, mission_record, role,
unique_experiment_id,
serverIp, serverPort)

pool = None
if role == 0:
logger.info("creating mission")
# We are the agent responsible for the integrated server.
# If we are part of a multi-agent mission, our mission should have been started before any of the others are attempted.
# This means we are in a position to reserve clients in the client pool:
reservedAgents = asyncio.run_coroutine_threadsafe(self.reserveClients(client_pool,
mission.getNumberOfAgents()),
self.io_service).result()

if len(reservedAgents) != mission.getNumberOfAgents():
# Not enough clients available - go no further.
logger.error("Failed to reserve sufficient clients - throwing MissionException.")
if (mission.getNumberOfAgents() == 1):
raise MissionException("Failed to find an available client for self.mission - tried all the clients in the supplied client pool.", MissionErrorCode.MISSION_INSUFFICIENT_CLIENTS_AVAILABLE)
else:
raise MissionException("There are not enough clients available in the ClientPool to start self." + str(mission.getNumberOfAgents()) + " agent mission.", MissionErrorCode.MISSION_INSUFFICIENT_CLIENTS_AVAILABLE)
pool = reservedAgents
else:
logger.info(f"our role {role}, joining existing mission")

logger.info("creating mission")
# assume each agent is run by it's own AgentHost
reservedAgents = asyncio.run_coroutine_threadsafe(self.reserveClients(client_pool,
1),
self.io_service).result()

if len(reservedAgents) != 1:
# Not enough clients available - go no further.
logger.error("Failed to reserve sufficient clients - throwing MissionException.")
if (mission.getNumberOfAgents() == 1):
raise MissionException("Failed to find an available client for self.mission - tried all the clients in the supplied client pool.", MissionErrorCode.MISSION_INSUFFICIENT_CLIENTS_AVAILABLE)
else:
raise MissionException("There are not enough clients available in the ClientPool to start self." + str(mission.getNumberOfAgents()) + " agent mission.", MissionErrorCode.MISSION_INSUFFICIENT_CLIENTS_AVAILABLE)
pool = reservedAgents
assert self.current_mission_init is not None
if( mission.getNumberOfAgents() > 1 and role > 0 \
and not self.current_mission_init.hasMinecraftServerInformation()):
raise NotImplementedError("role > 0 is not implemented yet")
#if( mission.getNumberOfAgents() > 1 and role > 0 \
# and not self.current_mission_init.hasMinecraftServerInformation()):
# raise NotImplementedError("role > 0 is not implemented yet")

# work through the client pool until we find a client to run our mission for us
assert pool
Expand Down Expand Up @@ -147,6 +145,7 @@ async def reserveClients(self, client_pool: List[ClientInfo], clients_required:
logger.info("Reserving client, received reply from " + str(item.ip_address) + ": " + reply)
malmo_reservation_prefix = "MALMOOK"
malmo_mismatch = "MALMOERRORVERSIONMISMATCH"
malmo_busy = "MALMOBUSY"
if reply.startswith(malmo_reservation_prefix):
# Successfully reserved self.client.
reservedClients.add(item)
Expand Down Expand Up @@ -223,7 +222,6 @@ def findClient(self, client_pool: List[ClientInfo]):
tried all the clients in the \
supplied client pool.", MissionErrorCode.MISSION_INSUFFICIENT_CLIENTS_AVAILABLE )


def peekWorldState(self) -> WorldState:
with self.world_state_mutex:
# Copy while holding lock.
Expand All @@ -243,10 +241,11 @@ def getRecordingTemporaryDirectory(self) -> str:

def initializeOurServers(self, mission: MissionSpec,
mission_record: MissionRecordSpec, role: int,
unique_experiment_id: str) -> None:
unique_experiment_id: str, serverIp: Optional[str], serverPort:int) -> None:
logging.debug("Initialising servers...")
# make a MissionInit structure with default settings
self.current_mission_init = MissionInitSpec.from_param(mission, unique_experiment_id, role)
self.current_mission_init = MissionInitSpec.from_param(mission, unique_experiment_id, role,
serverIp, serverPort)
self.current_mission_record = MissionRecord(mission_record)
self.current_role = role
self.listenForMissionControlMessages(self.current_mission_init.getAgentMissionControlPort())
Expand Down
Loading

0 comments on commit 20dd998

Please sign in to comment.