Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial support for multiagent experiments #178

Merged
merged 9 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading