Skip to content

Commit

Permalink
Updating the tests and adding support for performance writing. Closes #…
Browse files Browse the repository at this point in the history
…114 #115 (#116)

* Updating the tests and adding support for performance writing.

* Updating the version number.

* Upgrading the status writer to support keep alive pyro packets and log forwarding. Closes #115.

* Closes #87 
* Closes #69 
* Fixingcomment by brandon!

* Cleaning up malmo.

* Moving Malmo back to the MineRL branch.

* Adding Python3.7 compatability.

* Fixing festering wounds.
  • Loading branch information
MadcowD authored Jul 1, 2019
1 parent e1938d9 commit a18fb4c
Show file tree
Hide file tree
Showing 18 changed files with 328 additions and 100 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ var/

tests/logs/
.vscode/settings.json
*/performance
4 changes: 2 additions & 2 deletions docs/source/environments/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ General Information
The :code:`minerl` package includes several environments as follows.
This page describes each of the incldued environments, provides usage samples,
and describes the exact action and observation space provided by each
environment.
environment!



Expand All @@ -17,7 +17,7 @@ environment.
be evaluated in :code:`MineRLObtainDiamond-v0` which has **sparse** rewards. See `MineRLObtainDiamond-v0`_.

.. note::
Al environments offer a default no-op action via :code:`env.action_space.noop()`
All environments offer a default no-op action via :code:`env.action_space.noop()`
and a random action via :code:`env.action_space.sample()`

.. include:: handlers.rst
Expand Down
14 changes: 14 additions & 0 deletions minerl/env/comms.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,17 @@ def recvall(sock, count):
buf += newbuf
count -= len(newbuf)
return buf


class QueueLogger(logging.StreamHandler):
def __init__(self, queue):
self._queue = queue
return super().__init__(None)

def flush(self):
pass


def emit(self, record):
self._queue.append((self.level, record))

134 changes: 91 additions & 43 deletions minerl/env/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# ------------------------------------------------------------------------------------------------

import collections

import copy
import json
import logging
Expand All @@ -38,13 +39,14 @@
from lxml import etree
from minerl.env import comms
from minerl.env.comms import retry
from minerl.env.malmo import InstanceManager, malmo_version
from minerl.env.malmo import InstanceManager, malmo_version, launch_queue_logger_thread

logger = logging.getLogger(__name__)

missions_dir = os.path.join(os.path.dirname(__file__), 'missions')



class EnvException(Exception):
"""A special exception thrown in the creation of an environment's Malmo mission XML.
Expand Down Expand Up @@ -113,6 +115,7 @@ def __init__(self, xml, observation_space, action_space, port=None, noop_action=
self.ns = '{http://ProjectMalmo.microsoft.com}'
self.client_socket = None


self.exp_uid = ""
self.done = True
self.synchronous = True
Expand All @@ -123,14 +126,62 @@ def __init__(self, xml, observation_space, action_space, port=None, noop_action=

self.xml_file = xml
self.has_init = False
self.instance = None
self.had_to_clean = False

self._already_closed = False
self.instance = self._get_new_instance(port)


self._setup_spaces(observation_space, action_space)


self.resets = 0
self.done = True

def _get_new_instance(self, port=None):
"""
Gets a new instance and sets up a logger if need be.
"""

if not port is None:
instance = InstanceManager.add_existing_instance(port)
else:
instance = InstanceManager.get_instance(os.getpid())

if InstanceManager.is_remote():
launch_queue_logger_thread(instance, self.is_closed)

instance.launch()
return instance


def _setup_spaces(self, observation_space, action_space):
self.action_space = action_space
self.observation_space = observation_space

def map_space(space):
if isinstance(space, gym.spaces.Discrete) or isinstance(space, minerl.env.spaces.Enum):
return 0
elif isinstance(space, gym.spaces.Box):
return np.zeros(shape=space.shape, dtype=space.dtype)
else:
try:
return space.default()
except NameError:
raise ValueError(
'Specify non-None default_action in gym.register or extend all action spaces with default() method')
if self._default_action is None:
self._default_action = {key: map_space(
space) for key, space in action_space.spaces.items()}

def noop_func(a):
return deepcopy(self._default_action)

self.init(observation_space, action_space, port=port)
boundmethd = _bind(self.action_space, noop_func)
self.action_space.noop = boundmethd

def init(self, observation_space, action_space, port=None):

def init(self):
"""Initializes the MineRL Environment.
Note:
Expand All @@ -146,18 +197,14 @@ def init(self, observation_space, action_space, port=None):
ValueError: The space specified for this environment does not have a default action.
NotImplementedError: When multiagent environments are attempted to be used.
"""
episode = 0
exp_uid = None
if self.instance == None:
if not port is None:
self.instance = InstanceManager.add_existing_instance(port)
else:
self.instance = InstanceManager.get_instance()

# Parse XML file
with open(self.xml_file, 'r') as f:
xml_text = f.read()
xml = xml_text.replace('$(MISSIONS_DIR)', missions_dir)

xml = f.read()
# Todo: This will fail when using a remote instance manager.
xml = xml.replace('$(MISSIONS_DIR)', missions_dir)
xml = xml.replace('$(ENV_NAME)', self.spec.id)
# Bootstrap the environment if it hasn't been.
role = 0

Expand All @@ -174,30 +221,7 @@ def init(self, observation_space, action_space, port=None):
else:
self.exp_uid = exp_uid

self.action_space = action_space
self.observation_space = observation_space

def map_space(space):
if isinstance(space, gym.spaces.Discrete) or isinstance(space, minerl.env.spaces.Enum):
return 0
elif isinstance(space, gym.spaces.Box):
return np.zeros(shape=space.shape, dtype=space.dtype)
else:
try:
return space.default()
except NameError:
raise ValueError(
'Specify non-None default_action in gym.register or extend all action spaces with default() method')
if self._default_action is None:
self._default_action = {key: map_space(
space) for key, space in action_space.spaces.items()}

def noop_func(a):
return deepcopy(self._default_action)

boundmethd = _bind(self.action_space, noop_func)
self.action_space.noop = boundmethd


# Force single agent
self.agent_count = 1
turn_based = self.xml.find(
Expand All @@ -206,10 +230,6 @@ def noop_func(a):
raise NotImplementedError(
"Turn based or multi-agent environments not supported.")

self.done = True

self.resets = episode

e = etree.fromstring("""<MissionInit xmlns="http://ProjectMalmo.microsoft.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
SchemaVersion="" PlatformVersion=""" + '\"' + malmo_version + '\"' +
Expand Down Expand Up @@ -436,7 +456,8 @@ def _clean_connection(self):
"Connection with Minecraft client cleaned more than once; restarting.")
if self.instance:
self.instance.kill()
self.instance = InstanceManager.get_instance()
self.instance = self._get_new_instance()

self.had_to_clean = False
else:
self.had_to_clean = True
Expand Down Expand Up @@ -535,7 +556,31 @@ def step(self, action):
def _renderObs(self, obs):
if self.viewer is None:
from gym.envs.classic_control import rendering
self.viewer = rendering.SimpleImageViewer()
import pyglet
class ScaledWindowImageViewer(rendering.SimpleImageViewer):
def __init__(self, width, height):
super().__init__(None, 640)

if width > self.maxwidth:
scale = self.maxwidth / width
width = int(scale * width)
height = int(scale * height)
self.window = pyglet.window.Window(width=width, height=height,
display=self.display, vsync=False, resizable=True)
self.width = width
self.height = height
self.isopen = True

@self.window.event
def on_resize(width, height):
self.width = width
self.height = height

@self.window.event
def on_close():
self.isopen = False

self.viewer = ScaledWindowImageViewer(self.width*4, self.height*4)
self.viewer.imshow(obs)
return self.viewer.isopen

Expand All @@ -544,6 +589,9 @@ def render(self, mode='human'):
self._renderObs(self._last_pov)
return self._last_pov

def is_closed(self):
return self._already_closed

def close(self):
"""gym api close"""
if self.viewer is not None:
Expand Down
Loading

0 comments on commit a18fb4c

Please sign in to comment.