Repository: openai/universe
Branch: master
Commit: cc9ce6ec2418
Files: 136
Total size: 1.1 MB
Directory structure:
gitextract_4uz9ods7/
├── .dockerignore
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.rst
├── Dockerfile
├── ISSUE_TEMPLATE
├── LICENSE
├── Makefile
├── README.rst
├── doc/
│ ├── env_semantics.rst
│ ├── protocols.rst
│ └── remotes.rst
├── example/
│ ├── diagnostic-agent/
│ │ └── diagnostic-agent.py
│ ├── random-agent/
│ │ └── random-agent.py
│ ├── recorders/
│ │ ├── botaction_recorder.py
│ │ ├── reward_recorder.py
│ │ └── vnc_recorder.py
│ ├── starter-cluster/
│ │ ├── starter-cluster
│ │ ├── starter-cluster-cf.json
│ │ └── starter-cluster-requirements.txt
│ └── system-diagnostics/
│ └── system_diagnostics_logger.py
├── setup.py
├── test.dockerfile
├── tests/
│ └── functional/
│ ├── test_core_envs_semantics.py
│ └── test_envs.py
├── tox.ini
└── universe/
├── __init__.py
├── configuration.py
├── envs/
│ ├── __init__.py
│ ├── diagnostics.py
│ ├── dummy_vnc_env.py
│ ├── tests/
│ │ ├── __init__.py
│ │ └── test_semantics.py
│ ├── vnc_core_env/
│ │ ├── __init__.py
│ │ ├── key.py
│ │ ├── translator.py
│ │ └── vnc_core_env.py
│ ├── vnc_env.py
│ ├── vnc_flashgames.py
│ ├── vnc_gtav.py
│ ├── vnc_internet.py
│ ├── vnc_starcraft.py
│ └── vnc_wog.py
├── error.py
├── kube/
│ ├── __init__.py
│ └── discovery.py
├── pyprofile/
│ └── __init__.py
├── remotes/
│ ├── __init__.py
│ ├── allocator_remote.py
│ ├── build.py
│ ├── compose/
│ │ ├── __init__.py
│ │ ├── colors.py
│ │ ├── container.py
│ │ ├── log_printer.py
│ │ ├── progress_stream.py
│ │ ├── signals.py
│ │ └── utils.py
│ ├── docker_remote.py
│ ├── hardcoded_addresses.py
│ ├── healthcheck.py
│ └── remote.py
├── rewarder/
│ ├── __init__.py
│ ├── connection_timer.py
│ ├── env_status.py
│ ├── merge.py
│ ├── remote.py
│ ├── reward_buffer.py
│ ├── reward_proxy_server.py
│ ├── rewarder_client.py
│ ├── rewarder_session.py
│ └── tests/
│ └── test_reward_buffer.py
├── runtimes/
│ ├── .agignore
│ ├── __init__.py
│ ├── flashgames.json
│ └── registration.py
├── runtimes.yml
├── scoreboard/
│ └── __init__.py
├── spaces/
│ ├── __init__.py
│ ├── diagnostics.py
│ ├── hardcoded.py
│ ├── joystick_action_space.py
│ ├── joystick_event.py
│ ├── vnc_action_space.py
│ ├── vnc_event.py
│ └── vnc_observation_space.py
├── twisty.py
├── utils/
│ ├── __init__.py
│ └── display.py
├── vectorized/
│ ├── __init__.py
│ ├── core.py
│ ├── multiprocessing_env.py
│ ├── tests/
│ │ └── test_monitoring.py
│ └── vectorize_filter.py
├── vncdriver/
│ ├── README.md
│ ├── __init__.py
│ ├── auth.py
│ ├── constants.py
│ ├── dual_proxy_server.py
│ ├── error.py
│ ├── fbs_reader.py
│ ├── fbs_writer.py
│ ├── libvnc_session.py
│ ├── screen/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── numpy_screen.py
│ │ ├── pyglet_screen.py
│ │ └── screen_buffer.py
│ ├── server_messages.py
│ ├── vendor/
│ │ ├── __init__.py
│ │ └── pydes.py
│ ├── vnc_client.py
│ ├── vnc_proxy_server.py
│ └── vnc_session.py
└── wrappers/
├── __init__.py
├── action_space.py
├── blocking_reset.py
├── diagnostics.py
├── experimental/
│ ├── __init__.py
│ ├── action_space.py
│ ├── observation.py
│ └── random_env.py
├── gym_core.py
├── gym_core_sync.py
├── joint.py
├── logger.py
├── monitoring.py
├── multiprocessing_env.py
├── recording.py
├── render.py
├── tests/
│ ├── test_joint.py
│ └── test_time_limit.py
├── throttle.py
├── time_limit.py
├── timer.py
├── vectorize.py
└── vision.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
vncdriver/logs
.git
*.pyc
================================================
FILE: .gitignore
================================================
.DS_Store
.ropeproject
*.pyc
tags
*.swo
*.swp
*.sqlite
upload/
venv
dist
*.egg-info
.idea
/logs
/dist
build/
/.gitfiles
/.tox
/.cache
================================================
FILE: .travis.yml
================================================
language: python
python:
- "3.5"
services:
- docker
before_install:
- docker build -f test.dockerfile -t quay.io/openai/universe:test .
script:
- docker run -v /usr/bin/docker:/usr/bin/docker -v /root/.docker:/root/.docker -v /var/run/docker.sock:/var/run/docker.sock --net=host quay.io/openai/universe:test
notifications:
slack:
secure: HtkwTGU+cQbpQuRaMuC2ZcuaaJfUBEZxSaChkj74lFulHAc6g/Xj1ztzj/roR/kMl3dycYPl5QL5AkxPPD/x8BweOJmgabe9boPbU9+80tpa0ueZnt0q6vX23ZA7EcqIAOwQqHiaklxoCkSflpV2N9GP20yBf5YNneHWsbFc8RDuJmNsg8s+1sZIrT3aOcvAJmu8WrNVclKvnpH/qCtvkK6npXZvdMvGpQPT/uCYOyPcbURqelk7qzNpT0oJmkrutbkT3Hp03NRDEQgS47pTPMC5pklea5zDkyh++ETEMpXU75UgN3CURKhuf/oyq7JorG/lXQaz6HBYbcT9EhPVpTzPZEczk50VAp3RWWcN6NczJJ9rVL0h+bGZmcOlJz9igNl838ziL6nxMFO9W3psXQUoBvEDo+vXPDEOUxeBrtLqUN1vfQmMw7KKiGIimInWigW19WfVQhSt47+xKKmbvBKtQ/w8lCDlwO5h7QbApv6TiaGzxtzdJMAyhNOZE7KxqvtFCJgKL4ZfmVzziLlbdbr582Cc0wxvGLDC341+CqkYVv83oimM8Ks3wHRT/ABoO1uXOSsZniUU/+oU/mzyrhrkGNNSDCwdJ0mVEWRGTYZs26IcBIeYGsLJrv3J9ZgfiyD2Knl4/yVI0IbTs7qAzhBzsXvt9aH7kH7tXYZH9QQ=
webhooks:
urls:
- https://hooks.zapier.com/hooks/catch/1711022/6ztmzh/
- https://hooks.zapier.com/hooks/catch/1711022/6zhc8p/
on_success: always
on_failure: always
after_success:
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
- echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH"
- docker login quay.io -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
- docker tag quay.io/openai/universe:test quay.io/openai/universe-travis:passed-ci
- if [ "$BRANCH" == "master" ]; then ( while true; do echo '.'; sleep 60; done ) & docker push quay.io/openai/universe-travis:passed-ci; fi # This repo is used by universe-envs to run integration test. We echo in order to keep travis alive during a slow push
================================================
FILE: CODE_OF_CONDUCT.rst
================================================
OpenAI is dedicated to providing a harassment-free experience for
everyone, regardless of gender, gender identity and expression, sexual
orientation, disability, physical appearance, body size, age, race, or
religion. We do not tolerate harassment of participants in any form.
This code of conduct applies to all OpenAI spaces both online and
off. Anyone who violates this code of conduct may be sanctioned or
expelled from these spaces at the discretion of the OpenAI team.
We may add additional rules over time, which will be made clearly
available to participants. Participants are responsible for knowing
and abiding by these rules.
================================================
FILE: Dockerfile
================================================
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y libav-tools \
python3-numpy \
python3-scipy \
python3-setuptools \
python3-pip \
libpq-dev \
libjpeg-dev \
curl \
cmake \
swig \
python3-opengl \
libboost-all-dev \
libsdl2-dev \
wget \
unzip \
git \
golang \
net-tools \
iptables \
libvncserver-dev \
software-properties-common \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN ln -sf /usr/bin/pip3 /usr/local/bin/pip \
&& ln -sf /usr/bin/python3 /usr/local/bin/python \
&& pip install -U pip
# Install gym
RUN pip install gym[all]
# Get the faster VNC driver
RUN pip install go-vncdriver>=0.4.0
# Install pytest (for running test cases)
RUN pip install pytest
# Force the container to use the go vnc driver
ENV UNIVERSE_VNCDRIVER='go'
WORKDIR /usr/local/universe/
# Cachebusting
COPY ./setup.py ./
COPY ./tox.ini ./
RUN pip install -e .
# Upload our actual code
COPY . ./
# Just in case any python cache files were carried over from the source directory, remove them
RUN py3clean .
================================================
FILE: ISSUE_TEMPLATE
================================================
(First, please check https://github.com/openai/universe/wiki/Solutions-to-common-problems for solutions to many common problems)
### Expected behavior
### Actual behavior
### Versions
Please include the result of running
```
$ uname -a ; python --version; pip show universe gym tensorflow numpy go-vncdriver Pillow
```
================================================
FILE: LICENSE
================================================
The MIT License
Copyright (c) 2016 OpenAI (http://openai.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Makefile
================================================
upload:
rm -rf dist
python setup.py sdist
twine upload dist/*
test:
find . -name '*.pyc' -delete
docker build -f test.dockerfile -t quay.io/openai/universe:test .
docker run -v /usr/bin/docker:/usr/bin/docker -v /root/.docker:/root/.docker -v /var/run/docker.sock:/var/run/docker.sock --net=host quay.io/openai/universe:test
build:
find . -name '*.pyc' -delete
docker build -t quay.io/openai/universe .
docker build -f test.dockerfile -t quay.io/openai/universe:test .
push:
find . -name '*.pyc' -delete
docker build -t quay.io/openai/universe .
docker build -f test.dockerfile -t quay.io/openai/universe:test .
docker push quay.io/openai/universe
docker push quay.io/openai/universe:test
test-push:
docker build -f test.dockerfile -t quay.io/openai/universe:test .
docker push quay.io/openai/universe:test
================================================
FILE: README.rst
================================================
**This repository has been deprecated in favor of the Retro (https://github.com/openai/retro) library. See our Retro Contest (https://blog.openai.com/retro-contest) blog post for detalis.**
universe
***************
`Universe `_ is a software
platform for measuring and training an AI's general intelligence
across the world's supply of games, websites and other
applications. This is the ``universe`` open-source library, which
provides a simple `Gym `__
interface to each Universe environment.
Universe allows anyone to train and evaluate AI agents on an extremely
wide range of real-time, complex environments.
Universe makes it possible for any existing program to become an
OpenAI Gym environment, without needing special access to the
program's internals, source code, or APIs. It does this by packaging
the program into a Docker container, and presenting the AI with the
same interface a human uses: sending keyboard and mouse events, and
receiving screen pixels. Our initial release contains over 1,000
environments in which an AI agent can take actions and gather
observations.
Additionally, some environments include a reward signal sent to the
agent, to guide reinforcement learning. We've included a few hundred
environments with reward signals. These environments also include
automated start menu clickthroughs, allowing your agent to skip to the
interesting part of the environment.
We'd like the community's `help `_
to grow the number of available environments, including integrating
increasingly large and complex games.
The following classes of tasks are packaged inside of
publicly-available Docker containers, and can be run today with no
work on your part:
- Atari and CartPole environments over VNC: ``gym-core.Pong-v3``, ``gym-core.CartPole-v0``, etc.
- Flashgames over VNC: ``flashgames.DuskDrive-v0``, etc.
- Browser tasks ("World of Bits") over VNC: ``wob.mini.TicTacToe-v0``, etc.
We've scoped out integrations for many other games, including
completing a high-quality GTA V integration (thanks to `Craig Quiter `_ and NVIDIA), but these aren't included in today's release.
.. contents:: **Contents of this document**
:depth: 2
Getting started
===============
Installation
------------
Supported systems
~~~~~~~~~~~~~~~~~
We currently support Linux and OSX running Python 2.7 or 3.5.
We recommend setting up a `conda environment `__
before getting started, to keep all your Universe-related packages in the same place.
Install Universe
~~~~~~~~~~~~~~~~
To get started, first install ``universe``:
.. code:: shell
git clone https://github.com/openai/universe.git
cd universe
pip install -e .
If this errors out, you may be missing some required packages. Here's
the list of required packages we know about so far (please let us know
if you had to install any others).
On Ubuntu 16.04:
.. code:: shell
pip install numpy
sudo apt-get install golang libjpeg-turbo8-dev make
On Ubuntu 14.04:
.. code:: shell
sudo add-apt-repository ppa:ubuntu-lxc/lxd-stable # for newer golang
sudo apt-get update
sudo apt-get install golang libjpeg-turbo8-dev make
On OSX:
You might need to install Command Line Tools by running:
.. code:: shell
xcode-select --install
Or ``numpy``, ``libjpeg-turbo`` and ``incremental`` packages:
.. code:: shell
pip install numpy incremental
brew install golang libjpeg-turbo
Install Docker
~~~~~~~~~~~~~~
The majority of the environments in Universe run inside Docker
containers, so you will need to `install Docker
`__ (on OSX, we
recommend `Docker for Mac
`__). You should be able to
run ``docker ps`` and get something like this:
.. code:: shell
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Alternate configuration - running the agent in docker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The above instructions result in an agent that runs as a regular python process in your OS, and launches docker containers as needed for the remotes.
Alternatively, you can build a docker image for the agent and run it as a container as well.
You can do this in any operating system that has a recent version of docker installed, and the git client.
To get started, clone the ``universe`` repo:
.. code:: shell
git clone https://github.com/openai/universe.git
cd universe
Build a docker image, tag it as 'universe':
.. code:: shell
docker build -t universe .
This may take a while the first time, as the docker image layers are pulled from docker hub.
Once the image is built, you can do a quick run of the test cases to make sure everything is working:
.. code:: shell
docker run --privileged --rm -e DOCKER_NET_HOST=172.17.0.1 -v /var/run/docker.sock:/var/run/docker.sock universe pytest
Here's a breakdown of that command:
* ``docker run`` - launch a docker container
* ``--rm`` - delete the container once the launched process finishes
* ``-e DOCKER_NET_HOST=172.17.0.1`` - tells the universe remote (when launched) to make its VNC connection back to this docker-allocated IP
* ``-v /var/run/docker.sock:/var/run/docker.sock`` - makes the docker unix socket from the host available to the container. This is a common technique used to allow containers to launch other containers alongside itself.
* ``universe`` - use the imaged named 'universe' built above
* ``pytest`` - run 'pytest' in the container, which runs all the tests
At this point, you'll see a bunch of tests run and hopefully all pass.
To do some actual development work, you probably want to do another volume map from the universe repo on your host into the container, then shell in interactively:
.. code:: shell
docker run --privileged --rm -it -e DOCKER_NET_HOST=172.17.0.1 -v /var/run/docker.sock:/var/run/docker.sock -v (full path to cloned repo above):/usr/local/universe universe python
As you edit the files in your cloned git repo, they will be changed in your docker container and you'll be able to run them in python.
Note if you are using docker for Windows, you'll need to enable the relevant shared drive for this to work.
Notes on installation
~~~~~~~~~~~~~~~~~~~~~
* When installing ``universe``, you may see ``warning`` messages. These lines occur when installing numpy and are normal.
* You'll need a ``go version`` of at least 1.5. Ubuntu 14.04 has an older Go, so you'll need to `upgrade `_ your Go installation.
* We run Python 3.5 internally, so the Python 3.5 variants will be much more thoroughly performance tested. Please let us know if you see any issues on 2.7.
* While we don't officially support Windows, we expect our code to be very close to working there. We'd be happy to take pull requests that take our Windows compatibility to 100%. In the meantime, the easiest way for Windows users to run universe is to use the alternate configuration described above.
System overview
---------------
A Universe **environment** is similar to any other Gym environment:
the agent submits actions and receives observations using the ``step()``
method.
Internally, a Universe environment consists of two pieces: a **client** and a **remote**:
* The **client** is a `VNCEnv
`_
instance which lives in the same process as the agent. It performs
functions like receiving the agent's actions, proxying them to the
**remote**, queuing up rewards for the agent, and maintaining a
local view of the current episode state.
* The **remote** is the running environment dynamics, usually a
program running inside of a Docker container. It can run anywhere --
locally, on a remote server, or in the cloud. (We have a separate
page describing how to manage `remotes `__.)
* The client and the remote communicate with one another using the
`VNC `__
remote desktop system, as well as over an auxiliary WebSocket
channel for reward, diagnostic, and control messages. (For more
information on client-remote communication, see the separate page on
the `Universe internal communication protocols
`__.)
The code in this repository corresponds to the **client** side of the
Universe environments. Additionally, you can freely access the Docker
images for the **remotes**. We'll release the source repositories for
the remotes in the future, along with tools to enable users to
integrate new environments. Please sign up for our `beta
`_
if you'd like early access.
Run your first agent
--------------------
Now that you've installed the ``universe`` library, you should make
sure it actually works. You can paste the example below into your
``python`` REPL. (You may need to press enter an extra time to make
sure the ``while`` loop is executing.)
.. code:: python
import gym
import universe # register the universe environments
env = gym.make('flashgames.DuskDrive-v0')
env.configure(remotes=1) # automatically creates a local docker container
observation_n = env.reset()
while True:
action_n = [[('KeyEvent', 'ArrowUp', True)] for ob in observation_n] # your agent here
observation_n, reward_n, done_n, info = env.step(action_n)
env.render()
The example will instantiate a client in your Python process,
automatically pull the ``quay.io/openai/universe.flashgames`` image,
and will start that image as the remote. (In our `remotes
`__ documentation page, we explain other ways you can run
remotes.)
It will take a few minutes for the image to pull the first time. After that,
if all goes well, a window like the one below will soon pop up. Your
agent, which is just pressing the up arrow repeatedly, is now
playing a Flash racing game called `Dusk Drive
`__. Your agent
is programmatically controlling a VNC client, connected to a VNC
server running inside of a Docker container in the cloud, rendering a
headless Chrome with Flash enabled:
.. image:: https://github.com/openai/universe/blob/master/doc/dusk-drive.png?raw=true
:width: 600px
You can even connect your own VNC client to the environment, either
just to observe or to interfere with your agent. Our ``flashgames``
and ``gym-core`` images conveniently bundle a browser-based VNC
client, which can be accessed at
``http://localhost:15900/viewer/?password=openai``. If you're on Mac,
connecting to a VNC server is as easy as running: ``open
vnc://localhost:5900``.
(If using docker-machine, you'll need to replace "localhost" with the
IP address of your Docker daemon, and use ``openai`` as the password.)
Breaking down the example
~~~~~~~~~~~~~~~~~~~~~~~~~
So we managed to run an agent, what did all the code actually
mean? We'll go line-by-line through the example.
* First, we import the `gym `__ library,
which is the base on which Universe is built. We also import
``universe``, which `registers
`__
all the Universe environments.
.. code:: python
import gym
import universe # register the universe environments
* Next, we create the environment instance. Behind the scenes, ``gym``
looks up the `registration
`__
for ``flashgames.DuskDrive-v0``, and instantiates a `VNCEnv
`__
object which has been `wrapped
`__
to add a few useful diagnostics and utilities. The ``VNCEnv`` object
is the *client* part of the environment, and it is not yet connected
to a *remote*.
.. code:: python
env = gym.make('flashgames.DuskDrive-v0')
* The call to ``configure()`` connects the client to a remote
environment server. When called with ``configure(remotes=1)``,
Universe will automatically create a Docker image running locally on
your computer. The local client connects to the remote using VNC.
(More information on client-remote communication can be found in the
page on `universe internal communication protocols
`__. More on configuring remotes is at `remotes `__.)
.. code:: python
env.configure(remotes=1)
* When starting a new environment, you call ``env.reset()``. Universe
environments run in real-time, rather than stepping synchronously
with the agent's actions, so ``reset`` is asynchronous and returns
immediately. Since the environment will not have waited to finish
connecting to the VNC server before returning, the initial observations
from ``reset`` will be ``None`` to indicate that there is
not yet a valid observation.
Similarly, the environment keeps running in the background even
if the agent does not call ``env.step()``. This means that an agent
that successfully learns from a Universe environment cannot take
"thinking breaks": it must keep sending actions to the environment at all times.
Additionally, Universe introduces the *vectorized* Gym
API. Rather than controlling a single environment at a time, the agent
can control a fixed-size vector of ``n`` environments, each with its
own remote. The return value from ``reset`` is therefore a *vector*
of observations. For more information, see the separate page on
`environment semantics `__)
.. code:: python
observation_n = env.reset()
* At each ``step()`` call, the agent submits a vector of actions; one for
each environment instance it is controlling. Each VNC action is a
list of events; above, each action is the single event "press the
``ArrowUp`` key". The agent could press and release the key in one
action by instead submitting ``[('KeyEvent', 'ArrowUp', True),
('KeyEvent', 'ArrowUp', False)]`` for each observation.
In fact, the agent could largely have the same effect by just
submitting ``('KeyEvent', 'ArrowUp', True)`` once and then calling
``env.step([[] for ob in observation_n])`` thereafter, without ever
releasing the key using ``('KeyEvent', 'ArrowUp', False)``. The
browser running inside the remote would continue to statefully
represent the arrow key as being pressed. Sending other unrelated
keypresses would not disrupt the up arrow keypress; only explicitly
releasing the key would cancel it. There's one slight subtlety:
when the episode resets, the browser will reset, and will forget
about the keypress; you'd need to submit a new ``ArrowUp`` at the
start of each episode.
.. code:: python
action_n = [[('KeyEvent', 'ArrowUp', True)] for ob in observation_n]
* After we submit the action to the environment and render one frame,
``step()`` returns a list of *observations*, a list of *rewards*, a
list of *"done" booleans* indicating whether the episode has ended,
and then finally an *info dictionary* of the form ``{'n': [{},
...]}``, in which you can access the info for environment ``i`` as
``info['n'][i]``.
Each environment's ``info`` message contains useful diagnostic
information, including latency data, client and remote timings,
VNC update counts, and reward message counts.
.. code:: python
observation_n, reward_n, done_n, info = env.step(action_n)
env.render()
* We call ``step`` in what looks like a busy loop. In reality, there
is a `Throttle
`__
wrapper on the client which defaults to a target frame rate of 60fps, or one
frame every 16.7ms. If you call it more frequently than that,
``step`` will `sleep
`__
with any leftover time.
Testing
=======
We are using `pytest `__ for tests. You can run them via:
.. code:: shell
pytest
Run ``pytest --help`` for useful options, such as ``pytest -s`` (disables output capture) or ``pytest -k `` (runs only specific tests).
Additional documentation
========================
More documentation not covered in this README can be found in the
`doc folder `__ of this repository.
Getting help
============
If you encounter a problem that is not addressed in this README page
or in the `extra docs `__, then try our wiki page of `solutions
to common problems
`__ -
and add to it if your solution isn't there!
You can also search through the `issues
`__
on this repository and our `discussion board
`__ to see if another user has posted
about the same problem or to ask for help from the community.
If you still can't solve your problem after trying all of the above
steps, please post an issue on this repository.
What's next?
============
* Get started training RL algorithms! You can try out the `Universe Starter Agent `_, an implementation of the `A3C algorithm `_ that can solve several VNC environments.
* For more information on how to manage remotes, see the separate documentation page on `remotes `__.
* Sign up for a `beta `_ to get early access to upcoming Universe releases, such as tools to integrate new Universe environments or a dataset of recorded human demonstrations.
Changelog
---------
- 2017-02-08: The old location for wrappers.SafeActionSpace has been moved to wrappers.experimental.SafeActionSpace. SoftmaxClickMouse has also been moved to wrappers.experimental.SoftmaxClickMouse
- 2017-01-08: The wrappers.SafeActionSpace has been moved to wrappers.experimental.SafeActionSpace. The old location will remain with a deprecation warning until 2017-02-08.
- 2016-12-27: BACKWARDS INCOMPATIBILITY: The gym monitor is now a
wrapper. Rather than starting monitoring as
`env.monitor.start(directory)`, envs are now wrapped as follows:
`env = wrappers.Monitor(env, directory)`. This change is on master
and will be released with 0.21.0.
================================================
FILE: doc/env_semantics.rst
================================================
Environment semantics
*********************
Real-time environments
======================
Universe environments differ from other Gym environments in that the
environment keeps running in real-time, even when the agent does not
call ``step``. This has a few important implications:
* Actions and observations can no longer be considered to
occur on a "clock tick".
* An explicit call to ``reset`` is asynchronous and returns
immediately, even though the environment has not yet finished
resetting. (If you would prefer the ``reset`` call to block
until the reset has finished, you can wrap
the client-side environment with a `BlockingReset `__ wrapper)
* Since the environment will not have waited to finish
connecting to the VNC server before returning, the initial return
values from ``reset`` will be ``None`` to indicate that there is
not yet a valid observation.
* An agent that successfully learns from a Universe environment cannot
take "thinking breaks": it must keep sending actions to the
environment at all times.
* Lag and latency play a major role in your agent's ability to
successfully learn in a given environment. The latency and profiling
numbers returned in the ``info`` dictionary can provide important
information for training.
Vectorized API
==============
The vectorized Gym API allows a single client-side environment to
control a vector of remotes. The main difference with the
non-vectorized Gym API is that individual environments will
automatically reset upon reaching the end of an episode. (An episode
is defined as ending when an agent has concretely succeeded or failed
at the task, such as after clearing a level of a game, or losing the
game. Some environments without clearly delineated success and
failure conditions may not have episodes.)
There are two API methods, ``reset`` and ``step``. The semantics are:
- ``reset`` takes no arguments and returns a vector of observations:
.. code:: python
observation_n = env.reset()
- ``step`` consumes a vector of actions, and returns a vector of
observations, vector of rewards, vector of done booleans, and an
info dictionary. The info dictionary has an ``n`` key, which
contains a vector of infos specific to each env:
.. code:: python
observation_n, reward_n, done_n, info = env.step(action_n)
# len(info['n']) == len(observation_n)
Some important notes:
- At any given moment, some of the environments may be
resetting. Resetting environments will have a ``None`` value for
their observation. For example, an ``observation_n`` of ``[None,
{'vision': ...}, {'vision': ...}]`` indicates that the environment
at index 0 is resetting.
- When an index returns ``done=True``, the corresponding environment
will automatically start resetting.
- The user must call ``reset`` once before calling ``step``; undefined
behavior will result if ``reset`` is not called. Further ``reset``
calls are allowed, but generally are used only if the environment has
been idle for a while (such as with periodic evaluation), or when it
is important to start at the beginning
Versioning
==========
The remote is versioned and has fixed semantics, assuming sufficient
compute resources are applied (i.e. if you don't have enough CPU, your
flash environments will likely behave differently). The client's exact
semantics will depend on the version of universe you have installed,
and you should track the version of that together with the rest of
your agent code.
================================================
FILE: doc/protocols.rst
================================================
Universe internal communication protocols
*****************************************
Network architecture
====================
A Universe environment consists of two components that run in
separate processes and communicate over the network. The agent's
machine runs the environment **client** code, which connects
to the **remote** environment server.
Many related environments can be served from the same **runtime**,
usually packaged as a Docker container. For example, all the flash
games in Universe are served from the ``flashgames`` runtime, which
consists of the ``quay.io/openai/universe.flashgames`` Docker image
and runs with its corresponding set of configuration flags.
Each remote exposes two ports:
- A VNC port (5900 by default). The remote runs an off-the-shelf VNC
server (usually TigerVNC), so that users can connect their own
VNC viewers to the environments for interactive use. VNC delivers
pixel observations to the agent, and the agent submits keyboard and
mouse inputs over VNC as well.
- A rewarder port (15900 by default). The rewarder protocol is a
bi-directional JSON protocol runs over WebSockets. The rewarder
channel provides more than just a reward signal; in addition, it allows the
agent to submit control commands (such as to indicating which of
the available environments should be active for a given runtime) and
to receive structured information from the environment (such as latencies
and performance timings).
VNC system and Remote Frame Buffer protocol
===========================================
Keyboard and mouse actions and pixel observations are sent between the
client and the remote using the `VNC
`__
system. VNC is a pervasive standard for remote desktop operation. Many
implementations of VNC are available online, including VNC viewers
that make it easy to observe a running agent.
To achieve the performance we needed in order to train an
agent on dozens of simultaneous remote environments at 60FPS, we wrote a
`custom client-side VNC driver `__
in go. The remote VNC server that we use in most of our runtimes is `TigerVNC `__
More information about the Remote Frame Buffer protocol can be found
in the official `IETF RFC `__
spec, and in other tutorials elsewhere on the internet.
Rewarder protocol
=================
The Rewarder protocol is a Universe-specific bi-directional JSON
protocol run over WebSockets. In addition to the actions and
observations provided by the VNC connection, the rewarder connection
allows the agent to submit control commands to the environment, and to
receive rewards and other informational messages. This section details
the format of the Rewarder protocol.
Message format
--------------
Each message is serialized as a JSON object with the following
structure:
.. code::
{
"method": [string],
"headers": [object],
"body": [object]
}
For example, a ``v0.env.describe`` message might look as follows:
.. code::
{
"method": "v0.env.describe",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"episode_id": "1.2",
},
"body": {
"env_id": "internet.SlitherIO-v0",
"env_state": "running",
"fps": 60
}
}
Each message should have a unique ``message_id`` header and a ``sent_at``
header (which should be the current UNIX timestamp with at least
millisecond precision).
Server to client messages
-------------------------
env.describe
~~~~~~~~~~~~
.. code::
{
"method": "v0.env.describe",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"episode_id": "1.2",
},
"body": {
"env_id": "internet.SlitherIO-v0",
"env_state": "running",
}
}
env.reward
~~~~~~~~~~
.. code::
{
"method": "v0.env.reward",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"episode_id": "1.2",
},
"body": {
"reward": -3,
"done": False,
"info": {},
}
}
env.text
~~~~~~~~
.. code::
{
"method": "v0.env.text",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"episode_id": "1.2",
},
"body": {
"text": "this is some text"
}
}
env.observation
~~~~~~~~~~~~~~~
.. code::
{
"method": "v0.env.observation",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"episode_id": "1.2"
},
"body": {
"observation": [0.12, 0.51, 2, 12]
}
}
connection.close
~~~~~~~~~~~~~~~~
.. code::
{
"method": "v0.connection.close",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15
},
"body": {
"message": "Disconnected since time limit reached"
}
}
reply.error
~~~~~~~~~~~
.. code::
{
"method": "v0.reply.error",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"parent_message_id": "26"
},
"body": {
"message": "No such environment: llama"
}
}
reply.env.reset
~~~~~~~~~~~~~~~
.. code::
{
"method": "v0.reply.env.reset",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"parent_message_id": "26",
"episode_id": "1.2"
},
"body": {}
}
reply.control.ping
~~~~~~~~~~~~~~~~~~
.. code::
{
"method": "v0.reply.control.ping",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15,
"parent_message_id": "26"
},
"body": {}
}
Client to server messages
-------------------------
agent.action
~~~~~~~~~~~~
.. code::
{
"method": "v0.agent.action",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15
},
"body": {
"action: [["JoystickAxisXEvent", 0.1],
["JoystickAxisZEvent", 0.1]]
}
}
env.reset
~~~~~~~~~
.. code::
{
"method": "v0.env.reset",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15
},
"body": {
"env_id': "flashgames.DuskDrive-v0"
}
}
control.ping
~~~~~~~~~~~~
.. code::
{
"method": "v0.control.ping",
"headers": {
"sent_at": 1479493678.1937322617,
"message_id": 15
},
"body": {}
}
================================================
FILE: doc/remotes.rst
================================================
Remotes
*******
Since the remote part of the environment runs in its own server
process, managing remotes is an important task. The remote can run
anywhere - locally, or in the cloud. This section will explain
three ways to set up remotes.
.. contents:: **Contents of this document**
:depth: 2
Docker installation
===================
The majority of the remotes for Universe environments run inside
Docker containers, so the first step to running your own remotes is
to `install Docker `__ (on
OSX, we recommend `Docker for Mac
`__). You should be able to
run ``docker ps`` and get something like this:
.. code:: shell
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
How to start a remote
=====================
There are currently three ways to start a remote:
- Create an **automatic local remote** using ``env.configure(remotes=1)``.
In this case, ``universe`` automatically creates a remote locally by spinning
up a docker container for you.
- Create a **manual remote** by spinning up your own Docker container,
locally or on a server you control.
- Create a **starter cluster** in AWS, which will automatically provide you
with cloud-hosted remotes.
Automatic local remotes
-----------------------
To create an **automatic local remote**, call
``env.configure(remotes=1)`` (or ``4`` if you'd like 4 remotes).
This will download the ``quay.io/openai/universe.flashgames`` docker
container and start 1 copy of it locally.
.. code:: python
import gym
import universe # register the universe environments
env = gym.make('flashgames.DuskDrive-v0')
env.configure(remotes=1) # downloads and starts a flashgames runtime
observation_n = env.reset()
while True:
action_n = [[('KeyEvent', 'ArrowUp', True)] for ob in observation_n] # your agent here
observation_n, reward_n, done_n, info = env.step(action_n)
env.render()
Agents inside Docker
~~~~~~~~~~~~~~~~~~~~
If you're running your agent inside a Docker container, it can still create automatic remotes by connecting
to the docker daemon on the host. To do this, mount the docker binary and socket inside the agent container like this:
.. code:: shell
$ docker run --privileged \
-v /usr/bin/docker:/usr/bin/docker \
-v /root/.docker:/root/.docker \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DOCKER_NET_HOST=172.17.0.1 \
... \
my/agent:latest
The Universe remote will use ``$DOCKER_NET_HOST`` when connecting to the VNC and rewarder ports.
Manual remotes
--------------
To create a **manual remote**, start the remote Docker container
manually on the command line. Remotes can run locally on the same machine as
the client, or you can start them on servers you control.
To find the appropriate Docker command-line invocation for each
environment, you can look at where we `register
`__
the runtime for each environment. The command is also printed out
conveniently when you run with ``remotes=1``:
.. code:: shell
[2016-11-25 23:51:04,223] [0] Creating container:
image=quay.io/openai/universe.flashgames:0.19.19. Run the same thing by hand as:
docker run -p 10000:5900 -p 10001:15900 --cap-add NET_ADMIN --cap-add SYS_ADMIN
--ipc host quay.io/openai/universe.flashgames:0.19.19
Once you have started the docker container, configure your agent to
connect to the VNC server (port 5900 by default) and the reward/info channel
(port 15900 by default):
.. code:: python
env.configure(remotes='vnc://localhost:5900+15900')
To connect manually to multiple remotes, separate them by commas:
.. code:: python
env.configure(remotes='vnc://localhost:5900+15900,vnc://localhost:5901+15901')
If your docker container is running on a server rather than on localhost,
just plug in the appropriate URL or IP address:
.. code:: python
env.configure(remotes='vnc://your.host.here:5900+15900')
VNC compression settings
-----------------------------------------------
The VNC connection supports multiple compression settings that control the tradeoff
between a fast but highly compressed, low quality data stream and slow, uncompressed
data stream. These can be configured by using the ``vnc_kwargs`` argument to
``env.configure``. The default arguments are:
.. code:: python
env.configure(vnc_kwargs={'encoding':'tight', 'fine_quality_level':50, 'subsample_level':2})
Here, ``tight`` is a lossy encoding that uses JPEG for compression. We also support ``zrle`` instead, which is lossless.
The ``fine_quality_level`` controls the compression strength from high compression / low quality (0) to low compression / high quality (100).
For ``subsample_level``, 0 is highest quality, 2 is low quality and 3 is greyscale. You can investigate the effects
of many of these options on the visual fidelity by connecting to an environment using TurboVNC, which allows you to
tune these settings in the user interface.
Note that the codecs always operate on deltas of the screen, so if large portions of your screen are not changing then
you might be able to afford higher quality settings. Conversely, if you're playing a racing game that takes up a large
portion of the screen you should be more worried about bandwidth. The call to ``step`` is asynchronous with respect to
new frames arriving, so if the connection is too slow the environments will lag.
Automatic cloud-hosted remotes: starter cluster
-----------------------------------------------
If you have an AWS account, you can spin up a **starter Docker cluster** to host your own remotes. First click the "Launch Stack" button and follow the steps on the AWS console to deploy your cluster.
.. image:: https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png
:target: https://console.aws.amazon.com/cloudformation/home#/stacks/new?stackName=OpenAI-Universe&templateURL=thttps://s3-us-west-2.amazonaws.com/openai-public/universe/starter-cluster-cf-0.1.0.json
Once your stack on AWS is ready, run `starter-cluster` to start your environments
.. code:: shell
$ example/starter-cluster/starter-cluster start -s [stack-name] -i [path-to-ssh-key] \
--runtime [universe-runtime] -n [number-of-envs]
or example, the follow will start two flashgames remotes:
.. code:: shell
$ pip install -r bin/starter-cluster-requirements.txt
$ bin/starter-cluster -v start -s OpenAI-Universe -i my-ec2-key.pem -r flashgames -n 2
Creating network "flashgames_default" with the default driver
Pulling flashgames-0 (quay.io/openai/universe.flashgames:0.19.36)...
ip-172-33-1-4: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
ip-172-33-28-242: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
Creating flashgames_flashgames-0_1
Creating flashgames_flashgames-1_1
Environments started.
Remotes:
vnc://54.245.154.123:5013+5015
vnc://54.245.154.123:5006+5008
Now you can pass the IP address and ports for your remotes to your agent,
as was described in the previous section on manual remotes. For example:
.. code:: shell
$ python bin/random_agent.py -e flashgames.DuskDrive-v0 -r vnc://54.245.154.123:5013+5015,54.245.154.123:5006+5008
Running ``bin/starter-cluster start`` again will restart your remotes. To stop them, run:
.. code:: shell
$ bin/starter-cluster stop -s OpenAI-Universe -i my-ec2-key.pem -r flashgames
Stopping flashgames_flashgames-1_1 ... done
Stopping flashgames_flashgames-0_1 ... done
Removing flashgames_flashgames-1_1 ... done
Removing flashgames_flashgames-0_1 ... done
Removing network flashgames_default
Environments stopped.
Region
~~~~~~
By default, starter cluster remotes are spawned in AWS's ``us-west-2``
region. In our experience, the latencies of training over the public
internet are acceptable, but if you have trouble, it may make sense to
try running your agent code on an AWS server in the same region as the
remote.
Scaling Up
~~~~~~~~~~
If you encounter the following
.. code:: shell
$ bin/starter-cluster -v start -s OpenAI-Universe -i my-ec2-key.pem -r flashgames -n 2
Creating network "flashgames_default" with the default driver
Pulling flashgames-0 (quay.io/openai/universe.flashgames:0.19.36)...
ip-172-33-1-4: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
ip-172-33-28-242: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
ip-172-33-9-51: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
ip-172-33-27-141: Pulling quay.io/openai/universe.flashgames:0.19.36... : downloaded
Creating flashgames_flashgames-2_1
Creating flashgames_flashgames-3_1
Creating flashgames_flashgames-0_1
Creating flashgames_flashgames-1_1
Creating flashgames_flashgames-4_1
ERROR: for flashgames-0 no resources available to schedule container
then it means you've run out of computing resources on your cluster, and
have to add more worker nodes. You can do so by going to the AWS
Cloudformation console:
1. Select your stack
2. Click "Update Stack" in the "Actions" dropdown
3. Hit "Next" on the "Select Template" page
4. Input the new swarm size and hit "Next"
5. Hit "Next" on the "Options" page
6. Hit "Update" on the "Review" page
Reusing remotes
===============
If a consistent ``client_id`` is supplied to ``configure()``, then the
client will attempt to reuse the same remote for the new environment
rather than spinning up a new one each time.
Switching between environments in the same *runtime*
(i.e. environments that run on the same underlying docker container)
is possible without creating a new remote; however, if you want to
switch to an environment in a different runtime, you will need to create
a new remote. For example, you can switch between
``flashgames.DuskDrive-v0`` and ``flashgames.NeonRace-v0`` without
starting a new remote, because they both run in the ``flashgames``
runtime, but if you want to switch to ``wob.mini.UseColorwheel2-v0``
you cannot re-use the same remote.
The configuration for the runtimes is defined in
`universe/runtimes/__init__.py `__,
and the specific version number tags for the corresponding Docker
images are specified in
`runtimes.yml `__.
================================================
FILE: example/diagnostic-agent/diagnostic-agent.py
================================================
#!/usr/bin/env python
import argparse
import logging
import time
import gym
import numpy as np
import universe
from universe import pyprofile, wrappers, spaces
from gym import wrappers as gym_wrappers
# if not os.getenv("PYPROFILE_FREQUENCY"):
# pyprofile.profile.print_frequency = 5
from universe import vectorized
logger = logging.getLogger()
CHROME_X_OFFSET = 18
CHROME_Y_OFFSET = 84
class NoopSpace(gym.Space):
""" Null action space """
def sample(self, seed=0):
return []
def contains(self, x):
return x == []
class ForwardSpace(gym.Space):
""" Only move forward action space """
def __init__(self, key='w'):
self.key = [spaces.KeyEvent.by_name(key, down=True)]
def sample(self, seed=0):
return self.key
def contains(self, x):
return x == self.key
# The world's simplest agent!
class RandomAgent(object):
"""
Example usage:
bin/random_agent.py -e gym-core.Pong-v3 --remote localhost:5900+15900
"""
def __init__(self, action_space, n, vectorized):
self.action_space = action_space
self.n = n
self.vectorized = vectorized
def __call__(self, observation, reward, done):
if self.vectorized:
return [self.action_space.sample() for _ in range(self.n)]
else:
return self.action_space.sample()
if __name__ == '__main__':
# You can optionally set up the logger. Also fine to set the level
# to logging.DEBUG or logging.WARN if you want to change the
# amount of output.
logger.setLevel(logging.INFO)
universe.configure_logging()
# Actions this agent will take, 'random' is the default
action_choices = ['random', 'noop', 'forward', 'click']
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-e', '--env_id', default='gym-core.Pong-v3', help='Which environment to run on.')
parser.add_argument('-m', '--monitor', action='store_true', help='Whether to activate the monitor.')
parser.add_argument('-r', '--remote', default=None, help='The number of environments to create (e.g. -r 20), or the address of pre-existing VNC servers and rewarders to use (e.g. -r vnc://localhost:5900+15900,localhost:5901+15901)')
parser.add_argument('-c', '--client-id', default='0', help='Set client id.')
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
parser.add_argument('-R', '--no-render', action='store_true', help='Do not render the environment locally.')
parser.add_argument('-A', '--actions', choices=action_choices, default='random', help='How to sample actions to send to remote environment')
parser.add_argument('-d', '--docker-image', help='Force a version of the docker_image used with --remote . e.g --docker-image quay.io/openai/universe.gym-core:0.3')
parser.add_argument('-s', '--reuse', default=False, action='store_true', help='Reuse existing Docker container if present, and leave this one running after (only for "-r n")')
parser.add_argument('-f', '--fps', default=60., type=float, help='Desired frames per second')
parser.add_argument('-N', '--max-steps', type=int, default=10**7, help='Maximum number of steps to take')
parser.add_argument('-E', '--max-episodes', type=int, default=10**7, help='Maximum number of episodes')
parser.add_argument('-T', '--start-timeout', type=int, default=None, help='Rewarder session connection timeout (seconds)')
args = parser.parse_args()
logging.getLogger('gym').setLevel(logging.NOTSET)
logging.getLogger('universe').setLevel(logging.NOTSET)
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
if args.env_id is not None:
env = gym.make(args.env_id)
else:
env = wrappers.WrappedVNCEnv()
# env = wrappers.BlockingReset(env)
if not isinstance(env, wrappers.GymCoreAction):
# The GymCoreSyncEnv's try to mimic their core counterparts,
# and thus came pre-wrapped wth an action space
# translator. Everything else probably wants a SafeActionSpace
# wrapper to shield them from random-agent clicking around
# everywhere.
env = wrappers.experimental.SafeActionSpace(env)
else:
# Only gym-core are seedable
env.seed([0])
env = wrappers.Logger(env)
if args.monitor:
env = wrappers.Monitor(env, '/tmp/vnc_random_agent', force=True)
if args.actions == 'random':
action_space = env.action_space
elif args.actions == 'noop':
action_space = NoopSpace()
elif args.actions == 'forward':
action_space = ForwardSpace()
elif args.actions == 'click':
spec = universe.runtime_spec('flashgames').server_registry[args.env_id]
height = spec["height"]
width = spec["width"]
noclick_regions = [r['coordinates'] for r in spec['regions'] if r['type'] == 'noclick'] if spec.get('regions') else []
active_region = (CHROME_X_OFFSET, CHROME_Y_OFFSET, CHROME_X_OFFSET + width, CHROME_Y_OFFSET + height)
env = wrappers.SoftmaxClickMouse(env, active_region=active_region, noclick_regions=noclick_regions)
action_space = env.action_space
else:
logger.error("Invalid action choice: {}".format(args.actions))
exit(1)
env.configure(
fps=args.fps,
# print_frequency=None,
# ignore_clock_skew=True,
remotes=args.remote,
client_id=args.client_id,
start_timeout=args.start_timeout,
# remotes=remote, docker_image=args.docker_image, reuse=args.reuse, ignore_clock_skew=True,
# vnc_session_driver='go', vnc_session_kwargs={
# 'compress_level': 0,
# },
vnc_driver='go', vnc_kwargs={
# 'encoding': 'tight', 'compress_level': 0, 'fine_quality_level': 50, 'subsample_level': 2,
'encoding': 'tight', 'compress_level': 0, 'fine_quality_level': 50, 'subsample_level': 0, 'quality_level': 5,
},
)
agent = RandomAgent(action_space, n=env.n, vectorized=env.metadata['runtime.vectorized'])
render = not args.no_render
observation_n = env.reset()
target = time.time()
reward_n = [0] * env.n
done_n = [False] * env.n
observation_count = np.zeros(env.n)
episode_length = np.zeros(env.n)
episode_score = np.zeros(env.n)
episodes_completed = 0
for i in range(args.max_steps):
# print(observation_n)
# user_input.handle_events()
if render:
# Note the first time you call render, it'll be relatively
# slow and you'll have some aggregated rewards. We could
# open the render() window before `reset()`, but that's
# confusing since it pops up a black window for the
# duration of the reset.
env.render()
action_n = agent(observation_n, reward_n, done_n)
# Take an action
with pyprofile.push('env.step'):
observation_n, reward_n, done_n, info = env.step(action_n)
episode_length += 1
if not all(r is None for r in reward_n): # checks if we connected the rewarder
episode_score += np.array(reward_n)
for i, ob in enumerate(observation_n):
if ob is not None and (not isinstance(ob, dict) or ob['vision'] is not None):
observation_count[i] += 1
scores = {}
lengths = {}
observations = {}
for i, done in enumerate(done_n):
if not done:
continue
scores[i] = episode_score[i]
lengths[i] = episode_length[i]
observations[i] = observation_count[i]
episode_score[i] = 0
episode_length[i] = 0
observation_count[i] = 0
if len(scores) > 0:
logger.info('Total for completed episodes: reward=%s length=%s observations=%s', scores, lengths, observations)
errored = [i for i, info_i in enumerate(info['n']) if 'error' in info_i]
if errored:
logger.info('had errored indexes: %s: %s', errored, info)
episodes_completed += len([d for d in done_n if d])
if episodes_completed >= args.max_episodes:
break
# if info.get('n') and info['n'][0].get('env_status.instruction'):
# logger.info('received instruction = %s', info['n'][0]['env_status.instruction'])
# if observation_n[0].get('text'):
# logger.info('message_n=%s', [observation['text'] for observation in observation_n])
# if any(done_n) or any(r != 0.0 and r is not None for r in reward_n):
# logger.info('reward_n=%s done_n=%s info=%s', reward_n, done_n, info)
# We're done! clean up
env.close()
================================================
FILE: example/random-agent/random-agent.py
================================================
#!/usr/bin/env python
import argparse
import logging
import sys
import gym
import universe # register the universe environments
from universe import wrappers
logger = logging.getLogger()
def main():
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
args = parser.parse_args()
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
env = gym.make('flashgames.NeonRace-v0')
env.configure(remotes=1) # automatically creates a local docker container
# Restrict the valid random actions. (Try removing this and see
# what happens when the agent is given full control of the
# keyboard/mouse.)
env = wrappers.experimental.SafeActionSpace(env)
observation_n = env.reset()
while True:
# your agent here
#
# Try sending this instead of a random action: ('KeyEvent', 'ArrowUp', True)
action_n = [env.action_space.sample() for ob in observation_n]
observation_n, reward_n, done_n, info = env.step(action_n)
env.render()
return 0
if __name__ == '__main__':
sys.exit(main())
================================================
FILE: example/recorders/botaction_recorder.py
================================================
#!/usr/bin/env python
"""
This is a small server that accepts connections on a websocket port and writes it to a file.
The purpose is to allow a universe-env with a built-in bot to record the actions it's taking
as a demonstration. So the demonstration includes a botactions.jsonl file that gets used instead
of the vnc client log. (The vnc client log is still recorded and needed to fully parse the VNC
protocol.)
It's much simpler than reward_recorder.py, because it doesn't have to also talk to the agent.
It just takes json messages over a websocket and appends them separated by newlines to the log file.
The ws port is 15986 unless overridden with --listen-address
The log file is /tmp/demo/botactions.jsonl unless overridden with --botaction-logfile
"""
import argparse
import logging
import sys
import json
from autobahn.twisted import websocket
from universe.twisty import reactor
logger = logging.getLogger()
class BotactionRecordingServer(websocket.WebSocketServerProtocol, object):
_next_id = 1
@classmethod
def next_id(cls):
id = cls._next_id
cls._next_id += 1
return id
logfile_path='/tmp/demo/botactions.jsonl'
def __init__(self):
super(BotactionRecordingServer, self).__init__()
self.id = self.next_id()
self._closed = False
self.file = None
logger.info("[BotactionRecordingServer] [%d] Wrote version number", self.id)
def _emit(self, rec):
if self.file:
self.file.write(json.dumps(rec) + '\n');
self.file.flush()
def onConnect(self, request):
logger.info('[BotactionRecordingServer] [%d] Client connecting: %s. Writing to %s', self.id, request.peer, self.logfile_path)
self.file = open(self.logfile_path, 'w', encoding='utf-8')
self._emit({
'version': 1,
'session_id': self.id,
'_debug_version': '0.0.1', # Give this an internal version for debugging corrupt reward.demo files # TODO, pull this from setup.py or the host docker image
})
def onOpen(self):
logger.info("[BotactionRecordingServer] [%d] Websocket connection established", self.id)
def onClose(self, wasClean, code, reason):
logger.info('[BotactionRecordingServer] [%d] Client connection closed: %s', self.id, reason)
if self.file:
self.file.close()
self.file = None
self._closed = True
def onMessage(self, msg, binary):
logger.debug('[BotactionRecordingServer] [%d] Received message from client: %s', self.id, msg)
self._emit(json.loads(msg.decode('utf-8')));
def main():
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
parser.add_argument('-l', '--listen-address', default='127.0.0.1:15896', help='Address to listen on')
parser.add_argument('-o', '--botaction-logfile', default='/tmp/demo/botactions.jsonl', help='Filename for timestamped log of bot actions.')
args = parser.parse_args()
BotactionRecordingServer.logfile_path = args.botaction_logfile
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
factory = websocket.WebSocketServerFactory()
factory.protocol = BotactionRecordingServer
host, port = args.listen_address.split(':')
port = int(port)
logger.info('Listening on %s:%s', host, port)
reactor.listenTCP(port, factory)
reactor.run()
return 0
if __name__ == '__main__':
sys.exit(main())
================================================
FILE: example/recorders/reward_recorder.py
================================================
#!/usr/bin/env python
import argparse
import logging
import sys
from autobahn.twisted import websocket
from universe.rewarder import reward_proxy_server
from universe.twisty import reactor
logger = logging.getLogger()
def main():
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
parser.add_argument('-l', '--listen-address', default='0.0.0.0:15898', help='Address to listen on')
parser.add_argument('-s', '--rewarder-address', default='127.0.0.1:15900', help='Address of the reward server to run on.')
parser.add_argument('-d', '--logfile-dir', default=None, help='Base directory to write logs for each connection')
args = parser.parse_args()
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
factory = websocket.WebSocketServerFactory()
factory.protocol = reward_proxy_server.RewardProxyServer
factory.rewarder_address = args.rewarder_address
factory.logfile_dir = args.logfile_dir
factory.setProtocolOptions(maxConnections=1) # We only write reward logs to one place, so only allow one connection
host, port = args.listen_address.split(':')
port = int(port)
logger.info('Listening on %s:%s', host, port)
reactor.listenTCP(port, factory)
reactor.run()
return 0
if __name__ == '__main__':
sys.exit(main())
================================================
FILE: example/recorders/vnc_recorder.py
================================================
#!/usr/bin/env python
import argparse
import logging
import os
import re
import sys
from universe import utils
from universe.vncdriver import vnc_proxy_server
from twisted.internet import protocol, reactor
logger = logging.getLogger()
def main():
parser = argparse.ArgumentParser(description=None)
parser.add_argument('-v', '--verbose', action='count', dest='verbosity', default=0, help='Set verbosity.')
parser.add_argument('-l', '--listen-address', default='0.0.0.0:5899', help='Address to listen on')
parser.add_argument('-s', '--vnc-address', default='127.0.0.1:5900', help='Address of the VNC server to run on.')
parser.add_argument('-d', '--logfile-dir', default=None, help='Base directory to write logs for each connection')
args = parser.parse_args()
if args.verbosity == 0:
logger.setLevel(logging.INFO)
elif args.verbosity >= 1:
logger.setLevel(logging.DEBUG)
factory = protocol.ServerFactory()
factory.protocol = vnc_proxy_server.VNCProxyServer
factory.vnc_address = 'tcp:{}'.format(args.vnc_address)
factory.logfile_dir = args.logfile_dir
factory.recorder_id = utils.random_alphanumeric().lower()
host, port = args.listen_address.split(':')
port = int(port)
logger.info('Listening on %s:%s', host, port)
reactor.listenTCP(port, factory, interface=host)
reactor.run()
return 0
if __name__ == '__main__':
sys.exit(main())
================================================
FILE: example/starter-cluster/starter-cluster
================================================
#!/usr/bin/env python
from contextlib import contextmanager
import logging
import math
import os
import os.path
import subprocess
import sys
import boto3
import click
import docker
import yaml
import universe
logger = logging.getLogger('cluster')
DEBUG_LOGGING_MAP = {
0: logging.WARNING,
1: logging.INFO,
2: logging.DEBUG
}
VNC_PORT = 5900
REWARDER_PORT = 15900
def get_ports(cli, n):
"""
returns a list of n ports that are available in the docker cluster
"""
used_ports = set()
containers = cli.containers()
for container in containers:
for port in container.get('Ports', []):
used_ports.add(port.get('PublicPort'))
ports = []
obtained = 0
for i in range(5000, 10000):
if i not in used_ports:
ports.append(i)
obtained += 1
if obtained == n:
break
return ports
def start_ssh_tunnel(host, local_port, remote_path, key_path):
cmd = [
'ssh', '-L', 'localhost:{}:{}'.format(local_port, remote_path),
'-o', 'stricthostkeychecking=no', '-o', 'UserKnownHostsFile=/dev/null']
if key_path:
cmd += ['-i', key_path]
cmd.append('ec2-user@{}'.format(host))
cmd.append('echo tunnel-ready; sleep 3600')
logger.debug('Starting SSH tunnel: %s', cmd)
process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
return process
@contextmanager
def docker_cli(host, local_port, remote_path='localhost:4000',
key_path=None):
ssh_tunnel = start_ssh_tunnel(host, local_port, remote_path, key_path)
for line in iter(ssh_tunnel.stdout.readline, ''):
if line.strip() == b'tunnel-ready':
break
logger.debug('SSH tunnel ready [pid: %s]', ssh_tunnel.pid)
try:
yield docker.Client(base_url='localhost:{}'.format(local_port))
finally:
logger.debug('SSH tunnel terminating')
ssh_tunnel.terminate()
for line in iter(ssh_tunnel.stderr.readline, ''):
line = line.strip()
if not line:
break
logger.debug('[SSH tunnel stderr] %s', line)
def get_compose_file(runtime):
dir_path = os.path.dirname(os.path.realpath(__file__))
return os.path.join(dir_path, 'gen', runtime, 'docker-compose.yaml')
def build_compose(cli, runtime, n):
spec = universe.runtime_spec(runtime)
expose = [VNC_PORT-1, VNC_PORT, REWARDER_PORT-1, REWARDER_PORT]
usable_ports = get_ports(cli, len(expose) * n)
output = {
'version': '2'
}
services = {}
for i in range(n):
service = {
'image': spec.image,
'command': spec.command,
'cap_add': spec.host_config.get('cap_add', [])
}
if spec.host_config.get('ipc_mode'):
service['ipc'] = spec.host_config['ipc_mode']
service['cpu_shares'] = int(math.ceil(spec.default_params.get('cpu', 4)))
service['ports'] = ['{host}:{container}'.format(container=port, host=usable_ports[i*len(expose)+j])
for j, port in enumerate(expose)]
service['labels'] = {
'universe.runtime': runtime,
'universe.index': str(i)
}
services['{}-{}'.format(runtime, i)] = service
output['services'] = services
content = yaml.dump(output)
filepath = get_compose_file(runtime)
directory = os.path.dirname(filepath)
if not os.path.exists(directory):
logger.info('Creating directory: %s', directory)
os.makedirs(directory)
logger.info('Writing compose file to %s', filepath)
with open(filepath, 'w') as f:
f.write(content)
return filepath
def start_compose(cli, filepath):
subprocess.check_call(['docker-compose', '-H', cli.base_url, '-f', filepath, 'up', '-d', '--remove-orphans'])
def stop_compose(cli, filepath):
subprocess.check_call(['docker-compose', '-H', cli.base_url, '-f', filepath, 'down', '--remove-orphans'])
class Stack(object):
def __init__(self, data):
self.name = data['StackName']
outputs = {}
for output in data['Outputs']:
outputs[output['OutputKey']] = output['OutputValue']
self.docker_ip = outputs['DockerIP']
self.worker_asg = outputs['ASGName']
def get_stack(name):
client = boto3.client('cloudformation')
response = client.describe_stacks(StackName=name)
if len(response['Stacks']) == 0:
raise Exception('Failed to find CloudFormation stack {}'.format(name))
return Stack(response['Stacks'][0])
def get_worker_instances(stack_name):
instances = {}
client = boto3.client('ec2')
response = client.describe_instances(
Filters=[
{
'Name': 'tag:aws:cloudformation:stack-name',
'Values': [
stack_name,
]
},
{
'Name': 'instance-state-name',
'Values': [
'running'
]
}
]
)
for reservation in response['Reservations']:
for instance in reservation['Instances']:
instances[instance['PrivateIpAddress']] = {
'id': instance['InstanceId'],
'public_ip': instance.get('PublicIpAddress')
}
return instances
def get_runtime_containers(cli, runtime):
containers_map = {}
filters = {
'label': ['universe.runtime={}'.format(runtime)]
}
containers = cli.containers(filters=filters)
for container in containers:
labels = container['Labels']
addr = None
for name in container['Names']:
if name.startswith('/ip-'):
addr = name.split('/')[1][3:].replace('-', '.')
containers_map[labels['com.docker.compose.service']] = {
'labels': labels,
'ports': container['Ports'],
'host': addr
}
return containers_map
@click.group()
@click.option('--verbose', '-v',
help='Sets the debug noise level, specify multiple times for more verbosity.',
type=click.IntRange(0, 3, clamp=True),
count=True)
def cli(verbose):
logger_handler = logging.StreamHandler(sys.stderr)
logger_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(logger_handler)
logger.setLevel(DEBUG_LOGGING_MAP.get(verbose, logging.DEBUG))
@cli.command()
@click.option('--stack', '-s', 'stack_name', required=True,
help='AWS CloudFormation Stack name.')
@click.option('--port', '-P', default=2374,
help='Local port to use for remote docker connection')
@click.option('--key-path', '-i',
help='Path to private key for SSH connection to docker host')
@click.option('--runtime', default='flashgames',
help='Runtime ID to start. (See universe/runtimes/__init__ for a starting list)')
@click.option('-n', default=1,
help='Number of environments to start')
def start(stack_name, port, key_path, runtime, n):
stack = get_stack(stack_name)
with docker_cli(stack.docker_ip, port, key_path=key_path) as cli:
filepath = build_compose(cli, runtime, n)
start_compose(cli, filepath)
containers = get_runtime_containers(cli, runtime)
instances = get_worker_instances(stack.name)
endpoints = []
for name, container in containers.items():
for port in container['ports']:
if port['PrivatePort'] == VNC_PORT:
vnc_port = port['PublicPort']
elif port['PrivatePort'] == REWARDER_PORT:
rewarder_port = port['PublicPort']
if container['host'] in instances:
host_ip = instances[container['host']]['public_ip']
endpoints.append(
'vnc://{}:{}+{}'.format(host_ip, vnc_port, rewarder_port))
else:
logger.warn(
'Container %s on unknown host %s', name, container['host'])
print('Environments started.')
print('Remotes:')
for endpoint in endpoints:
print('\t{}'.format(endpoint))
@cli.command()
@click.option('--stack', '-s', 'stack_name', required=True,
help='AWS CloudFormation Stack name.')
@click.option('--port', '-P', default=2374,
help='Local port to use for remote docker connection')
@click.option('--key-path', '-i',
help='Path to private key for SSH connection to docker host')
@click.option('--runtime', default='flashgames',
help='Runtime ID to stop. (See universe/runtimes/__init__ for a starting list)')
def stop(stack_name, port, key_path, runtime):
stack = get_stack(stack_name)
with docker_cli(stack.docker_ip, port, key_path=key_path) as cli:
filepath = get_compose_file(runtime)
stop_compose(cli, filepath)
print('Environments stopped.')
if __name__ == '__main__':
cli()
================================================
FILE: example/starter-cluster/starter-cluster-cf.json
================================================
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Docker cluster for OpenAI Universe runtimes",
"Mappings": {
"AWSInstanceType2Arch": {
"c3.2xlarge": {
"Arch": "HVM64"
},
"c3.4xlarge": {
"Arch": "HVM64"
},
"c3.8xlarge": {
"Arch": "HVM64"
},
"c3.large": {
"Arch": "HVM64"
},
"c3.xlarge": {
"Arch": "HVM64"
},
"c4.2xlarge": {
"Arch": "HVM64"
},
"c4.4xlarge": {
"Arch": "HVM64"
},
"c4.8xlarge": {
"Arch": "HVM64"
},
"c4.large": {
"Arch": "HVM64"
},
"c4.xlarge": {
"Arch": "HVM64"
},
"cc2.8xlarge": {
"Arch": "HVM64"
},
"cr1.8xlarge": {
"Arch": "HVM64"
},
"d2.2xlarge": {
"Arch": "HVM64"
},
"d2.4xlarge": {
"Arch": "HVM64"
},
"d2.8xlarge": {
"Arch": "HVM64"
},
"d2.xlarge": {
"Arch": "HVM64"
},
"g2.2xlarge": {
"Arch": "HVMG2"
},
"hi1.4xlarge": {
"Arch": "HVM64"
},
"hs1.8xlarge": {
"Arch": "HVM64"
},
"i2.2xlarge": {
"Arch": "HVM64"
},
"i2.4xlarge": {
"Arch": "HVM64"
},
"i2.8xlarge": {
"Arch": "HVM64"
},
"i2.xlarge": {
"Arch": "HVM64"
},
"m3.2xlarge": {
"Arch": "HVM64"
},
"m3.large": {
"Arch": "HVM64"
},
"m3.medium": {
"Arch": "HVM64"
},
"m3.xlarge": {
"Arch": "HVM64"
},
"m4.10xlarge": {
"Arch": "HVM64"
},
"m4.2xlarge": {
"Arch": "HVM64"
},
"m4.4xlarge": {
"Arch": "HVM64"
},
"m4.large": {
"Arch": "HVM64"
},
"m4.xlarge": {
"Arch": "HVM64"
},
"r3.2xlarge": {
"Arch": "HVM64"
},
"r3.4xlarge": {
"Arch": "HVM64"
},
"r3.8xlarge": {
"Arch": "HVM64"
},
"r3.large": {
"Arch": "HVM64"
},
"r3.xlarge": {
"Arch": "HVM64"
},
"t2.large": {
"Arch": "HVM64"
},
"t2.medium": {
"Arch": "HVM64"
},
"t2.micro": {
"Arch": "HVM64"
},
"t2.small": {
"Arch": "HVM64"
}
},
"AWSRegionArch2AMI" : {
"us-east-1" : {"PV64" : "ami-2a69aa47", "HVM64" : "ami-6869aa05", "HVMG2" : "ami-a41a3fb3"},
"us-west-2" : {"PV64" : "ami-7f77b31f", "HVM64" : "ami-7172b611", "HVMG2" : "ami-caf253aa"},
"us-west-1" : {"PV64" : "ami-a2490dc2", "HVM64" : "ami-31490d51", "HVMG2" : "ami-00347e60"},
"eu-west-1" : {"PV64" : "ami-4cdd453f", "HVM64" : "ami-f9dd458a", "HVMG2" : "ami-e2f7bd91"},
"eu-central-1" : {"PV64" : "ami-6527cf0a", "HVM64" : "ami-ea26ce85", "HVMG2" : "ami-d2ff04bd"},
"ap-northeast-1" : {"PV64" : "ami-3e42b65f", "HVM64" : "ami-374db956", "HVMG2" : "ami-4c78d52d"},
"ap-northeast-2" : {"PV64" : "NOT_SUPPORTED", "HVM64" : "ami-2b408b45", "HVMG2" : "NOT_SUPPORTED"},
"ap-southeast-1" : {"PV64" : "ami-df9e4cbc", "HVM64" : "ami-a59b49c6", "HVMG2" : "ami-f3f95990"},
"ap-southeast-2" : {"PV64" : "ami-63351d00", "HVM64" : "ami-dc361ebf", "HVMG2" : "ami-3a122e59"},
"ap-south-1" : {"PV64" : "NOT_SUPPORTED", "HVM64" : "ami-ffbdd790", "HVMG2" : "ami-21a7d34e"},
"us-east-2" : {"PV64" : "NOT_SUPPORTED", "HVM64" : "ami-f6035893", "HVMG2" : "NOT_SUPPORTED"},
"sa-east-1" : {"PV64" : "ami-1ad34676", "HVM64" : "ami-6dd04501", "HVMG2" : "NOT_SUPPORTED"},
"cn-north-1" : {"PV64" : "ami-77559f1a", "HVM64" : "ami-8e6aa0e3", "HVMG2" : "NOT_SUPPORTED"}
},
"VpcCidrs": {
"pubsubnet1": {
"cidr": "172.33.0.0/20"
},
"pubsubnet2": {
"cidr": "172.33.16.0/20"
},
"pubsubnet3": {
"cidr": "172.33.32.0/20"
},
"pubsubnet4": {
"cidr": "172.33.48.0/20"
},
"vpc": {
"cidr": "172.33.0.0/16"
}
}
},
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Swarm Size"
},
"Parameters": [
"ClusterSize"
]
},
{
"Label": {
"default": "Swarm Properties"
},
"Parameters": [
"KeyName"
]
},
{
"Label": {
"default": "Swarm Manager Properties"
},
"Parameters": [
"ManagerInstanceType",
"ManagerDiskSize",
"ManagerDiskType"
]
},
{
"Label": {
"default": "Swarm Worker Properties"
},
"Parameters": [
"InstanceType",
"WorkerDiskSize",
"WorkerDiskType"
]
}
],
"ParameterLabels": {
"ClusterSize": {
"default": "Number of Swarm worker nodes?"
},
"InstanceType": {
"default": "Agent worker instance type?"
},
"KeyName": {
"default": "Which SSH key to use?"
},
"ManagerDiskSize": {
"default": "Manager ephemeral storage volume size?"
},
"ManagerDiskType": {
"default": "Manager ephemeral storage volume type"
},
"ManagerInstanceType": {
"default": "Swarm manager instance type?"
},
"WorkerDiskSize": {
"default": "Worker ephemeral storage volume size?"
},
"WorkerDiskType": {
"default": "Worker ephemeral storage volume type"
}
}
}
},
"Outputs": {
"ASGName": {
"Description": "Name of the worker AutoScalingGroup",
"Value": {
"Ref": "NodeAsg"
}
},
"DockerIP": {
"Description": "Public IP of the manager node that can be SSH'd into",
"Value": {
"Fn::GetAtt": [
"ManagerInstance",
"PublicIp"
]
}
}
},
"Parameters": {
"ClusterSize": {
"Default": "4",
"Description": "Number of worker nodes in the Swarm (1-1000).",
"MaxValue": "1000",
"MinValue": "1",
"Type": "Number"
},
"InstanceType": {
"AllowedValues": [
"t2.micro",
"t2.small",
"t2.medium",
"t2.large",
"m4.large",
"m4.xlarge",
"m4.2xlarge",
"m4.4xlarge",
"m4.10xlarge",
"m3.medium",
"m3.large",
"m3.xlarge",
"m3.2xlarge",
"c4.large",
"c4.xlarge",
"c4.2xlarge",
"c4.4xlarge",
"c4.8xlarge",
"c3.large",
"c3.xlarge",
"c3.2xlarge",
"c3.4xlarge",
"c3.8xlarge",
"r3.large",
"r3.xlarge",
"r3.2xlarge",
"r3.4xlarge",
"r3.8xlarge",
"i2.xlarge",
"i2.2xlarge",
"i2.4xlarge",
"i2.8xlarge"
],
"ConstraintDescription": "Must be a valid EC2 HVM instance type.",
"Default": "c4.xlarge",
"Description": "EC2 HVM instance type (t2.micro, m3.medium, etc).",
"Type": "String"
},
"KeyName": {
"ConstraintDescription": "must be the name of an existing EC2 KeyPair.",
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances",
"MinLength": "1",
"Type": "AWS::EC2::KeyPair::KeyName"
},
"ManagerDiskSize": {
"Default": "20",
"Description": "Size of Manager's ephemeral storage volume in GiB",
"MaxValue": "1024",
"MinValue": "20",
"Type": "Number"
},
"ManagerDiskType": {
"AllowedValues": [
"standard",
"gp2"
],
"Default": "standard",
"Description": "Manager ephemeral storage volume type",
"Type": "String"
},
"ManagerInstanceType": {
"AllowedValues": [
"t2.micro",
"t2.small",
"t2.medium",
"t2.large",
"m4.large",
"m4.xlarge",
"m4.2xlarge",
"m4.4xlarge",
"m4.10xlarge",
"m3.medium",
"m3.large",
"m3.xlarge",
"m3.2xlarge",
"c4.large",
"c4.xlarge",
"c4.2xlarge",
"c4.4xlarge",
"c4.8xlarge",
"c3.large",
"c3.xlarge",
"c3.2xlarge",
"c3.4xlarge",
"c3.8xlarge",
"r3.large",
"r3.xlarge",
"r3.2xlarge",
"r3.4xlarge",
"r3.8xlarge",
"i2.xlarge",
"i2.2xlarge",
"i2.4xlarge",
"i2.8xlarge"
],
"ConstraintDescription": "Must be a valid EC2 HVM instance type.",
"Default": "t2.small",
"Description": "EC2 HVM instance type (t2.micro, m3.medium, etc).",
"Type": "String"
},
"WorkerDiskSize": {
"Default": "50",
"Description": "Size of Workers's ephemeral storage volume in GiB",
"MaxValue": "1024",
"MinValue": "20",
"Type": "Number"
},
"WorkerDiskType": {
"AllowedValues": [
"standard",
"gp2"
],
"Default": "standard",
"Description": "Worker ephemeral storage volume type",
"Type": "String"
}
},
"Resources": {
"AttachGateway": {
"DependsOn": [
"Vpc",
"InternetGateway"
],
"Properties": {
"InternetGatewayId": {
"Ref": "InternetGateway"
},
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::VPCGatewayAttachment"
},
"DockerLogGroup": {
"Properties": {
"LogGroupName": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"lg"
]
]
},
"RetentionInDays": 7
},
"Type": "AWS::Logs::LogGroup"
},
"InternetGateway": {
"DependsOn": "Vpc",
"Properties": {
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"IGW"
]
]
}
}
]
},
"Type": "AWS::EC2::InternetGateway"
},
"ManagerInstance": {
"DependsOn": [
"PubSubnetAz1",
"PubSubnetAz2"
],
"Properties": {
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"Manager"
]
]
}
},
{
"Key": "swarm-node-type",
"Value": "manager"
},
{
"Key": "swarm-stack-id",
"Value": {
"Ref": "AWS::StackId"
}
}
],
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvdb",
"Ebs": {
"VolumeSize": {
"Ref": "ManagerDiskSize"
},
"VolumeType": {
"Ref": "ManagerDiskType"
}
}
}
],
"IamInstanceProfile": {
"Ref": "ProxyInstanceProfile"
},
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
{
"Fn::FindInMap": [
"AWSInstanceType2Arch",
{
"Ref": "ManagerInstanceType"
},
"Arch"
]
}
]
},
"InstanceType": {
"Ref": "ManagerInstanceType"
},
"KeyName": {
"Ref": "KeyName"
},
"SecurityGroupIds": [
{
"Ref": "ManagerVpcSG"
},
{
"Ref": "SwarmWideSG"
}
],
"SubnetId": {
"Ref": "PubSubnetAz1"
},
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/sh\n",
"sudo yum update -y\n",
"curl -sSL https://get.docker.com/ | sh\n",
"sed -i 's/OPTIONS=\"/OPTIONS=\"-H tcp:\\/\\/0\\.0\\.0\\.0:2375 -H unix:\\/\\/\\/var\\/run\\/docker\\.sock /' /etc/sysconfig/docker\n",
"sudo /etc/init.d/docker start\n",
"sudo usermod -aG docker ec2-user\n",
"\n",
"export LOCAL_IP=$(wget -qO- http://169.254.169.254/latest/meta-data/local-ipv4)\n",
"sleep 5\n",
"docker run -d -p 8500:8500 --name=consul progrium/consul -server -bootstrap -advertise=$LOCAL_IP\n",
"docker run -d -p 4000:4000 swarm manage -H :4000 --replication --advertise $LOCAL_IP:4000 consul://$LOCAL_IP:8500\n"
]
]
}
}
},
"Type": "AWS::EC2::Instance"
},
"ManagerVpcSG": {
"DependsOn": "NodeVpcSG",
"Properties": {
"GroupDescription": "Manager SecurityGroup",
"SecurityGroupIngress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "22",
"IpProtocol": "tcp",
"ToPort": "22"
},
{
"FromPort": "2377",
"IpProtocol": "tcp",
"SourceSecurityGroupId": {
"Fn::GetAtt": [
"NodeVpcSG",
"GroupId"
]
},
"ToPort": "2377"
},
{
"FromPort": "4789",
"IpProtocol": "udp",
"SourceSecurityGroupId": {
"Fn::GetAtt": [
"NodeVpcSG",
"GroupId"
]
},
"ToPort": "4789"
},
{
"FromPort": "7946",
"IpProtocol": "tcp",
"SourceSecurityGroupId": {
"Fn::GetAtt": [
"NodeVpcSG",
"GroupId"
]
},
"ToPort": "7946"
},
{
"FromPort": "7946",
"IpProtocol": "udp",
"SourceSecurityGroupId": {
"Fn::GetAtt": [
"NodeVpcSG",
"GroupId"
]
},
"ToPort": "7946"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::SecurityGroup"
},
"NodeAsg": {
"DependsOn": "ManagerInstance",
"Properties": {
"DesiredCapacity": {
"Ref": "ClusterSize"
},
"LaunchConfigurationName": {
"Ref": "NodeLaunchConfigBeta12"
},
"MaxSize": "1000",
"MinSize": "0",
"Tags": [
{
"Key": "Name",
"PropagateAtLaunch": "true",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"worker"
]
]
}
},
{
"Key": "swarm-node-type",
"PropagateAtLaunch": "true",
"Value": "worker"
},
{
"Key": "swarm-stack-id",
"PropagateAtLaunch": "true",
"Value": {
"Ref": "AWS::StackId"
}
}
],
"VPCZoneIdentifier": [
{
"Fn::Join": [
",",
[
{
"Ref": "PubSubnetAz1"
},
{
"Ref": "PubSubnetAz2"
}
]
]
}
]
},
"Type": "AWS::AutoScaling::AutoScalingGroup",
"UpdatePolicy": {
"AutoScalingRollingUpdate": {
"MaxBatchSize": "1",
"MinInstancesInService": "1"
}
}
},
"NodeLaunchConfigBeta12": {
"DependsOn": "ManagerInstance",
"Properties": {
"AssociatePublicIpAddress": "true",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvdb",
"Ebs": {
"VolumeSize": {
"Ref": "WorkerDiskSize"
},
"VolumeType": {
"Ref": "WorkerDiskType"
}
}
}
],
"IamInstanceProfile": {
"Ref": "ProxyInstanceProfile"
},
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
{
"Fn::FindInMap": [
"AWSInstanceType2Arch",
{
"Ref": "InstanceType"
},
"Arch"
]
}
]
},
"InstanceType": {
"Ref": "InstanceType"
},
"KeyName": {
"Ref": "KeyName"
},
"SecurityGroups": [
{
"Ref": "NodeVpcSG"
},
{
"Ref": "SwarmWideSG"
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/sh\n",
"sudo yum update\n",
"curl -sSL https://get.docker.com/ | sh\n",
"export LOCAL_IP=$(wget -qO- http://169.254.169.254/latest/meta-data/local-ipv4)\n",
"sed -i \"s/OPTIONS=\\\"/OPTIONS=\\\"--cluster-store consul:\\/\\/",
{
"Fn::GetAtt": [
"ManagerInstance",
"PrivateIp"
]
},
":8500 --cluster-advertise=$LOCAL_IP:2375 -H tcp:\\/\\/0\\.0\\.0\\.0:2375 -H unix:\\/\\/\\/var\\/run\\/docker\\.sock /\" /etc/sysconfig/docker\n",
"sudo /etc/init.d/docker start\n",
"sudo usermod -aG docker ec2-user\n",
"\n",
"docker run -d swarm join --advertise=$LOCAL_IP:2375 consul://",
{
"Fn::GetAtt": [
"ManagerInstance",
"PrivateIp"
]
},
":8500\n"
]
]
}
}
},
"Type": "AWS::AutoScaling::LaunchConfiguration"
},
"NodeVpcSG": {
"DependsOn": "Vpc",
"Properties": {
"GroupDescription": "Node SecurityGroup",
"SecurityGroupEgress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "8",
"IpProtocol": "icmp",
"ToPort": "0"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "0",
"IpProtocol": "udp",
"ToPort": "65535"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "0",
"IpProtocol": "tcp",
"ToPort": "2374"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "2376",
"IpProtocol": "tcp",
"ToPort": "65535"
}
],
"SecurityGroupIngress": [
{
"CidrIp": {
"Fn::FindInMap": [
"VpcCidrs",
"vpc",
"cidr"
]
},
"FromPort": "0",
"IpProtocol": "-1",
"ToPort": "65535"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::SecurityGroup"
},
"ProxyInstanceProfile": {
"Properties": {
"Path": "/",
"Roles": [
{
"Ref": "ProxyRole"
}
]
},
"Type": "AWS::IAM::InstanceProfile"
},
"ProxyPolicies": {
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "elasticloadbalancing:*",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "elb-update",
"Roles": [
{
"Ref": "ProxyRole"
}
]
},
"Type": "AWS::IAM::Policy"
},
"ProxyRole": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com",
"autoscaling.amazonaws.com"
]
}
}
],
"Version": "2012-10-17"
},
"Path": "/"
},
"Type": "AWS::IAM::Role"
},
"PubSubnet1RouteTableAssociation": {
"DependsOn": [
"PubSubnetAz1",
"RouteViaIgw"
],
"Properties": {
"RouteTableId": {
"Ref": "RouteViaIgw"
},
"SubnetId": {
"Ref": "PubSubnetAz1"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"PubSubnet2RouteTableAssociation": {
"DependsOn": [
"PubSubnetAz2",
"RouteViaIgw"
],
"Properties": {
"RouteTableId": {
"Ref": "RouteViaIgw"
},
"SubnetId": {
"Ref": "PubSubnetAz2"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"PubSubnetAz1": {
"DependsOn": "Vpc",
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
"0",
{
"Fn::GetAZs": {
"Ref": "AWS::Region"
}
}
]
},
"CidrBlock": {
"Fn::FindInMap": [
"VpcCidrs",
"pubsubnet1",
"cidr"
]
},
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"Subnet1"
]
]
}
}
],
"VpcId": {
"Ref": "Vpc"
},
"MapPublicIpOnLaunch": "true"
},
"Type": "AWS::EC2::Subnet"
},
"PubSubnetAz2": {
"DependsOn": "Vpc",
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
"1",
{
"Fn::GetAZs": {
"Ref": "AWS::Region"
}
}
]
},
"CidrBlock": {
"Fn::FindInMap": [
"VpcCidrs",
"pubsubnet2",
"cidr"
]
},
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"Subnet2"
]
]
}
}
],
"VpcId": {
"Ref": "Vpc"
},
"MapPublicIpOnLaunch": "true"
},
"Type": "AWS::EC2::Subnet"
},
"PublicRouteViaIgw": {
"DependsOn": [
"AttachGateway",
"RouteViaIgw"
],
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "InternetGateway"
},
"RouteTableId": {
"Ref": "RouteViaIgw"
}
},
"Type": "AWS::EC2::Route"
},
"RouteViaIgw": {
"DependsOn": "Vpc",
"Properties": {
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"RT"
]
]
}
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::RouteTable"
},
"SwarmAPIPolicy": {
"DependsOn": "ProxyRole",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeVpcAttribute",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "swarm-policy",
"Roles": [
{
"Ref": "ProxyRole"
}
]
},
"Type": "AWS::IAM::Policy"
},
"SwarmAutoscalePolicy": {
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "autoscaling:*",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "swarm-autoscale-policy",
"Roles": [
{
"Ref": "ProxyRole"
}
]
},
"Type": "AWS::IAM::Policy"
},
"SwarmWideSG": {
"DependsOn": "Vpc",
"Properties": {
"GroupDescription": "Swarm wide access",
"SecurityGroupIngress": [
{
"CidrIp": {
"Fn::FindInMap": [
"VpcCidrs",
"vpc",
"cidr"
]
},
"FromPort": "0",
"IpProtocol": "-1",
"ToPort": "65535"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "5000",
"IpProtocol": "tcp",
"ToPort": "10000"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::SecurityGroup"
},
"Vpc": {
"Properties": {
"CidrBlock": {
"Fn::FindInMap": [
"VpcCidrs",
"vpc",
"cidr"
]
},
"EnableDnsHostnames": "true",
"EnableDnsSupport": "true",
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"VPC"
]
]
}
}
]
},
"Type": "AWS::EC2::VPC"
}
}
}
================================================
FILE: example/starter-cluster/starter-cluster-requirements.txt
================================================
boto3>=1.4.2
click>=6.6
docker-py==1.10.6
PyYAML>=3.12
universe>=0.1.0
docker-compose>=1.9.0
================================================
FILE: example/system-diagnostics/system_diagnostics_logger.py
================================================
#!/usr/bin/env python
import json
import psutil
import time
class DiagnosticsLogger(object):
def __init__(self, interval=5):
self.interval = interval
self.last_cpu_times = {} # pid -> (user, sys)
def run(self):
while True:
cpu_times, chrome_reset = self.cpu_times()
print(json.dumps({
'time': time.time(),
'cpu_times': cpu_times,
'cpu_percent': psutil.cpu_percent(percpu=True),
'chrome_reset': chrome_reset,
}), flush=True)
self.chrome_reset = False
time.sleep(self.interval)
def get_chrome_procs(self):
def is_chrome(proc):
try:
return proc.name() == 'chrome'
except psutil.ZombieProcess:
return False
return [p for p in psutil.process_iter() if is_chrome(p)]
def cpu_times(self):
''' return {pid: {'user': 0.0, 'sys': 0.0}}, chrome_reset '''
chrome_procs = self.get_chrome_procs()
new_pids = {p.pid for p in chrome_procs}
old_pids = {pid for pid in self.last_cpu_times}
try:
cpu_times = {p.pid: p.cpu_times() for p in chrome_procs}
except psutil.NoSuchProcess:
# Chrome restarted since fetching the new pids above. Better luck next time.
return {}, True
if new_pids != old_pids:
# We don't know when the Chrome procs were restarted, so don't
# return elapsed time until next run.
self.last_cpu_times = cpu_times
return {}, True
# Same chrome pids as last run: measure the elapsed cpu times
ordered_old_times = (self.last_cpu_times[p.pid] for p in chrome_procs)
ordered_new_times = (cpu_times[p.pid] for p in chrome_procs)
cpu_times_diff = {p.pid: {'user': (t[0] - l[0]) / self.interval, 'sys': (t[1] - l[1]) / self.interval}
for (p, t, l) in zip(chrome_procs, ordered_new_times, ordered_old_times)}
self.last_cpu_times = cpu_times
return cpu_times_diff, False
if __name__ == '__main__':
DiagnosticsLogger().run()
================================================
FILE: setup.py
================================================
from setuptools import setup, find_packages
setup(name='universe',
version='0.21.5',
description="Universe: a software platform for measuring and training an AI's general intelligence across the world's supply of games, websites and other applications.",
url='https://github.com/openai/universe',
author='OpenAI',
author_email='universe@openai.com',
packages=[package for package in find_packages()
if package.startswith('universe')],
install_requires=[
'autobahn>=0.16.0',
'docker-py==1.10.3',
'docker-pycreds==0.2.1',
'fastzbarlight>=0.0.13',
'go-vncdriver>=0.4.8',
'gym>=0.8.1',
'Pillow>=3.3.0',
'PyYAML>=3.12',
'six>=1.10.0',
'twisted>=16.5.0',
'ujson>=1.35',
],
package_data={'universe': ['runtimes.yml', 'runtimes/flashgames.json']},
tests_require=['pytest'],
extras_require={
'atari': 'gym[atari]',
}
)
================================================
FILE: test.dockerfile
================================================
FROM quay.io/openai/universe
RUN pip install tox
# Upload our actual code
WORKDIR /usr/local/universe/
COPY . ./
# Run tox. Keep printing so Travis knows we're alive.
CMD ["bash", "-c", "( while true; do echo '.'; sleep 60; done ) & tox"]
================================================
FILE: tests/functional/test_core_envs_semantics.py
================================================
import logging
import pytest
import gym
import numpy as np
from PIL import Image
from gym import spaces
from universe import wrappers
from universe.envs.vnc_core_env import translator
def show(obs):
Image.fromarray(obs).show()
class AtariMatcher(object):
def translator(self, env):
return translator.AtariTranslator(env)
def crop(self, obs):
return obs[20:210, :160, :]
def assert_match(self, obs, vnc_obs, extra_info=None, stage=None):
# Crop out the mouse
vnc_obs_cropped = self.crop(vnc_obs)
obs_cropped = self.crop(obs)
if not np.all(vnc_obs_cropped == obs_cropped):
show(vnc_obs_cropped)
show(obs_cropped)
show(vnc_obs_cropped - obs_cropped)
assert False, '[{}] Observations do not match: vnc_obs_cropped={} obs_cropped={} extra_info={}'.format(stage, vnc_obs_cropped, obs_cropped, extra_info)
# Wraps an Atari-over-VNC env so that it behaves like a vectorized vanilla Atari env
def atari_vnc_wrapper(env):
env = wrappers.Vision(env)
env = wrappers.GymCoreAction(env)
return env
class CartPoleLowDMatcher(object):
def translator(self, env):
return translator.CartPoleTranslator(env)
def assert_match(self, obs, vnc_obs, extra_info=None, stage=None):
assert np.all(np.isclose(obs, vnc_obs)), '[{}] Observations do not match: vnc_obs={} obs={}'.format(stage, vnc_obs, obs)
def reset(matcher, env, vnc_env, stage=None):
obs = env.reset()
vnc_obs = vnc_env.reset()
matcher.assert_match(obs, vnc_obs, stage=stage)
def rollout(matcher, env, vnc_env, timestep_limit=None, stage=None):
count = 0
actions = matcher.translator(env)
done = None
while True:
action = env.action_space.sample()
obs, reward, done, info = env.step(action)
if done:
# Account for remote auto-reset
obs = env.reset()
vnc_obs, vnc_reward, vnc_done, vnc_info = vnc_env.step(action)
assert reward == vnc_reward
assert done == vnc_done
assert vnc_info['stats.reward.count'] == 1
matcher.assert_match(obs, vnc_obs, {'reward': reward, 'done': done}, stage=stage)
count += 1
if done or (timestep_limit is not None and count >= timestep_limit):
break
# TODO: we should have auto-env spinup
specs = [
(gym.spec('gym-core.PongDeterministicSync-v3'), AtariMatcher(), atari_vnc_wrapper),
(gym.spec('gym-core.PitfallDeterministicSync-v3'), AtariMatcher(), atari_vnc_wrapper),
# This test is still broken. Looks like we're not piping the seed
# to the CartPole env behind VNC
# (gym.spec('gym-core.CartPoleLowDSync-v0'), CartPoleLowDMatcher())
]
@pytest.mark.parametrize("spec,matcher,wrapper", specs)
def test_nice_vnc_semantics_match(spec, matcher, wrapper):
# Check that when running over VNC or using the raw environment,
# semantics match exactly.
gym.undo_logger_setup()
logging.getLogger().setLevel(logging.INFO)
spaces.seed(0)
vnc_env = spec.make()
if vnc_env.metadata.get('configure.required', False):
vnc_env.configure(remotes=1)
vnc_env = wrapper(vnc_env)
vnc_env = wrappers.Unvectorize(vnc_env)
env = gym.make(spec._kwargs['gym_core_id'])
env.seed(0)
vnc_env.seed(0)
# Check that reset observations work
reset(matcher, env, vnc_env, stage='initial reset')
# Check a full rollout
rollout(matcher, env, vnc_env, timestep_limit=50, stage='50 steps')
# Reset to start a new episode
reset(matcher, env, vnc_env, stage='reset to new episode')
# Check that a step into the next episode works
rollout(matcher, env, vnc_env, timestep_limit=1, stage='1 step in new episode')
# Make sure env can be reseeded
env.seed(1)
vnc_env.seed(1)
reset(matcher, env, vnc_env, 'reseeded reset')
rollout(matcher, env, vnc_env, timestep_limit=1, stage='reseeded step')
================================================
FILE: tests/functional/test_envs.py
================================================
import logging
import os
import pytest
import re
import gym
from universe import wrappers
from universe.runtimes import registration
logger = logging.getLogger(__name__)
# Choose a sample from each category
# TODO: Add more comprehensive test that runs all envs
test_envs = [
# 'gym-core.PongShortSync-v3',
# 'gym-core.CartPoleLowDSync-v0',
'flashgames.DuskDrive-v0',
'internet.SlitherIO-v0',
# 'wob.DragBox-v0',
]
@pytest.mark.parametrize('env_id', test_envs)
def test_smoke(env_id):
"""Check that environments start up without errors and that we can extract rewards and observations"""
gym.undo_logger_setup()
logging.getLogger().setLevel(logging.INFO)
env = gym.make(env_id)
if env.metadata.get('configure.required', False):
if os.environ.get('FORCE_LATEST_UNIVERSE_DOCKER_RUNTIMES'): # Used to test universe-envs in CI
configure_with_latest_docker_runtime_tag(env)
else:
env.configure(remotes=1)
env = wrappers.Unvectorize(env)
env.reset()
_rollout(env, timestep_limit=60*30) # Check a rollout
def _rollout(env, timestep_limit=None):
"""
Test that a rollout follows our desired format. Includes the following checks:
1. The environment resets and provides an observation within our timestep_limit
2. Done signals map to the following:
done=True => Episode over (sent once at end of episode)
done=None => Resetting, agent takes no actions until done=False again
done=False => Episode is running, agent should take actions
"""
count = 0
episode_state = "resetting"
while True:
obs, reward, done, info = env.step([]) # Step with noop action
count += 1
if episode_state == 'resetting':
if done is None: # Still resetting
assert obs is None
continue
elif done is False:
episode_state = 'running'
if episode_state == 'running':
assert done is False
assert isinstance(reward, float)
assert isinstance(done, bool), "Received done=None before done=True"
# TODO: Remove this None check after we fix done=None semantics
if obs is not None:
assert obs['vision'].shape == (768, 1024, 3)
break
if timestep_limit is not None and count >= timestep_limit:
assert episode_state == 'running', "Failed to finish resetting in timestep limit"
break
# if timestep_limit is not None and count >= timestep_limit:
# self.assertTrue(completed_full_episode, "Failed to complete a full episode in timestep limit")
# break
def configure_with_latest_docker_runtime_tag(env):
original_image = registration.runtime_spec(env.spec.tags['runtime']).image
latest_image = re.sub(r':.*', ':latest', original_image)
logger.info("Using latest image: {}".format(latest_image))
env.configure(remotes=1, docker_image=latest_image)
================================================
FILE: tox.ini
================================================
# Tox (http://tox.testrun.org/) is a tool for running tests
# in multiple virtualenvs. This configuration file will run the
# test suite on all supported python versions. To use it, "pip install tox"
# and then run "tox" from this directory.
[tox]
envlist = py27, py35
skipsdist=True
[testenv]
passenv=DISPLAY DOCKER_USERNAME DOCKER_PASSWORD FORCE_LATEST_UNIVERSE_DOCKER_RUNTIMES TRAVIS*
deps =
pytest
gym[atari]<0.9
docker-py==1.10.3
Pillow
autobahn
twisted
ujson
boto
commands =
pip install -e /usr/local/universe
pytest {posargs}
================================================
FILE: universe/__init__.py
================================================
# Welcome to Universe!
#
# This file contains the client-side registry of environments.
import logging
import os
# Suppress Twisted's warning about service_identity not being installed.
# We don't need service_identity right now and don't want to take it on
# as a dependency just to suppress this warning.
import warnings
warnings.filterwarnings(
'ignore',
message='You do not have a working installation of the service_identity'
)
from gym.envs.registration import register
import universe.scoreboard
import universe.configuration
from universe import error, envs
from universe.remotes import docker_remote
from universe.rewarder import merge_infos
from universe.runtimes.registration import runtime_spec
__all__ = [
'configuration', 'envs', 'error', 'kube', 'pyprofile', 'remotes', 'rewarder', 'runtimes',
'scoreboard', 'spaces', 'twisty', 'utils', 'vectorized', 'vncdriver', 'wrappers',
'configure_logging', 'docker_image', 'enable_logfile',
'logger', 'extra_logger']
def docker_image(runtime_id):
logger.warn('DEPRECATION WARNING: universe.docker_image(runtime_id) is deprecated and will be removed soon. Use runtime_spec(runtime_id).image instead. ')
return runtime_spec(runtime_id).image
#################### Logging configuration ####################
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
_logging_configured = False
def enable_logfile(path=None):
raise error.Error('Renamed to "universe.configure_logging()"')
def configure_logging(path=None):
"""
Set up log levels, and split verbose logs to a file
Configure the client-side environment logs to print
to stdout at "info" level, and also to print to a
verbose log file located at /tmp/universe-.log
or another path you specify at "debug" level.
We suggest calling this method at the beginning of
your script.
"""
global _logging_configured
if _logging_configured:
return
_logging_configured = True
if path is False:
# Disable logfile
return
elif path is None:
path = '/tmp/universe-{}.log'.format(os.getpid())
logger.info('Writing logs to file: %s', path)
# Turn up extra_logger level
extra_logger.setLevel(logging.DEBUG)
if path == '-':
return
# Add file handler to root logger
root_logger = logging.getLogger()
formatter = logging.Formatter('[%(asctime)s] %(message)s')
handler = logging.FileHandler(path, 'w', encoding='UTF-8')
handler.setFormatter(formatter)
root_logger.addHandler(handler)
# Set extra_logger to *only* use file handler
extra_logger.propagate = False
extra_logger.addHandler(handler)
############### Environment registration and runtime specification ###############
#
# Universe environments are registered with the gym
# environment registry when the universe module
# is imported. We use the "tags" field to store
# additional data specific to Universe.
#------------------------ Gym core environments -----------------------#
# Asynchronous VNC versions of core gym environments,
# such as CartPole and Pong
# Note on metadata:
# Environments send on-screen metadata: the current time, and the
# time the last action was received from the agent. This timestamp
# data is used to compute action and observation lags. For core
# environments, this data is sent using on-screen pixels that encode
# timestamps.
metadata_pixels = {
'type': 'pixels',
}
# Should be exactly the same as CartPole-v0
register(
id='gym-core.CartPoleLowDSync-v0',
entry_point='universe.wrappers:WrappedGymCoreSyncEnv',
max_episode_steps=500,
tags={
'vnc': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'rewarder_observation': True,
'gym_core_id': 'CartPole-v0',
},
trials=2,
)
# Dynamics should match CartPole-v0, but have pixel observations
register(
id='gym-core.CartPoleSync-v0',
entry_point='universe.wrappers:WrappedGymCoreSyncEnv',
max_episode_steps=500,
tags={
'vnc': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': 'CartPole-v0',
},
trials=2,
)
# Async cartpole with 4-d observations
register(
id='gym-core.CartPoleLowD-v0',
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=500,
tags={
'vnc': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'rewarder_observation': True,
'gym_core_id': 'CartPole-v0',
},
trials=2,
)
register(
id='gym-core.CartPole-v0',
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=500,
tags={
'vnc': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': 'CartPole-v0',
},
trials=2,
)
# gym-core.Atari
for game in ['air_raid', 'alien', 'amidar', 'assault', 'asterix',
'asteroids', 'atlantis', 'bank_heist', 'battle_zone',
'beam_rider', 'berzerk', 'bowling', 'boxing', 'breakout',
'carnival', 'centipede', 'chopper_command', 'crazy_climber',
'demon_attack', 'double_dunk', 'elevator_action', 'enduro',
'fishing_derby', 'freeway', 'frostbite', 'gopher', 'gravitar',
'ice_hockey', 'jamesbond', 'journey_escape', 'kangaroo', 'krull',
'kung_fu_master', 'montezuma_revenge', 'ms_pacman',
'name_this_game', 'phoenix', 'pitfall', 'pong', 'pooyan',
'private_eye', 'qbert', 'riverraid', 'road_runner', 'robotank',
'seaquest', 'skiing', 'solaris', 'space_invaders', 'star_gunner',
'tennis', 'time_pilot', 'tutankham', 'up_n_down', 'venture',
'video_pinball', 'wizard_of_wor', 'yars_revenge', 'zaxxon']:
# space_invaders should yield SpaceInvaders-v0 and SpaceInvaders-ram-v0
base = ''.join([g.capitalize() for g in game.split('_')]) # SpaceInvaders
for version in [0, 3]:
gym_core_id = '{}-v{}'.format(base, version) # e.g. SpaceInvaders-v3
register(
id='gym-core.{}'.format(gym_core_id),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=100000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': gym_core_id,
},
)
register(
id='gym-core.{}Sync-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreSyncEnv',
max_episode_steps=100000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': gym_core_id,
},
)
register(
id='gym-core.{}30FPS-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=100000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': gym_core_id,
'fps': 30,
},
)
register(
id='gym-core.{}Slow-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=100000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': gym_core_id,
'fps': 15,
},
)
deterministic_gym_core_id = '{}Deterministic-v{}'.format(base, version) # e.g. SpaceInvadersDeterministic-v3
register(
id='gym-core.{}Deterministic-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=100000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': deterministic_gym_core_id,
},
)
register(
id='gym-core.{}DeterministicSlow-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=75000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': deterministic_gym_core_id,
'fps': 15,
},
)
register(
id='gym-core.{}DeterministicSync-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreSyncEnv',
max_episode_steps=75000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': deterministic_gym_core_id,
},
)
no_frameskip_gym_core_id = '{}NoFrameskip-v{}'.format(base, version) # e.g. SpaceInvadersNoFrameskip-v3
register(
id='gym-core.{}NoFrameskip-v{}'.format(base, version),
entry_point='universe.wrappers:WrappedGymCoreEnv',
max_episode_steps=400000,
tags={
'vnc': True,
'atari': True,
'runtime': 'gym-core',
'metadata_encoding': metadata_pixels,
},
kwargs={
'gym_core_id': no_frameskip_gym_core_id,
},
)
#------------------------ Flash game environments ------------------------#
# Browser-based flash games, run locally
# in Chrome within a Docker container
# Note on metadata: flashgames send time metadata using
# an on-screen QR code
metadata_v1 = {
'type': 'qrcode',
'x': 914,
'y': 658,
'width': 100,
'height': 100,
}
# Please keep this registry mirrored with the benchmarks in universe-envs/flashgames/gym_flashgames/__init__.py
# You can use universe-envs/flashgames/bin/manage export_env_ids_for_registration to generate this list.
for game in [
'flashgames.1001ArabianNights-v0',
'flashgames.21Balloons-v0',
'flashgames.30Seconds-v0',
'flashgames.3FootNinja-v0',
'flashgames.3FootNinjaIi-v0',
'flashgames.3dBuggyRacing-v0',
'flashgames.3dClassicRacing-v0',
'flashgames.3dFlashRacer-v0',
'flashgames.3dFuriousDriver-v0',
'flashgames.3dLaSupercars-v0',
'flashgames.3dLaSupercars2-v0',
'flashgames.3dMuscleCarRacer-v0',
'flashgames.3dRallyFever-v0',
'flashgames.3dRookieCop-v0',
'flashgames.3dSpeedFever-v0',
'flashgames.3dSportRampage-v0',
'flashgames.3dSuperRide-v0',
'flashgames.3dTestDrive-v0',
'flashgames.3dTruckInTheWoods-v0',
'flashgames.3dUrbanMadness-v0',
'flashgames.3dUrbanMadness2-v0',
'flashgames.4x4Monster3-v0',
'flashgames.99BricksTheLegendOfGarry-v0',
'flashgames.AWeekendAtTweetys-v0',
'flashgames.AbductionGrannysVersion-v0',
'flashgames.AchilliaTheGame-v0',
'flashgames.AcidFactory-v0',
'flashgames.AdrenalineChaser-v0',
'flashgames.AdventuresOfBloo-v0',
'flashgames.AeroDefense-v0',
'flashgames.AerobaticMaster2-v0',
'flashgames.Aerorumble-v0',
'flashgames.AirWar1941-v0',
'flashgames.AircraftRace-v0',
'flashgames.AladdinAndTheWonderLamp-v0',
'flashgames.AlchemySwap-v0',
'flashgames.AliceNixsAdventure-v0',
'flashgames.Alien-v0',
'flashgames.AlienAssault-v0',
'flashgames.AlienTransporter-v0',
'flashgames.AmericanRacing-v0',
'flashgames.AmericanRacingLvl2-v0',
'flashgames.AmericanRacingLvl3-v0',
'flashgames.AmericanRacingLvl4-v0',
'flashgames.AmericanRacingLvl5-v0',
'flashgames.AmericanRacingLvl6-v0',
'flashgames.AmericanRacingLvl7-v0',
'flashgames.AmericanRacingLvl8-v0',
'flashgames.AmericanRacingLvl9-v0',
'flashgames.AmericanRacingLvl10-v0',
'flashgames.AmericanRacingLvl11-v0',
'flashgames.AmericanRacingLvl12-v0',
'flashgames.AmericanRacingLvl13-v0',
'flashgames.AmericanRacingLvl14-v0',
'flashgames.AmericanRacingLvl15-v0',
'flashgames.AmericanRacingLvl16-v0',
'flashgames.AmericanRacingLvl17-v0',
'flashgames.AmericanRacingLvl18-v0',
'flashgames.AmericanRacingLvl19-v0',
'flashgames.AmericanRacingLvl20-v0',
'flashgames.AmericanRacingLvl21-v0',
'flashgames.AmericanRacingLvl22-v0',
'flashgames.AmericanRacingLvl23-v0',
'flashgames.AmericanRacingLvl24-v0',
'flashgames.AmericanRacingLvl25-v0',
'flashgames.AmericanRacing2-v0',
'flashgames.AmigoPancho-v0',
'flashgames.AmigoPancho3SheriffSancho-v0',
'flashgames.AmigoPancho4Travel-v0',
'flashgames.AmigoPanchoInAfghanistan-v0',
'flashgames.AngryNewsVan-v0',
'flashgames.AnimeClicker2-v0',
'flashgames.AnotherLife2-v0',
'flashgames.AntsGlider-v0',
'flashgames.AnywayFish-v0',
'flashgames.ArkanoidGame-v0',
'flashgames.ArmyPursuit-v0',
'flashgames.ArmySpeeder-v0',
'flashgames.AspenSecret-v0',
'flashgames.AsphaltMadness-v0',
'flashgames.AssembleBots-v0',
'flashgames.Astroman-v0',
'flashgames.AtvRide-v0',
'flashgames.Autoattack-v0',
'flashgames.Avalancher-v0',
'flashgames.AwesomeRun2-v0',
'flashgames.BackHome-v0',
'flashgames.BaldEagleJigsawPuzzle-v0',
'flashgames.BalloonGods-v0',
'flashgames.BalloonHero-v0',
'flashgames.BalloonsPop-v0',
'flashgames.Basement-v0',
'flashgames.BeachCrazy-v0',
'flashgames.BearInSuperActionAdventure-v0',
'flashgames.BigWheelsTrial-v0',
'flashgames.BikeTrial-v0',
'flashgames.BikeTrial2-v0',
'flashgames.BikeTrial3-v0',
'flashgames.BikeTrial4-v0',
'flashgames.Bimmin2-v0',
'flashgames.BirdSlice-v0',
'flashgames.BirdsFeeding-v0',
'flashgames.BlackAndWhiteEscapeTheOffice-v0',
'flashgames.BlackForce-v0',
'flashgames.BlackInk-v0',
'flashgames.BlackRacerJigsawPuzzle-v0',
'flashgames.BlacksmithLab-v0',
'flashgames.BlastTheMooks-v0',
'flashgames.BlastTheMooksLevelPack-v0',
'flashgames.Blix-v0',
'flashgames.BlobsStory-v0',
'flashgames.BlockysEscape-v0',
'flashgames.BloodbathBay-v0',
'flashgames.BloodyMonstersPack2-v0',
'flashgames.Blosics2-v0',
'flashgames.Blosics2LevelPack-v0',
'flashgames.Blosics3-v0',
'flashgames.BoatDrive-v0',
'flashgames.BobbyNutcaseMotoJumping-v0',
'flashgames.BoltThrough-v0',
'flashgames.BombIt4-v0',
'flashgames.BombIt6-v0',
'flashgames.BombThePiratePigs-v0',
'flashgames.BottleCaps-v0',
'flashgames.BouncyCannon-v0',
'flashgames.BoxBlocks-v0',
'flashgames.BoxRacers-v0',
'flashgames.BoxingLiveRound2-v0',
'flashgames.BraveAstronaut-v0',
'flashgames.BraveHeads-v0',
'flashgames.BubbleAdventures-v0',
'flashgames.BubbleBlubbs-v0',
'flashgames.BubbleGlee-v0',
'flashgames.BubbleHit-v0',
'flashgames.BubbleHitChristmas-v0',
'flashgames.BubbleHitHalloween-v0',
'flashgames.BubbleHitPonyParade-v0',
'flashgames.BubbleHitValentine-v0',
'flashgames.BubbleMover-v0',
'flashgames.BubblePop-v0',
'flashgames.BubblePopAdventure-v0',
'flashgames.BubbleRubble-v0',
'flashgames.BubbleRubbleTheIsland-v0',
'flashgames.BubbleShooterChallenge-v0',
'flashgames.BubbleSlasher-v0',
'flashgames.BubbleTanksTd15-v0',
'flashgames.BubbleTub-v0',
'flashgames.BubblesInSpace-v0',
'flashgames.BugsGotGuns-v0',
'flashgames.BuildBalance2-v0',
'flashgames.BulletFury-v0',
'flashgames.BulletHeaven-v0',
'flashgames.BulletHeaven2-v0',
'flashgames.Bullets-v0',
'flashgames.BullfrogJigsawPuzzle-v0',
'flashgames.BumbleTumble-v0',
'flashgames.BunnyAndSquirt-v0',
'flashgames.BunnyCannon-v0',
'flashgames.BurgerBar-v0',
'flashgames.BushRoyalRampage-v0',
'flashgames.Business-v0',
'flashgames.BusinessmanSimulator-v0',
'flashgames.CableCapers2-v0',
'flashgames.CakeQuest-v0',
'flashgames.CandyMatch-v0',
'flashgames.CandyMatchCrush-v0',
'flashgames.CandySlider-v0',
'flashgames.Canopy-v0',
'flashgames.CanyonValleyRally-v0',
'flashgames.CaptainNutty-v0',
'flashgames.CaptainSteelbounce-v0',
'flashgames.CardinalQuest2-v0',
'flashgames.CarrotFantasy-v0',
'flashgames.CarrotFantasy2Desert-v0',
'flashgames.CarrotFantasy2Undersea-v0',
'flashgames.CarrotFantasyExtreme2-v0',
'flashgames.CarrotFantasyExtreme3-v0',
'flashgames.CarsVsRobots-v0',
'flashgames.CartoonCandy-v0',
'flashgames.CastleRush-v0',
'flashgames.CastleSolitaire-v0',
'flashgames.CatGodVsSunKing2-v0',
'flashgames.CatchTheStar-v0',
'flashgames.Cattlepult-v0',
'flashgames.CattlepultPlayerPack-v0',
'flashgames.CavemanEscape-v0',
'flashgames.CemeteryRoad-v0',
'flashgames.CharlieTheDuck-v0',
'flashgames.Chefday-v0',
'flashgames.ChickCannont-v0',
'flashgames.ChickInduce-v0',
'flashgames.ChockABox-v0',
'flashgames.ChristmasBubbles-v0',
'flashgames.ChromaticTowerDefense-v0',
'flashgames.ChuteAcademy-v0',
'flashgames.CircuitSuperCarsRacing-v0',
'flashgames.CitySiege3FubarLevelPack-v0',
'flashgames.CitySkyTyping-v0',
'flashgames.ClaustrophobiumFourStepsFromDeath-v0',
'flashgames.Cleopatra-v0',
'flashgames.ClickerMonsters-v0',
'flashgames.ClimbOrDrown2-v0',
'flashgames.ClimberGuy-v0',
'flashgames.ClimbingSanta-v0',
'flashgames.CloseCombat-v0',
'flashgames.Cloud9-v0',
'flashgames.ClubNitro-v0',
'flashgames.Clusterobot-v0',
'flashgames.CoastRunners-v0',
'flashgames.CoasterCars2Contact-v0',
'flashgames.CoasterCars2Megacross-v0',
'flashgames.CoasterCarsBridgesTrack-v0',
'flashgames.CoasterCarsCJackTrack-v0',
'flashgames.CoasterRacer-v0',
'flashgames.CoasterRacerLvl2-v0',
'flashgames.CoasterRacerLvl3-v0',
'flashgames.CoasterRacerLvl4-v0',
'flashgames.CoasterRacerLvl5-v0',
'flashgames.CoasterRacerLvl6-v0',
'flashgames.CoasterRacerLvl7-v0',
'flashgames.CoasterRacerLvl8-v0',
'flashgames.CoasterRacer2-v0',
'flashgames.CoasterRacer2Lvl2-v0',
'flashgames.CoasterRacer2Lvl3-v0',
'flashgames.CoasterRacer2Lvl4-v0',
'flashgames.CoasterRacer2Lvl5-v0',
'flashgames.CoasterRacer2Lvl6-v0',
'flashgames.CoasterRacer2Lvl7-v0',
'flashgames.CoasterRacer2Lvl8-v0',
'flashgames.CoasterRacer2Lvl9-v0',
'flashgames.CoasterRacer2Lvl10-v0',
'flashgames.CoasterRacer2Bike-v0',
'flashgames.CoasterRacer3-v0',
'flashgames.CoffeeClicker-v0',
'flashgames.ColorZapper-v0',
'flashgames.Colordefense-v0',
'flashgames.Colorfill-v0',
'flashgames.Coloruid-v0',
'flashgames.Colorwars-v0',
'flashgames.Commando-v0',
'flashgames.Commando2-v0',
'flashgames.Conjure-v0',
'flashgames.Connect2-v0',
'flashgames.Conquerium-v0',
'flashgames.Contra3TheAlienWars-v0',
'flashgames.Cooliobeat-v0',
'flashgames.Cooliodj-v0',
'flashgames.CopperheadJigsawPuzzle-v0',
'flashgames.CosmicSwitch-v0',
'flashgames.CosmoGravity2-v0',
'flashgames.CoverOrangeJourneyGangsters-v0',
'flashgames.CowboyVsUfos-v0',
'flashgames.Crane-v0',
'flashgames.CrapImBroke-v0',
'flashgames.CrazyDarts-v0',
'flashgames.Crazycle-v0',
'flashgames.CruiseAdventure-v0',
'flashgames.Cruisin-v0',
'flashgames.Crumbs2-v0',
'flashgames.CrystalCurse-v0',
'flashgames.CrystalStoryIi-v0',
'flashgames.CupidBubbles-v0',
'flashgames.CursedTreasureDontTouchMyGems-v0',
'flashgames.CurveFever-v0',
'flashgames.DaleAndPeakot-v0',
'flashgames.DanceBattle-v0',
'flashgames.DancingWithShadows-v0',
'flashgames.DartsSim-v0',
'flashgames.DaymareInvaders-v0',
'flashgames.DeadHungry2-v0',
'flashgames.DeathCabin-v0',
'flashgames.DeathDiceOverdose-v0',
'flashgames.DeepForest3dRace-v0',
'flashgames.DeepFreeze-v0',
'flashgames.Deliveryman-v0',
'flashgames.DetectiveConrad-v0',
'flashgames.Devilment-v0',
'flashgames.DiamondCrashMania-v0',
'flashgames.DigToChina-v0',
'flashgames.DinoBubble-v0',
'flashgames.DinoMeatHunt3Extra-v0',
'flashgames.DirkValentine-v0',
'flashgames.DisasterWillStrikeDefender-v0',
'flashgames.DisasterWillStrikeUltimateDisaster-v0',
'flashgames.DiscoverEurope-v0',
'flashgames.Ditloid-v0',
'flashgames.DnaLabRush-v0',
'flashgames.Dodge-v0',
'flashgames.DodgeAndCrash-v0',
'flashgames.DolphinVolleyball-v0',
'flashgames.DontPanic-v0',
'flashgames.DoodleGod2Walkthrough-v0',
'flashgames.DotGrowth-v0',
'flashgames.Dots-v0',
'flashgames.DotsRevamped-v0',
'flashgames.DoubleEdged-v0',
'flashgames.DoughSnake-v0',
'flashgames.DragonChain-v0',
'flashgames.DragonChronicles-v0',
'flashgames.DragonFortress-v0',
'flashgames.DragonFunflap-v0',
'flashgames.DragonVsMonster-v0',
'flashgames.DrawGems-v0',
'flashgames.DreamChristmasLink-v0',
'flashgames.DriftRunners-v0',
'flashgames.DriftRunners2-v0',
'flashgames.DriftRunners3d-v0',
'flashgames.Drifters-v0',
'flashgames.DrinkBeerNeglectFamily-v0',
'flashgames.DriveToWreck-v0',
'flashgames.DriveToWreck2-v0',
'flashgames.DriveToWreck3-v0',
'flashgames.Dropblox-v0',
'flashgames.DualDimension-v0',
'flashgames.DumperRush-v0',
'flashgames.DungeonBlocks-v0',
'flashgames.DuskDrive-v0',
'flashgames.DuskRacers-v0',
'flashgames.EasterBubbles-v0',
'flashgames.EasterBunnyCollectCarrots-v0',
'flashgames.EasterBunnyEggs-v0',
'flashgames.EasterEggSlider-v0',
'flashgames.EasterEggsChallenge-v0',
'flashgames.EatToWin-v0',
'flashgames.EctoHarvest-v0',
'flashgames.EffingWorms-v0',
'flashgames.EggzBlast-v0',
'flashgames.EiffelTowerAtNight-v0',
'flashgames.ElClassico-v0',
'flashgames.ElainesBakery-v0',
'flashgames.EmpireBusiness2Beta-v0',
'flashgames.Enhanced-v0',
'flashgames.EpicBattleFantasy4-v0',
'flashgames.EpicDefender-v0',
'flashgames.EpicDerbyRace-v0',
'flashgames.EpicTimePirates-v0',
'flashgames.EscapeTheRedGiant-v0',
'flashgames.EuroKicks2016-v0',
'flashgames.EvasiveRacers-v0',
'flashgames.EvilMinion-v0',
'flashgames.EvilSun-v0',
'flashgames.EvolutionRacing-v0',
'flashgames.EvolutionRacingLvl2-v0',
'flashgames.EvolutionRacingLvl3-v0',
'flashgames.EvolutionRacingLvl4-v0',
'flashgames.EvolutionRacingLvl5-v0',
'flashgames.EvolutionRacingLvl6-v0',
'flashgames.EvolutionRacingLvl7-v0',
'flashgames.EvolutionRacingLvl8-v0',
'flashgames.EvolutionRacingLvl9-v0',
'flashgames.EvolutionRacingLvl10-v0',
'flashgames.EvolutionRacingLvl11-v0',
'flashgames.EvolutionRacingLvl12-v0',
'flashgames.EvolutionRacingLvl13-v0',
'flashgames.EvolutionRacingLvl14-v0',
'flashgames.EvolutionRacingLvl15-v0',
'flashgames.EvolutionRacingLvl16-v0',
'flashgames.ExperimentalShooter2-v0',
'flashgames.ExploreTheCandies-v0',
'flashgames.ExtremeAirWars-v0',
'flashgames.ExtremeSkiing-v0',
'flashgames.F1RacingChallenge-v0',
'flashgames.FairyDefense-v0',
'flashgames.FallDamage-v0',
'flashgames.FarmRush-v0',
'flashgames.FasterMiterMaster-v0',
'flashgames.FeedMeMoar-v0',
'flashgames.FeedOurDoughnutOverlords-v0',
'flashgames.Filler-v0',
'flashgames.Filler2-v0',
'flashgames.FinalNinjaZero-v0',
'flashgames.FinalSiege-v0',
'flashgames.FindTheCandy3Kids-v0',
'flashgames.Firebug-v0',
'flashgames.FirefighterCannon-v0',
'flashgames.FireworksGame-v0',
'flashgames.FishAndDestroy-v0',
'flashgames.FishEatFish-v0',
'flashgames.FitItQuick-v0',
'flashgames.FiveTil-v0',
'flashgames.Fizzion-v0',
'flashgames.Flagman-v0',
'flashgames.FlappyAdventure-v0',
'flashgames.FlappyBat-v0',
'flashgames.FlappyCopter-v0',
'flashgames.FlappyPanda-v0',
'flashgames.FlashBombs-v0',
'flashgames.FlashDrive-v0',
'flashgames.FlashRace-v0',
'flashgames.FlashRacer-v0',
'flashgames.Flashcycle2-v0',
'flashgames.FlashsBounty-v0',
'flashgames.FlowerGuardian-v0',
'flashgames.FlowerSolitaire-v0',
'flashgames.FluffRush-v0',
'flashgames.FlyAwayRabbit2-v0',
'flashgames.FlyPlane-v0',
'flashgames.FlyingCookieQuest-v0',
'flashgames.FlyingKiwi-v0',
'flashgames.FlyingTest-v0',
'flashgames.Foosball2Player-v0',
'flashgames.FootballHeads201314Ligue1-v0',
'flashgames.FormulaRacer-v0',
'flashgames.FormulaRacerLvl2-v0',
'flashgames.FormulaRacerLvl3-v0',
'flashgames.FormulaRacerLvl4-v0',
'flashgames.FormulaRacerLvl5-v0',
'flashgames.FormulaRacerLvl6-v0',
'flashgames.FormulaRacerLvl7-v0',
'flashgames.FormulaRacerLvl8-v0',
'flashgames.FormulaRacer2012-v0',
'flashgames.FormulaRacer2012Lvl2-v0',
'flashgames.FormulaRacer2012Lvl3-v0',
'flashgames.FormulaRacer2012Lvl4-v0',
'flashgames.FormulaRacer2012Lvl5-v0',
'flashgames.FormulaRacer2012Lvl6-v0',
'flashgames.FormulaRacer2012Lvl7-v0',
'flashgames.FormulaRacer2012Lvl8-v0',
'flashgames.FormulaRacer2012Lvl9-v0',
'flashgames.FormulaRacer2012Lvl10-v0',
'flashgames.FormulaRacer2012Lvl11-v0',
'flashgames.FormulaRacer2012Lvl12-v0',
'flashgames.FormulaXspeed3d-v0',
'flashgames.FoxSnakeJigsawPuzzle-v0',
'flashgames.FpaWorld1Remix-v0',
'flashgames.FreakyRun-v0',
'flashgames.FredFigglehorn-v0',
'flashgames.FreeSouls-v0',
'flashgames.Free_to_use-v0',
'flashgames.FreecellDuplex-v0',
'flashgames.FrogEatFlies-v0',
'flashgames.Frogged-v0',
'flashgames.FrozenImps-v0',
'flashgames.FrozenIslandsNewHorizons-v0',
'flashgames.Funkostroll-v0',
'flashgames.FunnyEaster-v0',
'flashgames.GSwitch-v0',
'flashgames.GalacticCats-v0',
'flashgames.GalacticGems-v0',
'flashgames.GalacticGems2-v0',
'flashgames.GalacticGems2Accelerated-v0',
'flashgames.GalacticGems2LevelPack-v0',
'flashgames.GalacticGems2NewFrontiers-v0',
'flashgames.GalaxyDefender-v0',
'flashgames.GalaxyEvo2-v0',
'flashgames.GalaxyMission-v0',
'flashgames.GalleonFight-v0',
'flashgames.GameInit-v0',
'flashgames.Gameinit-v0',
'flashgames.GamerMemoryTest-v0',
'flashgames.GardenRush-v0',
'flashgames.GasSand-v0',
'flashgames.GemMania-v0',
'flashgames.GemPop-v0',
'flashgames.Gemclix-v0',
'flashgames.Gemcraft-v0',
'flashgames.GemstoneCastle-v0',
'flashgames.GhostClimb2Player-v0',
'flashgames.GiantsAndDwarvesTd-v0',
'flashgames.GlobalRallyRacer-v0',
'flashgames.Gloom-v0',
'flashgames.Gluey2-v0',
'flashgames.Go-v0',
'flashgames.GoGreenGo-v0',
'flashgames.GoKart3d-v0',
'flashgames.Goldextraction-v0',
'flashgames.GolfRun-v0',
'flashgames.GonAndMon-v0',
'flashgames.GrandPrixGo-v0',
'flashgames.GrandPrixGo2-v0',
'flashgames.GrappleCat-v0',
'flashgames.GravityBall-v0',
'flashgames.GravityGuy-v0',
'flashgames.GravityThruster-v0',
'flashgames.GroundBattles-v0',
'flashgames.Growbox-v0',
'flashgames.GsSoccerWorldCup-v0',
'flashgames.GunExpress-v0',
'flashgames.GunnerMayhem-v0',
'flashgames.GunpowderAndFeathers-v0',
'flashgames.HalloweenAdventureRun-v0',
'flashgames.HalloweenExplorer-v0',
'flashgames.HalloweenJam-v0',
'flashgames.HammerBall-v0',
'flashgames.Hamsterball-v0',
'flashgames.HandsOff-v0',
'flashgames.HappyBallz-v0',
'flashgames.HappyBees-v0',
'flashgames.HappyEasterEggs-v0',
'flashgames.Harvest-v0',
'flashgames.HarvestDay-v0',
'flashgames.Hash-v0',
'flashgames.Hearts-v0',
'flashgames.HeatRushFuture-v0',
'flashgames.HeatRushFutureLvl2-v0',
'flashgames.HeatRushFutureLvl3-v0',
'flashgames.HeatRushFutureLvl4-v0',
'flashgames.HeatRushFutureLvl5-v0',
'flashgames.HeatRushFutureLvl6-v0',
'flashgames.HeatRushFutureLvl7-v0',
'flashgames.HeatRushFutureLvl8-v0',
'flashgames.HeatRushFutureLvl9-v0',
'flashgames.HeatRushFutureLvl10-v0',
'flashgames.HeatRushFutureLvl11-v0',
'flashgames.HeatRushFutureLvl12-v0',
'flashgames.HeatRushFutureLvl13-v0',
'flashgames.HeatRushFutureLvl14-v0',
'flashgames.HeatRushFutureLvl15-v0',
'flashgames.HeatRushUsa-v0',
'flashgames.HeatRushUsaLvl2-v0',
'flashgames.HeatRushUsaLvl3-v0',
'flashgames.HeatRushUsaLvl4-v0',
'flashgames.HeatRushUsaLvl5-v0',
'flashgames.HeatRushUsaLvl6-v0',
'flashgames.HeatRushUsaLvl7-v0',
'flashgames.HeatRushUsaLvl8-v0',
'flashgames.HeatRushUsaLvl9-v0',
'flashgames.HeatRushUsaLvl10-v0',
'flashgames.HeatRushUsaLvl11-v0',
'flashgames.HeatRushUsaLvl12-v0',
'flashgames.HeatRushUsaLvl13-v0',
'flashgames.HeatRushUsaLvl14-v0',
'flashgames.HeatRushUsaLvl15-v0',
'flashgames.HeatRushUsaLvl16-v0',
'flashgames.HeavenAndHell-v0',
'flashgames.HeavyLegion2-v0',
'flashgames.HeliVsTower-v0',
'flashgames.HelicopsTerritories-v0',
'flashgames.Helicrane-v0',
'flashgames.Helixteus-v0',
'flashgames.HelmetBombers3-v0',
'flashgames.HeroRoofTop-v0',
'flashgames.HeroSimulator-v0',
'flashgames.HeroesOfMangaraTheFrostCrown-v0',
'flashgames.HexBattles-v0',
'flashgames.HeySummer-v0',
'flashgames.HighSpeedChase-v0',
'flashgames.HighwayRevenge-v0',
'flashgames.HiredHeroes-v0',
'flashgames.HoldTheFort-v0',
'flashgames.HoleInOne-v0',
'flashgames.Hotspot-v0',
'flashgames.HowDareYou-v0',
'flashgames.HungerHunter-v0',
'flashgames.HungryLittlePenguins-v0',
'flashgames.HungryPiranha-v0',
'flashgames.HunterForDismantlers-v0',
'flashgames.HyperTravel-v0',
'flashgames.IceBlock-v0',
'flashgames.IceCreamFromSpace-v0',
'flashgames.IceRun-v0',
'flashgames.IceSlide-v0',
'flashgames.Ics2-v0',
'flashgames.IcyGifts2-v0',
'flashgames.IdleChop-v0',
'flashgames.IdleFarmer-v0',
'flashgames.IdleLifting-v0',
'flashgames.IdlePlanet-v0',
'flashgames.ImitationNationSnakeGame-v0',
'flashgames.IncrementalAcceleration-v0',
'flashgames.Indefinite-v0',
'flashgames.IndependenceDaySlacking2015-v0',
'flashgames.InfectonatorSurvivorsAlphaDemo-v0',
'flashgames.InfernalMess-v0',
'flashgames.Infinitix-v0',
'flashgames.InsaneCircle-v0',
'flashgames.IntoSpace-v0',
'flashgames.IslandDefense-v0',
'flashgames.IsoblockerMaster-v0',
'flashgames.ItsDarkInHell-v0',
'flashgames.JakeTheSnake-v0',
'flashgames.JamesTheCircusZebra-v0',
'flashgames.JamesTheDeepSeaZebra-v0',
'flashgames.JamesThePirateZebra-v0',
'flashgames.JamesTheSpaceZebra-v0',
'flashgames.JelliesFun-v0',
'flashgames.JellyFriend-v0',
'flashgames.JellySnake-v0',
'flashgames.JetpackJackride-v0',
'flashgames.JollySwipe-v0',
'flashgames.JollySwipeLevelPack-v0',
'flashgames.JonnyBackflip-v0',
'flashgames.JumpOverTheRings-v0',
'flashgames.Jumprunner-v0',
'flashgames.Jumpz-v0',
'flashgames.JungleCrash-v0',
'flashgames.JungleEagle-v0',
'flashgames.KamikazeRace-v0',
'flashgames.KangoIslands-v0',
'flashgames.KartOn-v0',
'flashgames.KartRacing-v0',
'flashgames.KartingSuperGo-v0',
'flashgames.Kawairun-v0',
'flashgames.KeeperOfTheGrove3-v0',
'flashgames.Kinetikz-v0',
'flashgames.Kinetikz2-v0',
'flashgames.Kinetikz3-v0',
'flashgames.KingRolla-v0',
'flashgames.KitchenRestaurantCleanUp-v0',
'flashgames.KnightsOfRock-v0',
'flashgames.Knighttron-v0',
'flashgames.Knockers-v0',
'flashgames.Krome-v0',
'flashgames.LaserCannon3LevelsPack-v0',
'flashgames.LawnmowerRacing3d-v0',
'flashgames.LaxAirbusParking-v0',
'flashgames.Lazerman-v0',
'flashgames.LearnToFlyIdle-v0',
'flashgames.Legor9-v0',
'flashgames.LessQuick-v0',
'flashgames.LetsFall-v0',
'flashgames.LevelEditor3-v0',
'flashgames.LilyFighters-v0',
'flashgames.LineGameLimeEdition-v0',
'flashgames.LlamasInDistress-v0',
'flashgames.LonelyEscapeAsylum-v0',
'flashgames.LongJump-v0',
'flashgames.Long_short-v0',
'flashgames.LooneyAndJohny-v0',
'flashgames.LuckyBalls-v0',
'flashgames.LuxUltimate-v0',
'flashgames.Madburger3-v0',
'flashgames.MadpetSkateboarder2-v0',
'flashgames.MagicSafari-v0',
'flashgames.ManicRallyGo-v0',
'flashgames.MapTurtleJigsawPuzzle-v0',
'flashgames.MarblesShooter-v0',
'flashgames.MarsColonyTd-v0',
'flashgames.MarshmallowsEscape-v0',
'flashgames.MashaCollectsButterflies-v0',
'flashgames.MasterDifference-v0',
'flashgames.Match2Collapse-v0',
'flashgames.Match3Adventure-v0',
'flashgames.Match3ChristmasPack-v0',
'flashgames.Match3PresentBoxSaga-v0',
'flashgames.MatchAndCrash-v0',
'flashgames.MatchAndSpell-v0',
'flashgames.MatchAroundTheWorld-v0',
'flashgames.MatchCraft-v0',
'flashgames.MatchCrypt-v0',
'flashgames.MatchJong-v0',
'flashgames.MatchMonsters-v0',
'flashgames.MatchStars-v0',
'flashgames.MatchTheBugz-v0',
'flashgames.MatchTheFruits-v0',
'flashgames.MatchToEnjoy-v0',
'flashgames.MatchToEnjoyLevelPack-v0',
'flashgames.MatchingSweetHearts-v0',
'flashgames.MazeEye-v0',
'flashgames.MedievalShark-v0',
'flashgames.MeerkatMission-v0',
'flashgames.MexicoRex-v0',
'flashgames.MiceVsHammers-v0',
'flashgames.Michimind-v0',
'flashgames.MidnightCanine-v0',
'flashgames.MidnightMiner-v0',
'flashgames.MightyTower-v0',
'flashgames.Mimelet-v0',
'flashgames.MindImpulse-v0',
'flashgames.MineDrop-v0',
'flashgames.MineHero-v0',
'flashgames.MinedigJourneyToHollowEarth-v0',
'flashgames.MiniMachines-v0',
'flashgames.MiniSportsChallenge-v0',
'flashgames.MinicarHunt-v0',
'flashgames.Minicarting-v0',
'flashgames.MissionEscapeTheDojo-v0',
'flashgames.ModelCarRacing-v0',
'flashgames.MonkeyBlast-v0',
'flashgames.MonkeyGems-v0',
'flashgames.MonkeyGoHappyNinjaHunt2-v0',
'flashgames.MonkeyManic-v0',
'flashgames.MonsterChains-v0',
'flashgames.MonsterLabFeedThemAll-v0',
'flashgames.MonsterRun-v0',
'flashgames.MonsterTroubles-v0',
'flashgames.MonsterTruckFever-v0',
'flashgames.MonsterTruckRally-v0',
'flashgames.Moosters-v0',
'flashgames.MotherLoad-v0',
'flashgames.MotoMadness-v0',
'flashgames.MotoTrialMania-v0',
'flashgames.MotorWheels-v0',
'flashgames.Mrbirdie-v0',
'flashgames.MultiballMadness-v0',
'flashgames.Multitask-v0',
'flashgames.MummyMadness-v0',
'flashgames.Mushbooms-v0',
'flashgames.MushboomsLevelPack-v0',
'flashgames.MushboomsLevelPack2-v0',
'flashgames.MushroomFarmDefender-v0',
'flashgames.MushyMishy-v0',
'flashgames.MusicSmash-v0',
'flashgames.MusicStomp-v0',
'flashgames.MusicZap-v0',
'flashgames.MysteriousPirateJewels-v0',
'flashgames.MysticIndiaPop-v0',
'flashgames.MysticalAncientTreasure-v0',
'flashgames.NOfficialWebVersion-v0',
'flashgames.NadiasRage-v0',
'flashgames.NanoKingdoms2JokersRevenge-v0',
'flashgames.NeonRace-v0',
'flashgames.NeonRaceLvl2-v0',
'flashgames.NeonRaceLvl3-v0',
'flashgames.NeonRaceLvl4-v0',
'flashgames.NeonRaceLvl5-v0',
'flashgames.NeonRaceLvl6-v0',
'flashgames.NeonRaceLvl7-v0',
'flashgames.NeonRaceLvl8-v0',
'flashgames.NeonRace2-v0',
'flashgames.NeonRace2Lvl2-v0',
'flashgames.NeonRace2Lvl3-v0',
'flashgames.NeonRace2Lvl4-v0',
'flashgames.NeonRace2Lvl5-v0',
'flashgames.NeonRace2Lvl6-v0',
'flashgames.NeonRace2Lvl7-v0',
'flashgames.NeonRace2Lvl8-v0',
'flashgames.NeonRace2Lvl9-v0',
'flashgames.NeonRace2Lvl10-v0',
'flashgames.NeonRace2Lvl11-v0',
'flashgames.NeonRace2Lvl12-v0',
'flashgames.NeonRace2Lvl13-v0',
'flashgames.NeonRace2Lvl14-v0',
'flashgames.NeonRace2Lvl15-v0',
'flashgames.Neopods-v0',
'flashgames.NervousLadybug-v0',
'flashgames.NewSiberianSupercarsRacing-v0',
'flashgames.NewSplitterPals-v0',
'flashgames.NightDrivin-v0',
'flashgames.NightRaceRally-v0',
'flashgames.NinjaPainter-v0',
'flashgames.NinjaPandaArena-v0',
'flashgames.NinjaPandaCouple-v0',
'flashgames.NinjaTrainingWorlds-v0',
'flashgames.Nook-v0',
'flashgames.NoughtsAndCrosses-v0',
'flashgames.NoughtsAndCrossesExtreme-v0',
'flashgames.NukeDefense-v0',
'flashgames.Numz-v0',
'flashgames.NuttyBoom-v0',
'flashgames.ObamaAlienDefense-v0',
'flashgames.OceanMatch-v0',
'flashgames.Oddball2-v0',
'flashgames.OffRoaders3d-v0',
'flashgames.OfficeTrap-v0',
'flashgames.Offroaders-v0',
'flashgames.Offroaders2-v0',
'flashgames.OkParking-v0',
'flashgames.OldTv-v0',
'flashgames.OozingForever-v0',
'flashgames.OswaldTheAngryDwarf-v0',
'flashgames.Overheat-v0',
'flashgames.PaintWars-v0',
'flashgames.Paintwars-v0',
'flashgames.PanikInChocoland-v0',
'flashgames.PapaLouie3WhenSundaesAttack-v0',
'flashgames.PaperDefense-v0',
'flashgames.ParachuteRetrospect-v0',
'flashgames.ParallelLevels-v0',
'flashgames.ParkingFury-v0',
'flashgames.Parkour-v0',
'flashgames.ParticleWarsExtreme-v0',
'flashgames.Pathillogical-v0',
'flashgames.PaulVaulting-v0',
'flashgames.Peakart-v0',
'flashgames.PearlBreaking-v0',
'flashgames.Pel-v0',
'flashgames.PenguinCubes-v0',
'flashgames.PenguinHeroes-v0',
'flashgames.PenguinSkate2-v0',
'flashgames.PerilousJourney2-v0',
'flashgames.Phit-v0',
'flashgames.PickAndDig2-v0',
'flashgames.PickUpTruckRacing-v0',
'flashgames.PicnicPanicTd-v0',
'flashgames.PigDestroyer-v0',
'flashgames.PiggyWiggy-v0',
'flashgames.PiggysCupcakeQuest-v0',
'flashgames.PinBalls-v0',
'flashgames.PinataWarriors-v0',
'flashgames.PingPongSurvival-v0',
'flashgames.PirateRunAway-v0',
'flashgames.PiratesAndCannons-v0',
'flashgames.PixelBasher-v0',
'flashgames.PixelFighta-v0',
'flashgames.PixelPurge-v0',
'flashgames.PixelQuest-v0',
'flashgames.PlaneRace-v0',
'flashgames.PlaneRace2-v0',
'flashgames.PlopPlopLite-v0',
'flashgames.PocketRocket-v0',
'flashgames.Pointer-v0',
'flashgames.Pointless-v0',
'flashgames.PoliceChaseCrackdown-v0',
'flashgames.PoliceHotRacing-v0',
'flashgames.PoliceInterceptor-v0',
'flashgames.PolygonalFury-v0',
'flashgames.Popopop-v0',
'flashgames.Popopop2-v0',
'flashgames.PouJetpack-v0',
'flashgames.PouThanksgivingDaySlacking-v0',
'flashgames.PowerCopter-v0',
'flashgames.PowerSwing-v0',
'flashgames.Primary-v0',
'flashgames.PrincessBubblesRescuePrince-v0',
'flashgames.PrincessToTheRescue-v0',
'flashgames.ProjectMonochrome-v0',
'flashgames.PuddingPie-v0',
'flashgames.PufferFish-v0',
'flashgames.PumpkinCollector-v0',
'flashgames.PumpkinMan-v0',
'flashgames.PumpkinsInZombieTown-v0',
'flashgames.PunchBallJump-v0',
'flashgames.PurifyTheLegendOfZ-v0',
'flashgames.PuzzleMonsters-v0',
'flashgames.PuzzleRescuePrime-v0',
'flashgames.PyramidApocalypse-v0',
'flashgames.Pyro-v0',
'flashgames.Qoosh-v0',
'flashgames.QuashBoard-v0',
'flashgames.QubedMysteriousIsland-v0',
'flashgames.QubeyTheCube-v0',
'flashgames.Quick-v0',
'flashgames.RabbitPlanetEscape-v0',
'flashgames.RabbitRustler-v0',
'flashgames.RacerKartz-v0',
'flashgames.RacingSupercarChampionship-v0',
'flashgames.RainbowDrops-v0',
'flashgames.RapaNui-v0',
'flashgames.Raze3-v0',
'flashgames.Rb2-v0',
'flashgames.Rbots-v0',
'flashgames.ReachTheGoal-v0',
'flashgames.RedBeard-v0',
'flashgames.RedCode3-v0',
'flashgames.RedFuryRacing-v0',
'flashgames.ReleaseTheMooks-v0',
'flashgames.ReleaseTheMooks2-v0',
'flashgames.ReleaseTheMooks3-v0',
'flashgames.Resonance-v0',
'flashgames.RetroRunner-v0',
'flashgames.Retron-v0',
'flashgames.ReverseBoots-v0',
'flashgames.RhythmBlasterV2-v0',
'flashgames.RhythmRockets-v0',
'flashgames.RhythmSnake-v0',
'flashgames.RingsideHero-v0',
'flashgames.RiseOfChampions-v0',
'flashgames.RoadRacing-v0',
'flashgames.RoadblockAttack-v0',
'flashgames.RoboPop-v0',
'flashgames.RobotDuelFight-v0',
'flashgames.RobotWantsFishy-v0',
'flashgames.RocketBootsInc-v0',
'flashgames.Rocketeer-v0',
'flashgames.RollTheCluster-v0',
'flashgames.RollerRider-v0',
'flashgames.RollingHills-v0',
'flashgames.Rose-v0',
'flashgames.RubbleRacer-v0',
'flashgames.RunFaustoRun-v0',
'flashgames.RunNGun-v0',
'flashgames.RunRamRun-v0',
'flashgames.RunRunRan-v0',
'flashgames.RunSoldierRun-v0',
'flashgames.RushOfTanks-v0',
'flashgames.RussianTruck-v0',
'flashgames.SafariTime-v0',
'flashgames.SandcastleShowdown-v0',
'flashgames.SantaClimbHere-v0',
'flashgames.SantaMan-v0',
'flashgames.SantaSituation-v0',
'flashgames.SapphireClix-v0',
'flashgames.SaveTheDummyHolidays-v0',
'flashgames.SavingLittleAlien-v0',
'flashgames.SchoolBusRacing-v0',
'flashgames.Scribble-v0',
'flashgames.Scribble2-v0',
'flashgames.SeaPong-v0',
'flashgames.ShamelessClone2-v0',
'flashgames.Sheepster-v0',
'flashgames.Sheepy-v0',
'flashgames.ShimmyChute-v0',
'flashgames.ShootTheCircle-v0',
'flashgames.ShortCircuit-v0',
'flashgames.SiegeHeroPiratePillage-v0',
'flashgames.Sieger2LevelPack-v0',
'flashgames.SiegerRebuiltToDestroy-v0',
'flashgames.Sirtet-v0',
'flashgames.SistersOfNoMercy-v0',
'flashgames.SkiSim-v0',
'flashgames.SkyIsland-v0',
'flashgames.SkyKnight2-v0',
'flashgames.SkyQuest-v0',
'flashgames.Skytrip-v0',
'flashgames.SliceTheBox-v0',
'flashgames.SliceTheBoxRemaster-v0',
'flashgames.SlingBaby-v0',
'flashgames.SlipSlideSloth-v0',
'flashgames.SmashTheSwine-v0',
'flashgames.SmileyJumpFest-v0',
'flashgames.SmileyPuzzle-v0',
'flashgames.SmileyPuzzle2-v0',
'flashgames.SmileyPuzzleGirlEdition-v0',
'flashgames.SmileyShowdown-v0',
'flashgames.SnackOnLittleCreatures-v0',
'flashgames.SnailBob4-v0',
'flashgames.Snake-v0',
'flashgames.SnakeClassic-v0',
'flashgames.SnakeFightArena-v0',
'flashgames.SneakyScubaEscape-v0',
'flashgames.SnowPrincessMakeup-v0',
'flashgames.SnowQueen-v0',
'flashgames.SnowQueen3-v0',
'flashgames.SnowQueen4-v0',
'flashgames.Solarsaurs-v0',
'flashgames.SonicBubbles-v0',
'flashgames.SpaceBounty-v0',
'flashgames.SpaceColony-v0',
'flashgames.SpaceMadness-v0',
'flashgames.SpacePunkRacer-v0',
'flashgames.SpacePunkRacerLvl2-v0',
'flashgames.SpacePunkRacerLvl3-v0',
'flashgames.SpacePunkRacerLvl4-v0',
'flashgames.SpacePunkRacerLvl5-v0',
'flashgames.SpacePunkRacerLvl6-v0',
'flashgames.SpacePunkRacerLvl7-v0',
'flashgames.SpacePunkRacerLvl8-v0',
'flashgames.SpacemanMax-v0',
'flashgames.SpanishLiga2016-v0',
'flashgames.Sparks-v0',
'flashgames.Spectrum-v0',
'flashgames.SpectrumHeist-v0',
'flashgames.SpectrumRunner-v0',
'flashgames.SpeedBusters-v0',
'flashgames.SpellIdle2-v0',
'flashgames.SpinClimbGreen-v0',
'flashgames.SpinSoar-v0',
'flashgames.SpinSprint-v0',
'flashgames.SpunkyVsAliens-v0',
'flashgames.Stalingrad-v0',
'flashgames.Stalingrad2-v0',
'flashgames.Stalingrad3-v0',
'flashgames.Stand-v0',
'flashgames.StarCars-v0',
'flashgames.Stardrops-v0',
'flashgames.Stargrazer-v0',
'flashgames.Stars-v0',
'flashgames.Stealthbound-v0',
'flashgames.StealthboundLevelPack-v0',
'flashgames.StickBlender-v0',
'flashgames.StickyNinjaMissions-v0',
'flashgames.Stickylinky-v0',
'flashgames.StitchlandConflict-v0',
'flashgames.StormRage-v0',
'flashgames.Stratega-v0',
'flashgames.StreetRace-v0',
'flashgames.StreetRace2Nitro-v0',
'flashgames.StreetRace3-v0',
'flashgames.Streetrace2Nitro-v0',
'flashgames.StrikeForceKitty-v0',
'flashgames.SubmarineFighter-v0',
'flashgames.Sundrops-v0',
'flashgames.SuperAdventurePalsBattleArena-v0',
'flashgames.SuperBattleCity2-v0',
'flashgames.SuperBomb-v0',
'flashgames.SuperBoxotron2000-v0',
'flashgames.SuperCandyGems-v0',
'flashgames.SuperCarRacing-v0',
'flashgames.SuperDash-v0',
'flashgames.SuperIdleMaster-v0',
'flashgames.SuperK9-v0',
'flashgames.SuperPuzzlePlatformer-v0',
'flashgames.SuperRally3d-v0',
'flashgames.SuperRallyChallenge-v0',
'flashgames.SuperRallyChallenge2-v0',
'flashgames.SuperRallyExtreme-v0',
'flashgames.SuperShinyheadHarderThanFlappyBird-v0',
'flashgames.SuperXtreme5MinuteShootEmUp-v0',
'flashgames.SuperbikeExtreme-v0',
'flashgames.SuperbikeRacer-v0',
'flashgames.SupercarDomination-v0',
'flashgames.SupergirlGo-v0',
'flashgames.SurfBuggy-v0',
'flashgames.SurvivalLab-v0',
'flashgames.SurvivorMissionD-v0',
'flashgames.SushiCatTheHoneymoon-v0',
'flashgames.SwagMan-v0',
'flashgames.SwampTreck-v0',
'flashgames.SwapTheDots-v0',
'flashgames.SweetTooth-v0',
'flashgames.SwimmingRace-v0',
'flashgames.SwingTriangle-v0',
'flashgames.TableTennisChallenge-v0',
'flashgames.TamusMitta-v0',
'flashgames.TankStorm-v0',
'flashgames.TankStorm2-v0',
'flashgames.TankStorm3-v0',
'flashgames.TankStorm4-v0',
'flashgames.TapRocket-v0',
'flashgames.TastyFruits-v0',
'flashgames.TattooArtist-v0',
'flashgames.TaxiInc-v0',
'flashgames.TaxiRacers-v0',
'flashgames.TechnoMania-v0',
'flashgames.TempleRunKnight-v0',
'flashgames.TerrestrialConflict-v0',
'flashgames.ThatRedButton-v0',
'flashgames.Thaw-v0',
'flashgames.TheBigEscape-v0',
'flashgames.TheBoomlandsWorldWars-v0',
'flashgames.TheBravestHunter-v0',
'flashgames.TheCaseOfScaryShadow-v0',
'flashgames.TheCubicMonkeyAdventures2-v0',
'flashgames.TheGreatSiege-v0',
'flashgames.TheOneForkRestaurantDx-v0',
'flashgames.ThePretenderPartThree-v0',
'flashgames.TheProfessionals3-v0',
'flashgames.TheSilentPlanet-v0',
'flashgames.TheThreeTowers-v0',
'flashgames.TheTowerman-v0',
'flashgames.Thundercars-v0',
'flashgames.TinyCastle-v0',
'flashgames.TinyRacers-v0',
'flashgames.Titanic-v0',
'flashgames.TokyoGuineaPop-v0',
'flashgames.ToonEscapeMaze-v0',
'flashgames.ToonEscapeSpookHouse-v0',
'flashgames.Tosuta-v0',
'flashgames.TouchTheBubbles4-v0',
'flashgames.TouchTheSky-v0',
'flashgames.TowerCollapseDeluxe-v0',
'flashgames.TowerEmpire-v0',
'flashgames.TowerEmpire2-v0',
'flashgames.TowerMoon-v0',
'flashgames.TowerOfPisa-v0',
'flashgames.ToyRacers-v0',
'flashgames.ToyWarAngryRobotDog-v0',
'flashgames.TractorTrial-v0',
'flashgames.TractorTrial2-v0',
'flashgames.TrafficCollision-v0',
'flashgames.TrickOrToad-v0',
'flashgames.TrickyRick-v0',
'flashgames.Trizzle-v0',
'flashgames.TrollingLionJump-v0',
'flashgames.TtRacer-v0',
'flashgames.TumbleTiles-v0',
'flashgames.Tumblestump2-v0',
'flashgames.TurboCrew-v0',
'flashgames.TurboRally-v0',
'flashgames.TurtleBreak-v0',
'flashgames.TutiFruti-v0',
'flashgames.TwinkleStarRush-v0',
'flashgames.Typeasaurus-v0',
'flashgames.UdderChaos-v0',
'flashgames.UltimateEscape-v0',
'flashgames.UltimateLegend-v0',
'flashgames.Underrun-v0',
'flashgames.UnderwaterSecrets-v0',
'flashgames.UnfreezeMe3-v0',
'flashgames.UrbanFatburner-v0',
'flashgames.UrbanMicroRacers-v0',
'flashgames.V8MuscleCars-v0',
'flashgames.V8MuscleCars2-v0',
'flashgames.V8MuscleCars3-v0',
'flashgames.V8RacingChampion-v0',
'flashgames.VanguardWars-v0',
'flashgames.VectorRunner-v0',
'flashgames.Velocity-v0',
'flashgames.VengeanceRider-v0',
'flashgames.VideoGameMonster-v0',
'flashgames.ViewtifulFightClub2-v0',
'flashgames.Viridia-v0',
'flashgames.VirtualRacer-v0',
'flashgames.VolcanoPanicInIsland-v0',
'flashgames.VolleyBomb-v0',
'flashgames.WackyStrike-v0',
'flashgames.WarBerlinIdle-v0',
'flashgames.WarHeroes-v0',
'flashgames.WarOfTheShard-v0',
'flashgames.WastelandSiege-v0',
'flashgames.WaveLucha-v0',
'flashgames.Weirdville-v0',
'flashgames.WhatsInsideTheBox-v0',
'flashgames.Wheelers-v0',
'flashgames.WhistleAndMice-v0',
'flashgames.WildWestConflict-v0',
'flashgames.WilliamTell-v0',
'flashgames.WindowShooter-v0',
'flashgames.WinterSlider-v0',
'flashgames.WishTotems-v0',
'flashgames.WishTotemsLevelPack-v0',
'flashgames.WizkidEscape-v0',
'flashgames.WolfSpiderJigsawPuzzle-v0',
'flashgames.WonderRocket-v0',
'flashgames.WoollyBearJigsawPuzzle-v0',
'flashgames.WorldsGuard2-v0',
'flashgames.WormHappy-v0',
'flashgames.WreckRoad-v0',
'flashgames.XChains-v0',
'flashgames.XmasChains-v0',
'flashgames.Xmatch2016-v0',
'flashgames.Xnake-v0',
'flashgames.YepisJourney-v0',
'flashgames.YummyyummyMonsterShooter-v0',
'flashgames.Zed-v0',
'flashgames.Zevil2-v0',
'flashgames.ZodiacMatch-v0',
'flashgames.Zombality-v0',
'flashgames.ZombieDemolisher3-v0',
'flashgames.ZombieMatch3-v0',
'flashgames.ZombieTdReborn-v0',
'flashgames.ZombieTowerDefenseReborn-v0',
'flashgames.ZombiesAndDonuts-v0',
'flashgames.ZombiesMustDie-v0',
'flashgames.ZombiesVsBrains-v0',
'flashgames.Zombonarium-v0',
'flashgames.ZooRacer-v0',
]:
register(
id=game,
entry_point='universe.wrappers:WrappedFlashgamesEnv',
max_episode_steps=20000,
tags={
'vnc': True,
'flashgames': True,
'runtime': 'flashgames',
'metadata_encoding': metadata_v1,
'action_probe': {
'type': 'key',
'value': 0x60,
}
},
)
register(
id='VNCNoopFlashgamesEnv-v0', # Special noop flashgame env
entry_point='universe.vnc:WrappedFlashgamesEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'flashgames': True,
'runtime': 'flashgames',
},
)
#------------------------ World of Bits and MiniWoB ------------------------#
# "World of Bits" comprises a series of browser tasks,
# including a series of simple "MiniWoB" tasks such
# as using buttons and sliders, as well as more complex
# tasks such as booking flights on actual websites.
vnc_world_of_bits = [
'wob.MiniWorldOfBits-v0',
'wob.mini.BisectAngle-v0',
'wob.mini.BookFlight-v0',
'wob.mini.ChaseCircle-v0',
'wob.mini.ChooseDate-v0',
'wob.mini.ChooseList-v0',
'wob.mini.CircleCenter-v0',
'wob.mini.ClickButton-v0',
'wob.mini.ClickButtonSequence-v0',
'wob.mini.ClickCheckboxes-v0',
'wob.mini.ClickCollapsible-v0',
'wob.mini.ClickCollapsible2-v0',
'wob.mini.ClickColor-v0',
'wob.mini.ClickDialog-v0',
'wob.mini.ClickDialog2-v0',
'wob.mini.ClickLink-v0',
'wob.mini.ClickMenu-v0',
'wob.mini.ClickMenu2-v0',
'wob.mini.ClickOption-v0',
'wob.mini.ClickPie-v0',
'wob.mini.ClickScrollList-v0',
'wob.mini.ClickShades-v0',
'wob.mini.ClickShape-v0',
'wob.mini.ClickTab-v0',
'wob.mini.ClickTab2-v0',
'wob.mini.ClickTest-v0',
'wob.mini.ClickTest2-v0',
'wob.mini.ClickWidget-v0',
'wob.mini.CopyPaste-v0',
'wob.mini.CopyPaste2-v0',
'wob.mini.CountShape-v0',
'wob.mini.CountSides-v0',
'wob.mini.DragBox-v0',
'wob.mini.DragCube-v0',
'wob.mini.DragItem-v0',
'wob.mini.DragItems-v0',
'wob.mini.DragItemsGrid-v0',
'wob.mini.DragShapes-v0',
'wob.mini.DragSortNumbers-v0',
'wob.mini.EmailInbox-v0',
'wob.mini.EnterDate-v0',
'wob.mini.EnterPassword-v0',
'wob.mini.EnterText-v0',
'wob.mini.EnterText2-v0',
'wob.mini.EnterTextDynamic-v0',
'wob.mini.EnterTime-v0',
'wob.mini.FindMidpoint-v0',
'wob.mini.FindWord-v0',
'wob.mini.FocusText-v0',
'wob.mini.FocusText2-v0',
'wob.mini.GridCoordinate-v0',
'wob.mini.GuessNumber-v0',
'wob.mini.HighlightText-v0',
'wob.mini.HighlightText2-v0',
'wob.mini.IdentifyShape-v0',
'wob.mini.LoginUser-v0',
'wob.mini.MovingItems-v0',
'wob.mini.NavigateTree-v0',
'wob.mini.NumberCheckboxes-v0',
'wob.mini.ReadTable-v0',
'wob.mini.ReadTable2-v0',
'wob.mini.ResizeTextarea-v0',
'wob.mini.RightAngle-v0',
'wob.mini.ScrollText-v0',
'wob.mini.ScrollText2-v0',
'wob.mini.SearchEngine-v0',
'wob.mini.SimonSays-v0',
'wob.mini.SimpleAlgebra-v0',
'wob.mini.SimpleArithmetic-v0',
'wob.mini.SocialMedia-v0',
'wob.mini.Terminal-v0',
'wob.mini.TextEditor-v0',
'wob.mini.TextTransform-v0',
'wob.mini.TicTacToe-v0',
'wob.mini.UseAutocomplete-v0',
'wob.mini.UseColorwheel-v0',
'wob.mini.UseColorwheel2-v0',
'wob.mini.UseSlider-v0',
'wob.mini.UseSlider2-v0',
'wob.mini.UseSpinner-v0',
'wob.mini.VisualAddition-v0',
]
# signup forms.
for _id in range(20):
vnc_world_of_bits.append('wob.real.Signup-{}-v0'.format(_id))
for _site in ['Jetblue', 'Kayak', 'AA', 'VirginAmerica',
'United', 'Delta', 'Alaska']:
vnc_world_of_bits.append('wob.real.BookFlight-{}-v0'.format(_site))
for _site in ['Airfrance', 'Craigslist', 'Chase']:
vnc_world_of_bits.append('wob.real.ClickButton-{}-v0'.format(_site))
for _task in ['Learn', 'Test']:
for _name in ['Geography', 'Planet', 'Universe', 'Comet', 'Moon', 'Mars', 'Solar-System']:
vnc_world_of_bits.append('wob.real.Quizlet-{}-{}-v0'.format(_name, _task))
vnc_world_of_bits.append('wob.real.Duolingo-French-Basic-1-v0')
for game in vnc_world_of_bits:
register(
id=game,
entry_point='universe.wrappers:WrappedVNCEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'wob': True,
'runtime': 'world-of-bits',
},
)
#-------------------------- Complex Games ------------------------#
# Any game, program, app, or website can be a
# Universe environment. Here we include
# a handful of sample "complex" games
# such as World of Bits, GTA V, and StarCraft.
# Adding more games is straightforward, and
# we welcome contributions of environments
# from the community!
for id in ['starcraft.TerranAstralBalance-v0']:
register(
id=id,
entry_point='universe.wrappers:WrappedStarCraftEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'starcraft': True,
'runtime': 'starcraft',
},
)
for gtav_game in ['gtav.SaneDriving-v0', 'gtav.Speed-v0']:
register(
id=gtav_game,
entry_point='universe.wrappers:WrappedGTAVEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'gtav': True,
'runtime': 'vnc-windows',
},
)
register(
id='world.WorldOfGoo-v0',
entry_point='universe.wrappers:WrappedWorldOfGooEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'wog': True,
'runtime': 'vnc-world-of-goo',
},
)
for slith_game in ['SlitherIO-v0', 'SlitherIONoSkins-v0', 'SlitherIOEasy-v0']:
register(
id='internet.' + slith_game,
entry_point='universe.wrappers:WrappedInternetEnv',
max_episode_steps=10**7,
tags={
'vnc': True,
'internet': True,
'slither': True,
'runtime': 'flashgames',
'metadata_encoding': metadata_v1,
'action_probe': {
'type': 'key',
'value': 0x60,
}
},
)
register(
id='test.DummyVNCEnv-v0',
entry_point='universe.envs:DummyVNCEnv',
max_episode_steps= 10**7,
tags={
'vnc': True,
'metadata_encoding': metadata_v1,
'action_probe': {
'type': 'key',
'value': 0x60,
}
},
)
================================================
FILE: universe/configuration.py
================================================
import logging
from gym import configuration
universe_logger = logging.getLogger('universe')
universe_logger.setLevel(logging.INFO)
extra_logger = logging.getLogger('universe.extra')
extra_logger.setLevel(logging.INFO)
if hasattr(configuration, '_extra_loggers'):
configuration._extra_loggers.append(universe_logger)
configuration._extra_loggers.append(extra_logger)
================================================
FILE: universe/envs/__init__.py
================================================
import universe.envs.vnc_env
from universe.envs.vnc_env import VNCEnv
from universe.envs.vnc_core_env import GymCoreEnv, GymCoreSyncEnv
from universe.envs.vnc_flashgames import FlashgamesEnv
from universe.envs.vnc_internet import InternetEnv
from universe.envs.vnc_starcraft import StarCraftEnv
from universe.envs.vnc_gtav import GTAVEnv
from universe.envs.vnc_wog import WorldOfGooEnv
from universe.envs.dummy_vnc_env import DummyVNCEnv
================================================
FILE: universe/envs/diagnostics.py
================================================
import collections
import fastzbarlight
import itertools
import logging
from multiprocessing import pool
import numpy as np
import time
import threading
# import psutil
import sys
from collections import namedtuple
from gym.utils import reraise
import re
from universe import error, pyprofile, spaces
# TODO: prefix the loggers
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
def show(ob):
from PIL import Image
Image.fromarray(ob).show()
def standard_error(ary, axis, scale=1):
ary = np.array(ary) * scale
if len(ary) > 1:
return np.std(ary, axis=axis) / np.sqrt(len(ary) - 1)
else:
return np.std(ary, axis=axis)
def extract_timestamp(observation):
total = 0
for byte in observation[0]:
total = 256 * total + byte
for byte in observation[1]:
total = 256 * total + byte
timestamp = total/1000.
return timestamp
class MetadataDecoder(object):
@classmethod
def build(cls, metadata_encoding, pool, qr_pool, label):
metadata_encoding = metadata_encoding.copy()
type = metadata_encoding.pop('type')
if type == 'qrcode':
return QRCodeMetadataDecoder(label=label, pool=pool, qr_pool=qr_pool, **metadata_encoding)
elif type == 'pixels':
return PixelsMetadataDecoder(label=label)
else:
raise error.Error('Invalid encoding: {}'.format(type))
class AsyncDecode(object):
pool = None
def __init__(self, pool, qr_pool, method, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self._last_img = None
self.method = method
self.results = []
self.deque = collections.deque()
self.pool = pool
self.qr_pool = qr_pool
def __call__(self, img, available_at):
# Choose the return value
if len(self.deque) > 0 and self.deque[0].ready():
last = self.deque.popleft()
res = last.get()
if res is not None:
pyprofile.timing('vnc_env.diagnostics.async_decode.latency', time.time() - res['available_at'])
else:
res = False
pyprofile.gauge('vnc_env.diagnostics.async_decode.queue_depth', len(self.deque))
# Just grayscale it by keeping only one component. Should be
# good enough as this region is black and white anyway.
grayscale = img[self.y:self.y+self.height, self.x:self.x+self.width, 0]
# Apply processing if needed
match = np.array_equal(self._last_img, grayscale)
if not match:
pyprofile.incr('vnc_env.diagnostics.async_decode.schedule')
# sneakily copy if numpy hasn't, so it can be cached
self._last_img = np.ascontiguousarray(grayscale)
async = self.qr_pool.apply_async(self.method, (self._last_img, time.time(), available_at))
self.deque.append(async)
else:
pyprofile.incr('vnc_env.diagnostics.async_decode.cache_hit')
return res
class QRCodeMetadataDecoder(MetadataDecoder):
def __init__(self, pool, qr_pool, x, y, width, height, label):
self.flag_synchronous = False
self.x = x
self.y = y
self.width = width
self.height = height
self.label = label
self.decode = AsyncDecode(pool, qr_pool, self._decode, x, y, width, height)
def _decode(self, observation, start, available_at):
# This method gets wrapped by AsyncDecode.__call__
with pyprofile.push('vnc_env.diagnostics.QRCodeMetadataDecoder.qr_code_scanner'):
encoded = fastzbarlight.qr_code_scanner(observation.tobytes(), self.width, self.height)
if encoded is None:
# Failed to parse!
return
if encoded.startswith(b'v1:'):
encoded = encoded.decode('utf-8')
if len(encoded) != len('v1:') + 12 + 12:
raise error.Error('Bad length for metadata from enviroment: {}'.format(encoded))
encoded = encoded[len('v1:'):]
last_update = int(encoded[:12], 16) / 1000.0
last_action = int(encoded[12:24], 16) / 1000.
return {
# Timestamp on the image
'now': last_update,
# When the last probe was received
'probe_received_at': last_action,
'processing_start': start,
'processing_end': time.time(),
'available_at': available_at,
}
else:
raise error.Error('Bad version string for metadata from environment: {}'.format(encoded))
class PixelsMetadataDecoder(MetadataDecoder):
def __init__(self, label):
self.flag_synchronous = True
self.anchor = np.array([
[(0x12, 0x34, 0x56), (0x78, 0x90, 0xab)],
[(0x23, 0x45, 0x67), (0x89, 0x0a, 0xbc)],
], dtype=np.uint8)
self.location = None
self.last_search_metadata = 0
self.label = label
def _check_location(self, observation, location):
y, x = location
return np.all(observation[y:y+2, x:x+2] == self.anchor)
def _find_metadata_location(self, observation):
ys, xs = np.where(np.all(observation == self.anchor[0, 0], axis=-1))
if len(ys) == 0:
extra_logger.info('[%s] Could not find metadata anchor pixel', self.label)
return False
# TODO: handle multiple hits
assert len(ys) == 1
location = (ys[0], xs[0])
assert self._check_location(observation, location)
extra_logger.info('[%s] Found metadata anchor pixel: %s', self.label, location)
return location
def _should_search_metadata(self):
return time.time() - self.last_search_metadata > 1
def decode(self, observation, available_at=None):
start = time.time()
# metadata pixel location hasn't been initialized or it has moved
if not self.location or not self._check_location(observation,
self.location):
# only search for metadata occasionally
if self._should_search_metadata():
self.location = self._find_metadata_location(observation)
self.last_search_metadata = time.time()
if not self.location:
return False # False translates to None in DiagnosticsInstance
y, x = self.location
now = extract_timestamp(observation[y, x+2:x+4])
probe_received_at = extract_timestamp(observation[y, x+4:x+6])
return {
'now': now,
'probe_received_at': probe_received_at,
'processing_start': start,
'processing_end': time.time(),
'available_at': available_at,
}
class Diagnostics(object):
def __init__(self, n, probe_key, ignore_clock_skew=False, metadata_encoding=None, disable_action_probes=False):
# Each QR code takes about 1ms (and updates at 5fps). We do
# our best to ensure the QR is processed in time for the next
# step call (n/16 would put us right at the threshold).
self.pool = pool.ThreadPool(max(int(n/4), 1))
self.qr_pool = pool.ThreadPool(max(int(n/8), 1))
self.lock = threading.RLock()
self.instance_n = [None] * n
self.ignore_clock_skew = ignore_clock_skew
self.disable_action_probes = disable_action_probes
self.metadata_encoding = metadata_encoding
self.update(probe_key=probe_key, metadata_encoding=metadata_encoding)
# only used in flashgames right now
def update(self, probe_key, metadata_encoding):
self.probe_key = probe_key
self.metadata_encoding = metadata_encoding
for instance in self.instance_n:
if instance is not None:
instance.update(probe_key=self.probe_key, metadata_encoding=self.metadata_encoding)
def connect(self, i, network=None, label=None):
# This should technically be synchronized
self.instance_n[i] = DiagnosticsInstance(i, network, self.probe_key, self.ignore_clock_skew, self.metadata_encoding, disable_action_probes=self.disable_action_probes, qr_pool=self.qr_pool, pool=self.pool, label=label)
def close(self, i=None):
if i is not None:
self.instance_n[i] = None
else:
self.pool.close()
self.qr_pool.close()
for i in range(len(self.instance_n)):
self.close(i)
self.instance_n = None
def add_probe(self, action_n, mask_n):
if self.disable_action_probes or self.instance_n is None:
return
for instance, action, mask in zip(self.instance_n, action_n, mask_n):
# Important that masking prevents us from adding probes. (This
# avoids us e.g. filling in backticks into text boxes as the
# environment boots.)
if mask and instance:
instance.add_probe(action)
def add_metadata(self, observation_n, info_n, available_at=None):
"""Mutates the info_n dictionary."""
if self.instance_n is None:
return
with pyprofile.push('vnc_env.diagnostics.Diagnostics.add_metadata'):
async = self.pool.imap_unordered(
self._add_metadata_i,
zip(self.instance_n, observation_n, info_n, [available_at] * len(observation_n)))
list(async)
def _add_metadata_i(self, args):
instance, observation, info, now = args
if instance is None or observation is None:
return
instance.add_metadata(observation, info, now)
def extract_metadata(self, observation_n):
return [instance._extract_metadata(observation)
for instance, observation in zip(self.instance_n, observation_n)]
def clear_probes_when_done(self, done_n):
if self.instance_n is None: # if we've been closed there's nothing to do
return
for instance, done in zip(self.instance_n, done_n):
if done:
instance.clear_probe()
class DiagnosticsInstance(object):
anchor = np.array([
[(0x12, 0x12, 0x12), (0x78, 0x78, 0x78)],
[(0x23, 0x23, 0x23), (0x89, 0x89, 0x89)],
], dtype=np.uint8)
zero_clock_skew = np.zeros([2])
def __init__(self, i, network, probe_key, ignore_clock_skew, metadata_encoding, disable_action_probes, pool, qr_pool, label=None):
'''
network - either Network() object used to get clock skew, or None.
If None, we skip measuring clock skew, and skip measuring
diagnostics which rely on clock skew.
'''
if network is None:
assert ignore_clock_skew
self.ignore_clock_skew = ignore_clock_skew
self.label = label
self.i = i
self.network = network
self.probe_sent_at = None # local time
self.probe_received_at = None # remote time
self.action_latency_skewed = None
self.last_observation_timestamp = None
self.disable_action_probes = disable_action_probes
self.pool = pool
self.qr_pool = qr_pool
self.could_read_metadata = None
self.update(probe_key=probe_key, metadata_encoding=metadata_encoding)
def update(self, probe_key, metadata_encoding):
self.probe = [
spaces.KeyEvent(probe_key, down=True).compile(),
spaces.KeyEvent(probe_key, down=False).compile(),
]
if metadata_encoding is not None:
self.metadata_decoder = MetadataDecoder.build(metadata_encoding, pool=self.pool, qr_pool=self.qr_pool, label=self.label)
else:
self.metadata_decoder = None
def clear_probe(self):
self.probe_sent_at = None
self.probe_received_at = None
def add_probe(self, action):
if self.network is not None and not self.network.active():
return
if self.probe_sent_at is not None and self.probe_sent_at + 10 < time.time():
extra_logger.warn('[%s] Probe to determine action latency timed out (was sent %s). (This is harmless, but worth knowing about.)', self.label, self.probe_sent_at)
self.probe_sent_at = None
if self.probe_sent_at is None:
extra_logger.debug('[%s] Sending out new action probe: %s', self.label, self.probe)
self.probe_sent_at = time.time()
action += self.probe
assert self.probe_sent_at is not None
def add_metadata(self, observation, info, available_at=None):
"""Extract metadata from a pixel observation and add it to the info
"""
observation = observation['vision']
if observation is None: return
if self.network is not None and not self.network.active():
return
elif self.metadata_decoder is None:
return
elif observation is None:
return
# should return a dict with now/probe_received_at keys
with pyprofile.push('vnc_env.diagnostics.DiagnosticsInstance.add_metadata.decode'):
metadata = self.metadata_decoder.decode(observation, available_at=available_at)
if metadata is False:
# No metadata ready, though it doesn't mean parsing failed
metadata = None
elif metadata is None:
if self.could_read_metadata:
self.could_read_metadata = False
extra_logger.info('[%s] Stopped being able to read metadata (expected when environment resets)', self.label)
elif not self.could_read_metadata:
self.could_read_metadata = True
extra_logger.info('[%s] Started being able to read metadata', self.label)
if self.metadata_decoder.flag_synchronous and metadata is not None:
info['diagnostics.image_remote_time'] = metadata['now']
local_now = time.time()
if self.network is None:
# Assume the clock skew is zero. Should only be run on the
# same machine as the VNC server, such as the universe
# instance inside of the environmenth containers.
real_clock_skew = self.zero_clock_skew
else:
# Note: this is a 2-length vector of (min, max), so anything added to
# it is also going to be a 2-length vector.
# Most of the diagnostics below are, but you have to look carefully.
real_clock_skew = self.network.reversed_clock_skew()
# Store real clock skew here
info['stats.gauges.diagnostics.clock_skew'] = real_clock_skew
if self.ignore_clock_skew:
clock_skew = self.zero_clock_skew
else:
clock_skew = real_clock_skew
if metadata is not None:
# We'll generally update the observation timestamp infrequently
if self.last_observation_timestamp == metadata['now']:
delta = None
else:
# We just got a new timestamp in the observation!
self.last_observation_timestamp = metadata['now']
observation_now = metadata['now']
delta = observation_now - metadata['available_at']
# Subtract *local* time it was received from the *remote* time
# displayed. Negate and reverse order to fix time ordering.
info['stats.gauges.diagnostics.lag.observation'] = -(delta + clock_skew)[[1, 0]]
# if self.network is None:
# # The rest of diagnostics need the network, so we're done here
# return
probe_received_at = metadata['probe_received_at']
if probe_received_at == 0 or self.disable_action_probes:
# Happens when the env first starts
self.probe_received_at = None
elif self.probe_received_at is None: # this also would work for the equality case
self.probe_received_at = probe_received_at
elif self.probe_received_at != probe_received_at and self.probe_sent_at is None:
logger.info('[%s] Probe is marked as received at %s, but probe_sent_at is None. This is surprising. (HINT: do you have multiple universe instances talking to the same environment?)', self.label, probe_received_at)
elif self.probe_received_at != probe_received_at:
extra_logger.debug('[%s] Next probe received: old=%s new=%s', self.label, self.probe_received_at, probe_received_at)
self.probe_received_at = probe_received_at
# Subtract the *local* time we sent it from the *remote* time it was received
self.action_latency_skewed = probe_received_at - self.probe_sent_at
self.probe_sent_at = None
if self.action_latency_skewed:
action_lag = self.action_latency_skewed + clock_skew
self.action_latency_skewed = None
else:
action_lag = None
info['stats.gauges.diagnostics.lag.action'] = action_lag
local_now = time.time()
# Look at when the remote believed it parsed the score (not
# all envs send this currently).
#
# Also, if we received no new rewards, then this values is
# None. This could indicate a high reward latency (bad,
# uncommon), or that the agent is calling step faster than new
# rewards are coming in (good, common).
remote_score_now = info.get('rewarder.lag.observation.timestamp')
if remote_score_now is not None:
delta = remote_score_now - local_now
info['stats.gauges.diagnostics.lag.reward'] = -(delta + clock_skew)[[1, 0]]
# Look at when the remote send the message, so we know how
# long it's taking for messages to get to us.
rewarder_message_now = info.get('reward_buffer.remote_time')
if rewarder_message_now:
delta = rewarder_message_now - local_now
info['stats.gauges.diagnostics.lag.rewarder_message'] = -(delta + clock_skew)[[1, 0]]
def extract_n_m(dict_n_m, key):
output = []
for dict_n in dict_n_m:
layer = []
for dict in dict_n:
layer.append(dict[key])
output.append(layer)
return np.array(output)
# class ChromeProcessInfo(object):
# proc_regex = re.compile('.*(chrome|Chrome|nacl_helper).*')
# def add_system_stats(self, info, now):
# """TODO: This needs be moved to universe-envs and run there. Otherwise it only works if the env and agent
# are on the same machine. In addition a new rpc call, rpc.env.diagnostics, should be added to return
# data to the agent periodically.
# """
# start = time.time()
# # CPU
# cpu_percent = psutil.cpu_percent()
# info['diagnostics.env.cpu.percent'] = cpu_percent
# cpu_cores_percent = psutil.cpu_percent(percpu=True)
# num_cores = len(cpu_cores_percent)
# info['diagnostics.env.cpu.percent.all_cores'] = cpu_percent / num_cores
# info['diagnostics.env.cpu.percent.each_core'] = cpu_cores_percent
# info['diagnostics.env.cpu.num_cores'] = num_cores
# # MEMORY
# mem = psutil.virtual_memory()
# info['diagnostics.env.memory.percent'] = mem.percent
# info['diagnostics.env.memory.total'] = mem.total
# info['diagnostics.env.memory.available'] = mem.available
# # NETWORK
# if self.last_measured_at is not None:
# elapsed_ms = (now - self.last_measured_at) * 1000.
# current = psutil.net_io_counters()
# dl = (current.bytes_recv - self.system_network_counters.bytes_recv) / elapsed_ms
# ul = (current.bytes_sent - self.system_network_counters.bytes_sent) / elapsed_ms
# info['diagnostics.env.network.download_bytes_ps'] = dl * 1000.
# info['diagnostics.env.network.upload_bytes_ps'] = ul * 1000.
# self.system_network_counters = current
# # CHROME
# if self.chrome_last_measured_at is None or (time.time() - self.chrome_last_measured_at) > 30:
# # Fetch every 30 seconds
# self.chrome_last_measured_at = time.time()
# logger.info("Measuring Chrome process statistics")
# chrome_info = ChromeProcessInfo()
# chrome_info = best_effort(chrome_info.fetch, num_cores)
# if chrome_info is not None:
# self.chrome_info = chrome_info
# if self.chrome_info is not None:
# self._populate_chrome_info(self.chrome_info, info)
# # TODO: Add GPU stats
# pyprofile.push('diagnostics.system_stats')
# def _populate_chrome_info(self, chrome_info, info):
# pyprofile.push('diagnostics.chrome_process_info.process_iter')
# pyprofile.push('diagnostics.chrome_process_info.total')
# info['diagnostics.chrome.age'] = chrome_info.age
# info['diagnostics.chrome.cpu.time'] = chrome_info.cpu_time
# info['diagnostics.chrome.cpu.percent'] = chrome_info.cpu_percent
# info['diagnostics.chrome.cpu.percent.all_cores'] = chrome_info.cpu_percent_all_cores
# info['diagnostics.chrome.cpu.percent.all_cores_all_time'] = chrome_info.cpu_percent_all_cores_all_time
# info['diagnostics.chrome.num_processes'] = len(chrome_info.processes)
# def __init__(self):
# self.cpu_time = 0.
# self.cpu_percent = 0.
# self.min_create_time = None
# self.visited_pids = set()
# self.processes = []
# self.time_to_get_procs = None
# self.total_time_to_measure = None
# self.age = None
# self.cpu_percent_all_cores_all_time = None
# self.cpu_percent_all_cores = None
# def fetch(self, num_cores):
# start = time.time()
# start_process_iter = time.time()
# procs = list(psutil.process_iter())
# self.time_to_get_procs = time.time() - start_process_iter
# for proc in procs:
# try:
# name = proc.name()
# if self.proc_regex.match(name):
# self._fetch_single(proc, name)
# # N.B. Don't read children. defunct processes make this take 4ever.
# # Child processes are all uncovered by initial scan.
# except (psutil.AccessDenied, psutil.NoSuchProcess) as e:
# pass
# self.total_time_to_measure = time.time() - start
# if self.min_create_time is None:
# self.age = 0
# else:
# self.age = time.time() - self.min_create_time
# self.cpu_percent_all_cores_all_time = 100. * self.cpu_time / (self.age * num_cores)
# self.cpu_percent_all_cores = self.cpu_percent / num_cores
# return self
# def _fetch_single(self, proc, name):
# if proc.pid in self.visited_pids:
# return
# try:
# cpu_times = proc.cpu_times()
# cpu_percent = proc.cpu_percent()
# created = proc.create_time()
# if self.min_create_time is None:
# self.min_create_time = created
# else:
# self.min_create_time = min(created, self.min_create_time)
# cpu_time = cpu_times.user + cpu_times.system
# proc_info = namedtuple('proc_info', 'name cpu_time cpu_percent created age')
# proc_info.name = name
# proc_info.cpu_time = cpu_time
# proc_info.cpu_percent = cpu_percent
# proc_info.created = created
# proc_info.age = time.time() - created
# proc_info.pid = proc.pid
# self.processes.append(proc_info)
# # Totals
# self.cpu_time += cpu_time
# self.cpu_percent += cpu_percent
# self.visited_pids.add(proc.pid)
# except (psutil.AccessDenied, psutil.NoSuchProcess) as e:
# pass
================================================
FILE: universe/envs/dummy_vnc_env.py
================================================
import logging
import numpy as np
from gym.utils import reraise
from universe import error, rewarder, spaces, utils, vectorized
from universe.envs import diagnostics
from universe.remotes import healthcheck
from universe.runtimes import registration
class DummyVNCEnv(vectorized.Env):
"""
A simple env for unit testing that does nothing, but looks like a VNC env.
It accepts any actions, and returns black screens.
It also returns the actions in the observation, so you can test that action wrappers are producing the right answers
For example, to test that YourActionWrapper converts example_input_action to example_output_action:
>>> dummy_env = gym.make('test.DummyVNCEnv-v0')
>>> e = YourActionWrapper(dummy_env)
>>> e = universe.wrappers.Unvectorize(e)
>>> observation, reward, done, info = e.step(example_input_action)
>>> assert observation['action'] == example_output_action
"""
metadata = {
'render.modes': ['human'], # we wrap with a Render which can render to rgb_array
'semantics.async': True,
'semantics.autoreset': True,
'video.frames_per_second' : 60,
'runtime.vectorized': True,
}
def __init__(self):
self._started = False
self.observation_space = spaces.VNCObservationSpace()
self.action_space = spaces.VNCActionSpace()
def configure(self, remotes=None,
client_id=None,
start_timeout=None, docker_image=None,
ignore_clock_skew=False, disable_action_probes=False,
vnc_driver=None, vnc_kwargs={},
replace_on_crash=False, allocate_sync=True,
observer=False,
_n=3,
):
self.n = _n
self._reward_buffers = [rewarder.RewardBuffer('dummy:{}'.format(i)) for i in range(self.n)]
self._started = True
def _reset(self):
return [None] * self.n
def _step(self, action_n):
assert self.n == len(action_n), "Expected {} actions but received {}: {}".format(self.n, len(action_n), action_n)
observation_n = [{
'vision': np.zeros((1024, 768, 3), dtype=np.uint8),
'text': [],
'action': action_n[i]
} for i in range(self.n)]
reward_n = []
done_n = []
info_n = []
for reward_buffer in self._reward_buffers:
reward, done, info = reward_buffer.pop()
reward_n.append(reward)
done_n.append(done)
info_n.append(info)
return observation_n, reward_n, done_n, {'n': info_n}
def __str__(self):
return 'DummyVNCEnv'
================================================
FILE: universe/envs/tests/__init__.py
================================================
================================================
FILE: universe/envs/tests/test_semantics.py
================================================
# import numpy as np
# from universe import vectorized
# class SimpleEnv(vectorized.Env):
# def _step(self, action_n):
# return {'vision': np.zeros((10, 10))}, 10, False, {}
# from universe.envs import vnc_env
import gym
import numpy as np
import os
import universe
from autobahn.twisted import websocket
from PIL import Image
from universe.rewarder import rewarder_client, rewarder_session, reward_buffer
from universe import spaces, wrappers
def get_rewarder_session(env):
return env.unwrapped.rewarder_session
def get_vnc_session(env):
return env.unwrapped.vnc_session
def get_rewarder_client(env):
rewarder_session = get_rewarder_session(env)
return rewarder_session.connections['0'].rewarder_client
def get_reward_buffer(env):
rewarder_session = get_rewarder_session(env)
return rewarder_session.connections['0'].reward_buffer
def setup_module(module):
universe.configure_logging('-')
class FakeVNCConnection(object):
def __init__(self, name, address, password, encoding=None, fine_quality_level=None, subsample_level=None, start_timeout=None):
self.name = name
self.address = address
self.password = password
self.encoding = encoding
self.fine_quality_level = fine_quality_level
self.subsample_level = subsample_level
self.start_timeout = start_timeout
self._frame = np.array(Image.open(os.path.join(os.path.dirname(__file__), 'dusk-drive.png')))
def step(self, action):
info_d = {}
return self._frame, info_d
def _to_dict(self):
return {
'name': self.name,
'address': self.address,
'password': self.password,
'encoding': self.encoding,
'fine_quality_level': self.fine_quality_level,
'subsample_level': self.subsample_level,
'start_timeout': self.start_timeout,
}
class FakeVNCSession(object):
def __init__(self):
self.connections = {}
def connect(self, name, **kwargs):
self.connections[name] = FakeVNCConnection(name=name, **kwargs)
def close(self, name=None):
if name is not None:
del self.connections[name]
else:
self.connections = None
def step(self, action_d):
observation_d = {}
info_d = {}
err_d = {}
for name, conn in self.connections.items():
observation, info = conn.step(action_d.get(name, []))
observation_d[name] = observation
info_d[name] = info
return observation_d, info_d, err_d
def _to_dict(self):
return {name: conn._to_dict() for name, conn in self.connections.items()}
class FakeRewarderConnection(object):
def __init__(self, name, address, label, password, env_id=None, seed=None, fps=60,
start_timeout=None, observer=False, skip_network_calibration=False):
self.name = name
self.address = address
self.label = label
self.password = password
self.env_id = env_id
self.seed = None
self.fps = fps
self.start_timeout = start_timeout
self.observer = observer
self.skip_network_calibration = skip_network_calibration
self.reward_buffer = reward_buffer.RewardBuffer(label=self.label)
factory = websocket.WebSocketClientFactory('ws://'+address)
factory.reward_buffer = self.reward_buffer
factory.label = self.label
self.rewarder_client = rewarder_client.RewarderClient()
self.rewarder_client.factory = factory
self.rewarder_client.onConnect(None)
def reset(self, seed=None):
self.seed = seed
def pop(self, peek=False):
return self.reward_buffer.pop(peek=peek)
def _to_dict(self):
return {
'name': self.name,
'address': self.address,
'label': self.label,
'password': self.password,
'env_id': self.env_id,
'seed': self.seed,
'fps': self.fps,
'start_timeout': self.start_timeout,
'observer': self.observer,
'skip_network_calibration': self.skip_network_calibration,
}
class FakeRewarder(object):
def __init__(self):
self.connections = {}
def reset(self, seed=None, **kwargs):
for conn in self.connections.values():
conn.reset(seed=seed)
def connect(self, name, **kwargs):
self.connections[name] = FakeRewarderConnection(name=name, **kwargs)
return rewarder_session.Network()
def close(self, name=None):
if name is not None:
del self.connections[name]
else:
self.connections = None
def pop(self, peek_d):
reward_d = {}
done_d = {}
info_d = {}
err_d = {}
for name, conn in self.connections.items():
reward, done, info = conn.pop(peek=peek_d.get(name))
reward_d[name] = reward
done_d[name] = done
info_d[name] = info
return reward_d, done_d, info_d, err_d
def _to_dict(self):
return {name: conn._to_dict() for name, conn in self.connections.items()}
def test_connect():
env = gym.make('flashgames.DuskDrive-v0')
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
vnc_session = get_vnc_session(env)
rewarder_session = get_rewarder_session(env)
assert vnc_session._to_dict() == {'0': {'name': '0', 'subsample_level': 2, 'encoding': 'tight', 'fine_quality_level': 50, 'start_timeout': 7, 'address': 'example.com:5900', 'password': 'openai'}}
assert rewarder_session._to_dict() == {'0': {'start_timeout': 7, 'seed': None, 'name': '0', 'fps': 60, 'address': 'example.com:15900', 'env_id': 'flashgames.DuskDrive-v0', 'password': 'openai', 'skip_network_calibration': False, 'observer': False, 'label': '0:example.com:5900'}}
def test_describe_handling():
env = gym.make('flashgames.DuskDrive-v0')
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
env.reset()
reward_buffer = get_reward_buffer(env)
rewarder_client = get_rewarder_client(env)
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '1'})
assert reward_buffer._remote_episode_id == '1'
assert reward_buffer._remote_env_state == 'resetting'
assert reward_buffer._current_episode_id == None
assert reward_buffer.reward_state(reward_buffer._current_episode_id)._env_state == None
rewarder_client._manual_recv('v0.reply.env.reset', {}, {'episode_id': '1'})
assert reward_buffer._remote_episode_id == '1'
assert reward_buffer._remote_env_state == 'resetting'
assert reward_buffer._current_episode_id == '1'
assert reward_buffer.reward_state(reward_buffer._current_episode_id)._env_state == 'resetting'
def test_vnc_env():
env = gym.make('flashgames.DuskDrive-v0')
env = wrappers.Unvectorize(env)
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
env.reset()
rewarder_client = get_rewarder_client(env)
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '1'})
observation, reward, done, info = env.step([spaces.KeyEvent.by_name('a', down=True)])
assert (observation, reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (None, 0, False, None, None)
rewarder_client._manual_recv('v0.reply.env.reset', {}, {'episode_id': '1'})
observation, reward, done, info = env.step([spaces.KeyEvent.by_name('a', down=True)])
assert (observation, reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (None, 0, False, 'resetting', '1')
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'running', 'fps': 60}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 10, 'done': False, 'info': {}}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 15, 'done': False, 'info': {}}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.reward', {'reward': -3, 'done': False, 'info': {}}, {'episode_id': '1'})
observation, reward, done, info = env.step([spaces.KeyEvent.by_name('a', down=True)])
assert sorted(observation.keys()) == ['text', 'vision']
assert observation['text'] == []
assert observation['vision'].shape == (768, 1024, 3)
assert (reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (22, False, 'running', '1')
assert info['stats.reward.count'] == 3
def test_boundary_simple():
env = gym.make('flashgames.DuskDrive-v0')
env = wrappers.Unvectorize(env)
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
env.reset()
rewarder_client = get_rewarder_client(env)
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.reply.env.reset', {}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 1, 'done': False, 'info': {}}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 2, 'done': True, 'info': {}}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '2'})
# We have reward of 3 for episode 1, and episode 2 should now be resetting
observation, reward, done, info = env.step([])
assert info['mask.masked.observation']
assert info['mask.masked.action']
assert (reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (3, True, 'resetting', '2')
def test_boundary_multiple():
env = gym.make('flashgames.DuskDrive-v0')
env = wrappers.Unvectorize(env)
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
env.reset()
rewarder_client = get_rewarder_client(env)
# episode 2
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '2'})
rewarder_client._manual_recv('v0.reply.env.reset', {}, {'episode_id': '2'})
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'running', 'fps': 60}, {'episode_id': '2'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 2, 'done': True, 'info': {}}, {'episode_id': '2'})
# episode 3
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '3'})
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'running', 'fps': 60}, {'episode_id': '3'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 3, 'done': True, 'info': {}}, {'episode_id': '3'})
# episode 4
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '4'})
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'running', 'fps': 60}, {'episode_id': '4'})
rewarder_client._manual_recv('v0.env.reward', {'reward': 4, 'done': False, 'info': {}}, {'episode_id': '4'})
observation, reward, done, info = env.step([])
assert not info.get('mask.masked.observation')
assert not info.get('mask.masked.action')
assert (reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (2, True, 'running', '4')
assert (info['env_status.complete.env_state'], info['env_status.complete.episode_id']) == ('running', '2')
observation, reward, done, info = env.step([])
assert (reward, done, info['env_status.env_state'], info['env_status.episode_id']) == (4, False, 'running', '4')
def test_peek():
env = gym.make('flashgames.DuskDrive-v0')
env = wrappers.Unvectorize(env)
env.configure(vnc_driver=FakeVNCSession, rewarder_driver=FakeRewarder, remotes='vnc://example.com:5900+15900')
env.reset()
rewarder_client = get_rewarder_client(env)
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '1'})
rewarder_client._manual_recv('v0.reply.env.reset', {}, {'episode_id': '1'})
observation, reward, done, info = env.step([spaces.PeekReward])
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'resetting', 'fps': 60}, {'episode_id': '2'})
observation, reward, done, info = env.step([spaces.PeekReward])
assert info['mask.masked.observation']
assert info['mask.masked.action']
assert info['env_status.episode_id'] == '1'
assert info['env_status.env_state'] == 'resetting'
assert info['env_status.peek.episode_id'] == '2'
assert info['env_status.peek.env_state'] == 'resetting'
rewarder_client._manual_recv('v0.env.describe', {'env_id': 'flashgames.DuskDrive-v0', 'env_state': 'running', 'fps': 60}, {'episode_id': '2'})
observation, reward, done, info = env.step([spaces.PeekReward])
assert not info.get('mask.masked.observation')
assert not info.get('mask.masked.action')
assert info['env_status.episode_id'] == '1'
assert info['env_status.env_state'] == 'resetting'
assert info['env_status.peek.episode_id'] == '2'
assert info['env_status.peek.env_state'] == 'running'
================================================
FILE: universe/envs/vnc_core_env/__init__.py
================================================
from universe.envs.vnc_core_env.vnc_core_env import GymCoreEnv, GymCoreSyncEnv
from universe.envs.vnc_core_env.translator import AtariTranslator, CartPoleTranslator
================================================
FILE: universe/envs/vnc_core_env/key.py
================================================
# imported from Pyglet
# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2008 Alex Holkner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of pyglet nor the names of its
# contributors may be used to endorse or promote products
# derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------
'''Key constants and utilities for pyglet.window.
Usage::
from pyglet.window import Window
from pyglet.window import key
window = Window()
@window.event
def on_key_press(symbol, modifiers):
# Symbolic names:
if symbol == key.RETURN:
# Alphabet keys:
elif symbol == key.Z:
# Number keys:
elif symbol == key._1:
# Number keypad keys:
elif symbol == key.NUM_1:
# Modifiers:
if modifiers & key.MOD_CTRL:
'''
__docformat__ = 'restructuredtext'
__version__ = '$Id$'
class KeyStateHandler(dict):
'''Simple handler that tracks the state of keys on the keyboard. If a
key is pressed then this handler holds a True value for it.
For example::
>>> win = window.Window
>>> keyboard = key.KeyStateHandler()
>>> win.push_handlers(keyboard)
# Hold down the "up" arrow...
>>> keyboard[key.UP]
True
>>> keyboard[key.DOWN]
False
'''
def on_key_press(self, symbol, modifiers):
self[symbol] = True
def on_key_release(self, symbol, modifiers):
self[symbol] = False
def __getitem__(self, key):
return self.get(key, False)
def modifiers_string(modifiers):
'''Return a string describing a set of modifiers.
Example::
>>> modifiers_string(MOD_SHIFT | MOD_CTRL)
'MOD_SHIFT|MOD_CTRL'
:Parameters:
`modifiers` : int
Bitwise combination of modifier constants.
:rtype: str
'''
mod_names = []
if modifiers & MOD_SHIFT:
mod_names.append('MOD_SHIFT')
if modifiers & MOD_CTRL:
mod_names.append('MOD_CTRL')
if modifiers & MOD_ALT:
mod_names.append('MOD_ALT')
if modifiers & MOD_CAPSLOCK:
mod_names.append('MOD_CAPSLOCK')
if modifiers & MOD_NUMLOCK:
mod_names.append('MOD_NUMLOCK')
if modifiers & MOD_SCROLLLOCK:
mod_names.append('MOD_SCROLLLOCK')
if modifiers & MOD_COMMAND:
mod_names.append('MOD_COMMAND')
if modifiers & MOD_OPTION:
mod_names.append('MOD_OPTION')
if modifiers & MOD_FUNCTION:
mod_names.append('MOD_FUNCTION')
return '|'.join(mod_names)
def symbol_string(symbol):
'''Return a string describing a key symbol.
Example::
>>> symbol_string(BACKSPACE)
'BACKSPACE'
:Parameters:
`symbol` : int
Symbolic key constant.
:rtype: str
'''
if symbol < 1 << 32:
return _key_names.get(symbol, str(symbol))
else:
return 'user_key(%x)' % (symbol >> 32)
def motion_string(motion):
'''Return a string describing a text motion.
Example::
>>> motion_string(MOTION_NEXT_WORD):
'MOTION_NEXT_WORD'
:Parameters:
`motion` : int
Text motion constant.
:rtype: str
'''
return _motion_names.get(motion, str(motion))
def user_key(scancode):
'''Return a key symbol for a key not supported by pyglet.
This can be used to map virtual keys or scancodes from unsupported
keyboard layouts into a machine-specific symbol. The symbol will
be meaningless on any other machine, or under a different keyboard layout.
Applications should use user-keys only when user explicitly binds them
(for example, mapping keys to actions in a game options screen).
'''
assert scancode > 0
return scancode << 32
# Modifier mask constants
MOD_SHIFT = 1 << 0
MOD_CTRL = 1 << 1
MOD_ALT = 1 << 2
MOD_CAPSLOCK = 1 << 3
MOD_NUMLOCK = 1 << 4
MOD_WINDOWS = 1 << 5
MOD_COMMAND = 1 << 6
MOD_OPTION = 1 << 7
MOD_SCROLLLOCK = 1 << 8
MOD_FUNCTION = 1 << 9
#: Accelerator modifier. On Windows and Linux, this is ``MOD_CTRL``, on
#: Mac OSX it's ``MOD_COMMAND``.
MOD_ACCEL = MOD_CTRL
# gdb: don't need this import
# from pyglet import compat_platform
# if compat_platform == 'darwin':
# MOD_ACCEL = MOD_COMMAND
# Key symbol constants
# ASCII commands
BACKSPACE = 0xff08
TAB = 0xff09
LINEFEED = 0xff0a
CLEAR = 0xff0b
RETURN = 0xff0d
ENTER = 0xff0d # synonym
PAUSE = 0xff13
SCROLLLOCK = 0xff14
SYSREQ = 0xff15
ESCAPE = 0xff1b
SPACE = 0xff20
# Cursor control and motion
HOME = 0xff50
LEFT = 0xff51
UP = 0xff52
RIGHT = 0xff53
DOWN = 0xff54
PAGEUP = 0xff55
PAGEDOWN = 0xff56
END = 0xff57
BEGIN = 0xff58
# Misc functions
DELETE = 0xffff
SELECT = 0xff60
PRINT = 0xff61
EXECUTE = 0xff62
INSERT = 0xff63
UNDO = 0xff65
REDO = 0xff66
MENU = 0xff67
FIND = 0xff68
CANCEL = 0xff69
HELP = 0xff6a
BREAK = 0xff6b
MODESWITCH = 0xff7e
SCRIPTSWITCH = 0xff7e
FUNCTION = 0xffd2
# Text motion constants: these are allowed to clash with key constants
MOTION_UP = UP
MOTION_RIGHT = RIGHT
MOTION_DOWN = DOWN
MOTION_LEFT = LEFT
MOTION_NEXT_WORD = 1
MOTION_PREVIOUS_WORD = 2
MOTION_BEGINNING_OF_LINE = 3
MOTION_END_OF_LINE = 4
MOTION_NEXT_PAGE = PAGEDOWN
MOTION_PREVIOUS_PAGE = PAGEUP
MOTION_BEGINNING_OF_FILE = 5
MOTION_END_OF_FILE = 6
MOTION_BACKSPACE = BACKSPACE
MOTION_DELETE = DELETE
# Number pad
NUMLOCK = 0xff7f
NUM_SPACE = 0xff80
NUM_TAB = 0xff89
NUM_ENTER = 0xff8d
NUM_F1 = 0xff91
NUM_F2 = 0xff92
NUM_F3 = 0xff93
NUM_F4 = 0xff94
NUM_HOME = 0xff95
NUM_LEFT = 0xff96
NUM_UP = 0xff97
NUM_RIGHT = 0xff98
NUM_DOWN = 0xff99
NUM_PRIOR = 0xff9a
NUM_PAGE_UP = 0xff9a
NUM_NEXT = 0xff9b
NUM_PAGE_DOWN = 0xff9b
NUM_END = 0xff9c
NUM_BEGIN = 0xff9d
NUM_INSERT = 0xff9e
NUM_DELETE = 0xff9f
NUM_EQUAL = 0xffbd
NUM_MULTIPLY = 0xffaa
NUM_ADD = 0xffab
NUM_SEPARATOR = 0xffac
NUM_SUBTRACT = 0xffad
NUM_DECIMAL = 0xffae
NUM_DIVIDE = 0xffaf
NUM_0 = 0xffb0
NUM_1 = 0xffb1
NUM_2 = 0xffb2
NUM_3 = 0xffb3
NUM_4 = 0xffb4
NUM_5 = 0xffb5
NUM_6 = 0xffb6
NUM_7 = 0xffb7
NUM_8 = 0xffb8
NUM_9 = 0xffb9
# Function keys
F1 = 0xffbe
F2 = 0xffbf
F3 = 0xffc0
F4 = 0xffc1
F5 = 0xffc2
F6 = 0xffc3
F7 = 0xffc4
F8 = 0xffc5
F9 = 0xffc6
F10 = 0xffc7
F11 = 0xffc8
F12 = 0xffc9
F13 = 0xffca
F14 = 0xffcb
F15 = 0xffcc
F16 = 0xffcd
F17 = 0xffce
F18 = 0xffcf
F19 = 0xffd0
F20 = 0xffd1
# Modifiers
LSHIFT = 0xffe1
RSHIFT = 0xffe2
LCTRL = 0xffe3
RCTRL = 0xffe4
CAPSLOCK = 0xffe5
LMETA = 0xffe7
RMETA = 0xffe8
LALT = 0xffe9
RALT = 0xffea
LWINDOWS = 0xffeb
RWINDOWS = 0xffec
LCOMMAND = 0xffed
RCOMMAND = 0xffee
LOPTION = 0xffef
ROPTION = 0xfff0
# Latin-1
SPACE = 0x020
EXCLAMATION = 0x021
DOUBLEQUOTE = 0x022
HASH = 0x023
POUND = 0x023 # synonym
DOLLAR = 0x024
PERCENT = 0x025
AMPERSAND = 0x026
APOSTROPHE = 0x027
PARENLEFT = 0x028
PARENRIGHT = 0x029
ASTERISK = 0x02a
PLUS = 0x02b
COMMA = 0x02c
MINUS = 0x02d
PERIOD = 0x02e
SLASH = 0x02f
_0 = 0x030
_1 = 0x031
_2 = 0x032
_3 = 0x033
_4 = 0x034
_5 = 0x035
_6 = 0x036
_7 = 0x037
_8 = 0x038
_9 = 0x039
COLON = 0x03a
SEMICOLON = 0x03b
LESS = 0x03c
EQUAL = 0x03d
GREATER = 0x03e
QUESTION = 0x03f
AT = 0x040
BRACKETLEFT = 0x05b
BACKSLASH = 0x05c
BRACKETRIGHT = 0x05d
ASCIICIRCUM = 0x05e
UNDERSCORE = 0x05f
GRAVE = 0x060
QUOTELEFT = 0x060
A = 0x061
B = 0x062
C = 0x063
D = 0x064
E = 0x065
F = 0x066
G = 0x067
H = 0x068
I = 0x069
J = 0x06a
K = 0x06b
L = 0x06c
M = 0x06d
N = 0x06e
O = 0x06f
P = 0x070
Q = 0x071
R = 0x072
S = 0x073
T = 0x074
U = 0x075
V = 0x076
W = 0x077
X = 0x078
Y = 0x079
Z = 0x07a
BRACELEFT = 0x07b
BAR = 0x07c
BRACERIGHT = 0x07d
ASCIITILDE = 0x07e
_key_names = {}
_motion_names = {}
for _name, _value in locals().copy().items():
if _name[:2] != '__' and _name.upper() == _name and \
not _name.startswith('MOD_'):
if _name.startswith('MOTION_'):
_motion_names[_value] = _name
else:
_key_names[_value] = _name
================================================
FILE: universe/envs/vnc_core_env/translator.py
================================================
from universe import spaces
from universe.envs.vnc_core_env import key
import logging
logger = logging.getLogger(__name__)
class AtariKeyState(object):
"""
Converts from VNCEvents to an Atari-v0 action index
Since spaces.KeyEvent only give you a diff of a keyboard, we need to persist the total state of the keyboard to
convert from VNCEvents to an action index
"""
def __init__(self, env):
self._translator = AtariTranslator(env)
self._down_keysyms = set() # Assumes that your env starts with no keys pressed down
def apply_vnc_actions(self, vnc_actions):
"""
Play a list of vnc_actions forward over the current keysyms state
NOTE: Since we are squashing a set of diffs into a single keyboard state, some information may be lost.
For example if the Z key is down, then we receive [(Z-up), (Z-down)], the output will not reflect any change in Z
You can make each frame shorter to offset this effect.
"""
for event in vnc_actions:
if isinstance(event, spaces.KeyEvent):
if event.down:
self._down_keysyms.add(event.key)
else:
self._down_keysyms.discard(event.key)
logger.debug("AtariKeyState._down_keysyms: {}".format(self._down_keysyms))
def to_keysyms(self):
"""Returns the current state as keysyms"""
return list(self._down_keysyms)
def to_index(self):
"""Returns the current state as an index"""
return self._translator.keysyms_to_index(self.to_keysyms())
class AtariTranslator(object):
"""Translates Atari actions to and from various formats"""
_all_keysyms = [key.UP, key.DOWN, key.LEFT, key.RIGHT, key.Z]
def __init__(self, env):
# e.g. {0: 'NOOP', 1: 'FIRE', 2: 'RIGHT', 3: 'LEFT', 4: 'RIGHTFIRE', 5: 'LEFTFIRE'}
self._index_to_name_ = {}
# e.g. {'RIGHT': 2, 'FIRE': 1, 'RIGHTFIRE': 4, 'LEFTFIRE': 5, 'NOOP': 0, 'LEFT': 3}
self._name_to_index_ = {}
for i, meaning in enumerate(env.unwrapped.get_action_meanings()):
self._name_to_index_[meaning] = i
self._index_to_name_[i] = meaning
def keysyms_to_vnc_actions(self, keysyms):
actions = []
keysyms = set(keysyms)
for keysym in self._all_keysyms:
down = keysym in keysyms
actions.append(spaces.KeyEvent(keysym, down=down))
return actions
def keysyms_to_index(self, keysyms):
name = self._keysyms_to_name(keysyms)
return self._name_to_index(name)
def index_to_keysyms(self, i):
name = self._index_to_name(i)
keysyms = []
if 'UP' in name:
keysyms.append(key.UP)
if 'DOWN' in name:
keysyms.append(key.DOWN)
if 'LEFT' in name:
keysyms.append(key.LEFT)
if 'RIGHT' in name:
keysyms.append(key.RIGHT)
if 'FIRE' in name:
keysyms.append(key.Z)
return keysyms
def _name_to_index(self, name):
return self._name_to_index_.get(name, 0)
def _index_to_name(self, i):
return self._index_to_name_[i]
def _keysyms_to_name(self, keysyms):
keys = ''
if key.UP in keysyms:
keys += 'UP'
if key.DOWN in keysyms:
keys += 'DOWN'
if key.LEFT in keysyms:
keys += 'LEFT'
if key.RIGHT in keysyms:
keys += 'RIGHT'
if key.Z in keysyms:
keys += 'FIRE'
return keys
class CartPoleTranslator(object):
def __init__(self, env):
pass
def keysyms_to_vnc_actions(self, keysyms):
down = key.LEFT in keysyms
return [spaces.KeyEvent(key.LEFT, down=down)]
def keysyms_to_index(self, keys):
if key.LEFT in keys:
return 0
else:
return 1
def index_to_keysyms(self, i):
if i == 0:
return [key.LEFT]
else:
return []
================================================
FILE: universe/envs/vnc_core_env/vnc_core_env.py
================================================
import logging
import time
import gym
from universe import spaces
from universe.envs import vnc_env
logger = logging.getLogger(__name__)
class GymCoreEnv(vnc_env.VNCEnv):
def __init__(self, gym_core_id, fps=60):
super(GymCoreEnv, self).__init__()
self.metadata = dict(self.metadata)
self.metadata['video.frames_per_second'] = fps
self.gym_core_id = gym_core_id
self._seed_value = None
self.vnc_pixels = True
class GymCoreSyncEnv(GymCoreEnv):
"""A synchronized version of the core envs. Its semantics should match
that of the core envs. (By default, observations are pixels from
the VNC session, but it also supports receiving the normal Gym
observations over the rewarder socket.)
Provided primarily for testing and debugging.
"""
def __init__(self, gym_core_id, fps=60, vnc_pixels=True):
super(GymCoreSyncEnv, self).__init__(gym_core_id, fps=fps)
# Metadata has already been cloned
self.metadata['semantics.async'] = False
self.gym_core_id = gym_core_id
self.vnc_pixels = vnc_pixels
if not vnc_pixels:
self._core_env = gym.spec(gym_core_id).make()
else:
self._core_env = None
def _flip_past(self, when_n):
info_n = [{} for i in range(self.n)]
while True:
observation_n, obs_info_n = self.vnc_session.flip()
metadata_n = self.diagnostics.extract_metadata(observation_n)
# Save the update count
self._propagate_obs_info(info_n, obs_info_n)
# All remote times, so no clock skew adjustments needed
invalid = []
for i, (metadata, when) in enumerate(zip(metadata_n, when_n)):
delta = when - metadata.get('now', 0)
if delta > 0:
invalid.append((i, delta))
if not invalid:
break
else:
tick = 1./self.metadata['video.frames_per_second']
logger.info('Waiting %sms for the following observations to catch up: %s', int(1000*tick), invalid)
time.sleep(tick)
return observation_n, info_n
def _reset(self):
assert self.rewarder_session
result = self.rewarder_session.reset(
seed=self._seed_value,
)
# Clear seed value so we don't double-send it
self._seed_value = None
# Wait until all the observations have passed the reset_time
remote_reset_time = [response['headers']['sent_at'] for _, _, response in result]
observation_n, _ = self._flip_past(remote_reset_time)
# Double check that our reward queue is empty
assert all(c == 0 for c in self.rewarder_session.rewards_count())
return self._observation(observation_n)
def _observation(self, observation_n):
if self.vnc_pixels:
return observation_n
else:
observation_n = self.rewarder_session.pop_observation()
assert all(observation is not None for observation in observation_n), 'At least one missing observation: {}'.format(observation_n)
return self._core_env.observation_space.from_jsonable(observation_n)
def _step(self, action_n):
# Add C keypress in order to "commit" the action, as
# interpreted by the remote.
action_n = [action + [
spaces.KeyEvent.by_name('c', down=True),
spaces.KeyEvent.by_name('c', down=False)
] for action in action_n]
# Submit directly to VNC session, without popping rewards
logger.debug('Submitting actions: %s', action_n)
action_n = self._compile_actions(action_n)
_, obs_info_n = self.vnc_session.step(action_n)
# Wait until the actions have actually happened
self.rewarder_session.wait(timeout=5)
# TODO: this is now present in the info messages; need to
# update the implementation.
when_n = [reward_buffer.info['reward_buffer.remote_time'] for reward_buffer in self.rewarder_session.reward_buffers]
observation_n, obs_info_n = self._flip_past(when_n)
reward_n, reward_time_n, done_n, info_n = self.rewarder_session.pop()
self._propagate_obs_info(info_n, obs_info_n)
# Warn if we detect multiple rewards
if any(info['stats.reward.count'] != 1 for info in info_n):
# Arrived but there was a bug
logger.warn('Likely bug: should have received 1 reward for every env, but instead received %s. Current return: observation=%s reward=%s done=%s info=%s', [info['stats.reward.count'] for info in info_n], observation_n, reward_n, done_n, info_n)
return self._observation(observation_n), reward_n, done_n, {'n': info_n}
================================================
FILE: universe/envs/vnc_env.py
================================================
import getpass
import logging
import os
import random
import uuid
import universe
from gym.utils import reraise
from universe import error, pyprofile, rewarder, spaces, twisty, vectorized, vncdriver
from universe import remotes as remotes_module
from universe.envs import diagnostics
from universe.runtimes import registration
from universe.vncdriver import libvnc_session
# The Go driver is the most supported one. So long as the Go driver
# turns out to be easy to install, we'll continue forcing the Go
# driver here.
# noinspection PyUnresolvedReferences
import go_vncdriver
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
if os.environ.get('UNIVERSE_VNCDRIVER', None) == 'go':
# Importing the Go driver early is desirable, so that people get
# errors if it's not present. Also sometimes if go_vncdriver is
# loaded after TensorFlow it will crash.
import go_vncdriver
def default_client_id():
return '{}-{}'.format(uuid.uuid4(), getpass.getuser())
def rewarder_session(which):
if which is None:
which = rewarder.RewarderSession
if isinstance(which, type):
return which
else:
raise error.Error('Invalid RewarderSession driver: {!r}'.format(which))
def go_vncdriver():
import go_vncdriver
return go_vncdriver.VNCSession
def py_vncdriver():
return vncdriver.VNCSession
def libvnc_vncdriver():
return libvnc_session.LibVNCSession
def vnc_session(which=None):
# Short circuit so long as we're forcing the Go driver. Other code
# left behind for the future if we need to support the other
# drivers again.
if isinstance(which, type):
# Used in the tests to pass a custom VNC driver
return which
logger.info('Using the golang VNC implementation')
return go_vncdriver()
if which is None:
which = os.environ.get('UNIVERSE_VNCDRIVER')
if isinstance(which, type):
return which
if which == 'go':
logger.info('Using the golang VNC implementation')
return go_vncdriver()
elif which == 'py':
logger.info('Using the Python VNC implementation')
return py_vncdriver()
elif which == 'libvnc':
logger.info('Using the libvnc VNC implementation')
return libvnc_vncdriver()
elif which is None:
try:
go = go_vncdriver()
logger.debug('Using golang VNC implementation')
return go
except ImportError as e:
logger.info("Go driver failed to import: {}".format(e))
logger.info("Using pure Python vncdriver implementation. Run 'pip install go-vncdriver' to install the more performant Go implementation. Optionally set the environment variable UNIVERSE_VNCDRIVER='go' to force its use.")
return py_vncdriver()
else:
raise error.Error('Invalid VNCSession driver: {!r}'.format(which))
def build_observation_n(visual_observation_n, info_n):
observation_n = []
for visual, info in zip(visual_observation_n, info_n):
text = info.pop('env.text', [])
obs = {
'vision': visual,
'text': text,
}
if 'env.generic' in info:
obs['generic'] = info.pop('env.generic')
observation_n.append(obs)
return observation_n
class VNCEnv(vectorized.Env):
metadata = {
'render.modes': ['human'], # we wrap with a Render which can render to rgb_array
'semantics.async': True,
'semantics.autoreset': True,
'video.frames_per_second' : 60,
'runtime.vectorized': True,
'configure.required': True,
}
def __init__(self, fps=None, probe_key=None):
self.metadata = dict(self.metadata)
if fps is not None:
self.metadata['video.frames_per_second'] = fps
self._started = False
self.observation_space = spaces.VNCObservationSpace()
self.action_space = spaces.VNCActionSpace()
self._seed_value = None
self._remotes_manager = None
self._probe_key = probe_key or 0xbeef1
self.vnc_session = None
self.rewarder_session = None
self._send_actions_over_websockets = False
self._skip_network_calibration = False
def _seed(self, seed):
self._seed_value = seed
return [seed]
def configure(self, remotes=None,
client_id=None,
start_timeout=None, docker_image=None,
ignore_clock_skew=False, disable_action_probes=False,
vnc_driver=None, vnc_kwargs=None,
rewarder_driver=None,
replace_on_crash=False, allocate_sync=True,
observer=False, api_key=None,
record=False,
sample_env_ids=None,
):
"""Universe method to configure the environment.
Args:
ignore_clock_skew (bool): Assume remotes are on the same machine as us,
for the purposes of diagnostics measurement.
If true, we skip measuring the clock skew over the network,
and skip generating diagnostics which rely on it.
True when used by the rewarder to measure latency between
the VNC frame and its calculation of reward for that
frame. In this case we share a common clock with the env
generating the VNC frame, so we don't need to send/receive
probes. Clock skew is zero in this case.
False when remotes are potentially different machines
(such as an agent, or a demonstrator), and we will be
sending probe keys and measuring network ping rountrip
times to calculate clock skew.
"""
if self._started:
raise error.Error('{} has already been started; cannot change configuration now.'.format(self))
universe.configure_logging()
twisty.start_once()
if self.spec is not None:
runtime = registration.runtime_spec(self.spec.tags['runtime'])
# Let the user manually set the docker_image version
if docker_image:
# TODO: don't support this option?
runtime.image = docker_image
else:
runtime = None
if remotes is None:
remotes = os.environ.get('GYM_VNC_REMOTES', '1')
if client_id is None:
client_id = default_client_id()
if vnc_kwargs is None:
vnc_kwargs = {}
self.remote_manager, self.n = remotes_module.build(
client_id=client_id,
remotes=remotes, runtime=runtime, start_timeout=start_timeout,
api_key=api_key,
use_recorder_ports=record,
)
self.connection_names = [None] * self.n
self.connection_labels = [None] * self.n
self.crashed = {}
self.allow_reconnect = replace_on_crash and self.remote_manager.supports_reconnect
if self.remote_manager.connect_vnc:
cls = vnc_session(vnc_driver)
vnc_kwargs.setdefault('start_timeout', self.remote_manager.start_timeout)
if runtime == 'gym-core':
vnc_kwargs.setdefault('encoding', 'zrle')
else:
vnc_kwargs.setdefault('encoding', 'tight')
vnc_kwargs.setdefault('fine_quality_level', 50)
vnc_kwargs.setdefault('subsample_level', 2)
# Filter out None values, since some drivers may not handle them correctly
vnc_kwargs = {k: v for k, v in vnc_kwargs.items() if v is not None}
logger.info('Using VNCSession arguments: %s. (Customize by running "env.configure(vnc_kwargs={...})"', vnc_kwargs)
self.vnc_kwargs = vnc_kwargs
self.vnc_session = cls()
else:
self.vnc_session = None
self._observer = observer
if self.remote_manager.connect_rewarder:
cls = rewarder_session(rewarder_driver)
self.rewarder_session = cls()
else:
self.rewarder_session = None
if ignore_clock_skew:
logger.info('Printed stats will ignore clock skew. (This usually makes sense only when the environment and agent are on the same machine.)')
if self.rewarder_session or ignore_clock_skew:
# Don't need rewarder session if we're ignoring clock skew
if self.spec is not None:
metadata_encoding = self.spec.tags.get('metadata_encoding')
else:
metadata_encoding = None
self.diagnostics = diagnostics.Diagnostics(self.n, self._probe_key, ignore_clock_skew, metadata_encoding=metadata_encoding, disable_action_probes=disable_action_probes)
else:
self.diagnostics = None
self._sample_env_ids = sample_env_ids
self._reset_mask()
self._started = True
self.remote_manager.allocate([str(i) for i in range(self.n)], initial=True)
if allocate_sync:
# Block until we've fulfilled n environments
self._handle_connect(n=self.n)
else:
# Handle any backends which synchronously fufill their
# allocation.
self._handle_connect()
def connect(self, i, name, vnc_address, rewarder_address, vnc_password=None, rewarder_password=None):
logger.info('[%s] Connecting to environment: vnc://%s password=%s. If desired, you can manually connect a VNC viewer, such as TurboVNC. Most environments provide a convenient in-browser VNC client: http://%s/viewer/?password=%s', name, vnc_address, vnc_password, rewarder_address, vnc_password)
extra_logger.info('[%s] Connecting to environment details: vnc_address=%s vnc_password=%s rewarder_address=%s rewarder_password=%s', name, vnc_address, vnc_password, rewarder_address, rewarder_password)
self.connection_names[i] = name
self.connection_labels[i] = '{}:{}'.format(name, vnc_address)
if self.vnc_session is not None:
kwargs = {
'name': name,
'address': vnc_address,
'password': vnc_password,
}
kwargs.update(self.vnc_kwargs)
try:
self.vnc_session.connect(**kwargs)
except TypeError as e:
reraise(suffix="(HINT: this error was while passing arguments to the VNCSession driver: {})".format(kwargs))
# TODO: name becomes index:pod_id
# TODO: never log index, just log name
if self.rewarder_session is not None:
if self.spec is not None:
env_id = self.spec.id
else:
env_id = None
if self._seed_value is not None:
# Once we use a seed, we clear it so we never
# accidentally reuse the seed. Seeds are an advanced
# feature and aren't supported by most envs in any
# case.
seed = self._seed_value[i]
self._seed_value[i] = None
else:
seed = None
assert rewarder_password, "Missing rewarder password: {}".format(rewarder_password)
network = self.rewarder_session.connect(
name=name, address=rewarder_address,
seed=seed, env_id=env_id,
fps=self.metadata['video.frames_per_second'],
password=rewarder_password,
label=self.connection_labels[i],
start_timeout=self.remote_manager.start_timeout,
observer=self._observer,
skip_network_calibration=self._skip_network_calibration
)
else:
network = None
if self.diagnostics is not None:
self.diagnostics.connect(i, network, label=name)
self.crashed.pop(i, None)
def _close(self, i=None):
if i is not None:
name = self.connection_names[i]
if self.rewarder_session:
self.rewarder_session.close(name)
if self.vnc_session:
self.vnc_session.close(name)
if self.diagnostics:
self.diagnostics.close(i)
self.mask.close(i)
self.connection_names[i] = None
self.connection_labels[i] = None
else:
if hasattr(self, 'rewarder_session') and self.rewarder_session:
self.rewarder_session.close()
if hasattr(self, 'vnc_session') and self.vnc_session:
self.vnc_session.close()
if hasattr(self, 'diagnostics') and self.diagnostics:
self.diagnostics.close()
if hasattr(self, 'remotes_manager') and self._remotes_manager:
self._remotes_manager.close()
def _reset(self):
self._handle_connect()
if self.rewarder_session:
if self._sample_env_ids:
env_id = random.choice(self._sample_env_ids)
logger.info("Randomly sampled env_id={}".format(env_id))
else:
env_id = None
self.rewarder_session.reset(env_id=env_id)
else:
logger.info("No rewarder session exists, so cannot send a reset via the rewarder channel")
self._reset_mask()
return [None] * self.n
def _reset_mask(self):
self.mask = Mask(self.connection_labels, initially_masked=bool(self.rewarder_session))
def _pop_rewarder_session(self, peek_d):
with pyprofile.push('vnc_env.VNCEnv.rewarder_session.pop'):
reward_d, done_d, info_d, err_d = self.rewarder_session.pop(peek_d=peek_d)
reward_n = []
done_n = []
info_n = []
err_n = []
for name in self.connection_names:
reward_n.append(reward_d.get(name, 0))
done_n.append(done_d.get(name, False))
info_n.append(info_d.get(name, {'env_status.disconnected': True}))
err_n.append(err_d.get(name))
return reward_n, done_n, info_n, err_n
def _step_vnc_session(self, compiled_d):
if self._send_actions_over_websockets:
self.rewarder_session.send_action(compiled_d, self.spec.id)
vnc_action_d = {}
else:
vnc_action_d = compiled_d
with pyprofile.push('vnc_env.VNCEnv.vnc_session.step'):
observation_d, info_d, err_d = self.vnc_session.step(vnc_action_d)
observation_n = []
info_n = []
err_n = []
for name in self.connection_names:
observation_n.append(observation_d.get(name))
info_n.append(info_d.get(name))
err_n.append(err_d.get(name))
return observation_n, info_n, err_n
def _compile_actions(self, action_n):
compiled_n = []
peek_d = {}
try:
for i, action in enumerate(action_n):
compiled = []
compiled_n.append(compiled)
for event in action:
# Handle any special control actions
if event == spaces.PeekReward:
name = self.connection_names[i]
peek_d[name] = True
continue
# Do a generic compile
compiled.append(compile_action(event))
except Exception as e:
raise error.Error('Could not compile actions. Original error: {} ({}). action_n={}'.format(e, type(e), action_n))
else:
return compiled_n, peek_d
def _action_d(self, action_n):
action_d = {}
for i, action in enumerate(action_n):
action_d[self.connection_names[i]] = action
return action_d
def _step(self, action_n):
self._handle_connect()
# Compile actions to something more palatable by drivers
# written in other language.
action_n, peek_d = self._compile_actions(action_n)
# We pop from the rewarder session first since we need to
# determine if any of the current VNC actions need to be
# masked. (If the environment is resetting, we should
# definitely not send it any actions.)
#
# It's a bit counterintuitive to check for rewards first,
# since we haven't submitted the actions yet, but keep in mind
# that everything here is asynchronous!
if self.rewarder_session:
reward_n, done_n, info_n, err_n = self._pop_rewarder_session(peek_d)
else:
reward_n = done_n = [None] * self.n
info_n = [{} for _ in range(self.n)]
err_n = [None] * self.n
action_mask = observation_mask = self.mask.maintain_mask(done_n, info_n)
# Drop any actions to resetting environments.
#
# We pass the mask to add_probe so it doesn't try to schedule
# probes which are potentially harmful and will just time out
# anyway.
self.mask.apply_to_actions(action_n, info_n, action_mask)
# Send our actions and return the current framebuffer
if self.vnc_session:
if self.diagnostics:
self.diagnostics.clear_probes_when_done(done_n)
self.diagnostics.add_probe(action_n, action_mask)
action_d = self._action_d(action_n)
visual_observation_n, obs_info_n, vnc_err_n = self._step_vnc_session(action_d)
# Merge in any keys from the observation
self._propagate_obs_info(info_n, obs_info_n)
else:
visual_observation_n = [None] * self.n
vnc_err_n = [None] * self.n
observation_n = build_observation_n(visual_observation_n, info_n)
self.mask.apply_to_return(observation_n, reward_n, done_n, info_n, observation_mask)
self._handle_initial_n(observation_n, reward_n)
self._handle_err_n(err_n, vnc_err_n, info_n, observation_n, reward_n, done_n)
self._handle_crashed_n(info_n)
return observation_n, reward_n, done_n, {'n': info_n}
def _handle_initial_n(self, observation_n, reward_n):
if self.rewarder_session is None:
return
for i, reward in enumerate(reward_n):
if reward is None:
# Index hasn't come up yet, so ensure the observation
# is blanked out.
observation_n[i] = None
def _handle_err_n(self, err_n, vnc_err_n, info_n, observation_n=None, reward_n=None, done_n=None):
# Propogate any errors upwards.
for i, (err, vnc_err) in enumerate(zip(err_n, vnc_err_n)):
if err is None and vnc_err is None:
# All's well at this index.
continue
if observation_n is not None:
observation_n[i] = None
done_n[i] = True
# Propagate the error
if err is not None and vnc_err is not None:
# Both the rewarder and vnc failed at the same
# time. What are the odds?
info_n[i]['error'] = 'Both VNC and rewarder sessions failed: {}; {}'.format(vnc_err, err)
elif err is not None:
info_n[i]['error'] = 'Rewarder session failed: {}'.format(err)
else:
info_n[i]['error'] = 'VNC session failed: {}'.format(vnc_err)
extra_logger.info('[%s] %s', self.connection_names[i], info_n[i]['error'])
if self.allow_reconnect:
logger.info('[%s] Making a call to the allocator to replace crashed index: %s', self.connection_names[i], info_n[i]['error'])
self.remote_manager.allocate([str(i)])
self.crashed[i] = self.connection_names[i]
self._close(i)
def _handle_connect(self, n=None):
# Connect to any environments which are ready
for remote in self.remote_manager.pop(n=n):
if remote.name is not None:
name = '{}:{}'.format(remote.handle, remote.name)
else:
name = remote.handle
self.connect(
int(remote.handle), name=name,
vnc_address=remote.vnc_address, vnc_password=remote.vnc_password,
rewarder_address=remote.rewarder_address, rewarder_password=remote.rewarder_password)
def _handle_crashed_n(self, info_n):
# for i in self.crashed:
# info_n[i]['env_status.crashed'] = True
if self.allow_reconnect:
return
if len(self.crashed) > 0:
errors = {}
for i, info in enumerate(info_n):
if 'error' in info:
errors[self.crashed[i]] = info['error']
if len(errors) == 0:
raise error.Error('{}/{} environments have crashed. No error key in info_n: {}'.format(len(self.crashed), self.n, info_n))
else:
raise error.Error('{}/{} environments have crashed! Most recent error: {}'.format(len(self.crashed), self.n, errors))
def _propagate_obs_info(self, info_n, obs_info_n):
for obs_info, info in zip(obs_info_n, info_n):
# obs_info keys take precedence over info keys
if obs_info is not None:
info.update(obs_info)
def _render(self, mode='human', close=False):
if close:
# render(close) is not currently supported by the Go VNCSession
return
if mode is 'human' and self.vnc_session is not None:
if self.connection_names[0]:
self.vnc_session.render(self.connection_names[0])
def __str__(self):
if self.spec:
return ''.format(self.spec.id)
else:
return 'VNCEnv'
class Mask(object):
"""Blocks the agent from interacting with the environment while the
environment is resetting.
On the boundaries:
- Mask will *drop* actions to environments which have just started
resetting (i.e. returning done=True in this iteration and have
env_state=resetting).
- Mask will *allow* actions to environments which have just
finished resetting (i.e. their env_state=running).
- Mask will *allow* rewards from environments which have just
started resetting (i.e. returning done=True in this iteration
and have env_state=resetting), but mask observations from them.
- Mask will *allow* observations from environments which have just
finished resetting (i.e. their env_state is running)
"""
def __init__(self, connection_labels, initially_masked=True):
self.connection_labels = connection_labels
self.n = len(connection_labels)
self.episode_ids = [None] * self.n
if initially_masked:
self.mask = [None] * self.n
else:
self.mask = [True] * self.n
def close(self, i):
self.mask[i] = None
self.episode_ids[i] = None
def maintain_mask(self, done_n, info_n):
for i, ok in enumerate(self.mask):
if info_n[i].get('peek'):
env_state = info_n[i].get('env_status.peek.env_state', 'resetting')
episode_id = info_n[i].get('env_status.peek.episode_id')
if info_n[i].get('env_status.episode_id') != episode_id:
completed_episode_id = info_n[i].get('env_status.episode_id')
else:
completed_episode_id = None
else:
env_state = info_n[i].get('env_status.env_state', 'resetting')
episode_id = info_n[i].get('env_status.episode_id')
completed_episode_id = info_n[i].get('env_status.complete.episode_id')
# Either:
# 1. The env is currently masked (not ok)
# 2. We have an active episode (self.episode_ids[i])
# 3. We didn't connect the rewarder (done_n[i] is None)
assert not ok or self.episode_ids[i] is not None or done_n[i] is None, "ok={} episode_id={} i={}".format(ok, episode_id, i)
if not ok and env_state == 'running':
extra_logger.info('[%s] Episode began: episode_id=%s env_state=%s', self.connection_labels[i], episode_id, env_state)
self.mask[i] = True
# this should change only for initial reset
self.episode_ids[i] = episode_id
elif ok and self.episode_ids[i] != episode_id and env_state == 'running':
extra_logger.info('[%s] Episode ended (and began, so not masking): episode_id=%s->%s env_state=%s', self.connection_labels[i], completed_episode_id, episode_id, env_state)
self.episode_ids[i] = episode_id
elif ok and self.episode_ids[i] != episode_id:
extra_logger.info('[%s] Episode ended: episode_id=%s->%s env_state=%s', self.connection_labels[i], completed_episode_id, episode_id, env_state)
self.mask[i] = False
self.episode_ids[i] = episode_id
return self.mask
def apply_to_actions(self, action_n, info_n, mask):
for i, ok in enumerate(mask):
if ok:
continue
action_n[i] = []
info_n[i]['mask.masked.action'] = True
return self.mask
def apply_to_return(self, observation_n, reward_n, done_n, info_n, observation_mask):
# Next, mask any environments which are resetting. We are
# guaranteed to get done=True messages prior to getting the
# v0.env.describe message telling us it's resetting, so the
# conservative route (block upon done=True, unblock upon
# v0.env.describe with env_state=running) locks us out of the
# maximum surface area of environment reset possible.
for i, ok in enumerate(observation_mask):
if ok:
continue
if len(observation_n[i]['text']) > 0 and ok is False:
logger.warn('[%s] WARNING: Masking text observation for environment %d: %r. This means we received text data before the environment finished resetting; the text observation has been lost. This is not expected and should be reported.', self.connection_labels[i], i, observation_n[i]['text'])
observation_n[i] = None
info_n[i]['mask.masked.observation'] = True
def compile_action(event):
if isinstance(event, tuple):
if event[0] == 'KeyEvent':
name, down = event[1:]
return spaces.KeyEvent.by_name(name, down=down).compile()
elif event[0] == 'PointerEvent':
x, y, buttonmask = event[1:]
return spaces.PointerEvent(x, y, buttonmask).compile()
else:
return event.compile()
================================================
FILE: universe/envs/vnc_flashgames.py
================================================
from universe.envs import vnc_env
class FlashgamesEnv(vnc_env.VNCEnv):
def __init__(self):
super(FlashgamesEnv, self).__init__()
self._probe_key = 0x60 # backtick `
================================================
FILE: universe/envs/vnc_gtav.py
================================================
from universe.envs import vnc_env
from universe.spaces.joystick_action_space import JoystickActionSpace
class GTAVEnv(vnc_env.VNCEnv):
def __init__(self):
super(GTAVEnv, self).__init__()
self.action_space = JoystickActionSpace(axis_x=True, axis_z=True)
self._send_actions_over_websockets = True
self._skip_network_calibration = True
================================================
FILE: universe/envs/vnc_internet.py
================================================
from universe.envs import vnc_env
class InternetEnv(vnc_env.VNCEnv):
def __init__(self):
super(InternetEnv, self).__init__()
self._probe_key = 0x60 # backtick `
================================================
FILE: universe/envs/vnc_starcraft.py
================================================
import string
from universe import spaces
from universe.spaces import vnc_event, VNCActionSpace
from universe.spaces.vnc_event import KeyEvent, PointerEvent
from universe.envs import vnc_env
from universe.vncdriver import constants
import logging
logger = logging.getLogger()
SCREEN_DIM = (640, 480)
class StarCraftEnv(vnc_env.VNCEnv):
def __init__(self):
super(StarCraftEnv, self).__init__()
self.action_space = VNCActionSpace(
keys=['f2', # Map positions
'f3', # Map positions
'f4', # Map positions
'spacebar',
'left',
'up',
'right',
'down'],
screen_shape=SCREEN_DIM
)
self.safe_action_space = self.action_space
# def _step(self, action_n):
# return super(StarCraftEnv, self)._step(
# (StarCraftEventFilter.filter(a) for a in action_n))
# class StarCraftEventFilter(object):
# """
# We only allow keyboard inputs used by StarCraft:
# http://gamingweapons.com/image/steelseries/zboard-starcraft2-keyset/steelseries_zboard_starcraft2_keyset_02.jpg
# """
# _x_offset = 5 # Centered
# _y_offset = 30 # Remove the chrome
# @classmethod
# def _safe_pointer_event(cls, event):
# """Returns true if the click is in a place that will not break out of the box"""
# height = SCREEN_DIM[0]
# width = SCREEN_DIM[1]
# margin = 5 # Never allow clicking within 5 pixels of the edge of the screen
# unsafe_locations = [
# (event.y < cls._y_offset + margin), # At the top, where menu chrome is
# (event.y > height + cls._y_offset - margin), # Too far down
# (event.x < cls._x_offset + margin), # Too far left
# (event.x > width + cls._x_offset - margin), # Too far right
# (410 < event.x < 510) and (370 < event.y < 450), # Where the menu button is
# ]
# unsafe = any(unsafe_locations)
# if unsafe:
# logger.warning('skipping unsafe pointer event')
# return not unsafe
# @classmethod
# def safe_event(cls, event):
# if isinstance(event, PointerEvent):
# return cls._safe_pointer_event(event)
# @classmethod
# def filter(cls, events):
# return filter(cls.safe_event, events)
================================================
FILE: universe/envs/vnc_wog.py
================================================
from universe.envs import vnc_env
from universe.spaces import VNCActionSpace
class WorldOfGooEnv(vnc_env.VNCEnv):
def __init__(self):
super(WorldOfGooEnv, self).__init__()
# TODO: set action space screen shape to match
# HACK: empty keys list fails for some weird reason, give it an 'a'
self.action_space = VNCActionSpace(keys=['a'], buttonmasks=[1])
================================================
FILE: universe/error.py
================================================
import sys
class Error(Exception):
pass
class RPCError(Error):
pass
class ConnectionError(Error):
pass
class TimeoutError(Error):
pass
class AuthenticationError(Error):
pass
================================================
FILE: universe/kube/__init__.py
================================================
from universe.kube.discovery import discover, discover_batches
================================================
FILE: universe/kube/discovery.py
================================================
import json
import logging
import pipes
import subprocess
class Error(Exception):
pass
logger = logging.getLogger()
def pretty_command(command):
return ' '.join(pipes.quote(c) for c in command)
def log_command(command, prefix=''):
logger.info('%sExecuting: %s', prefix, pretty_command(command))
def check_call(command, *args, **kwargs):
log_command(command)
return subprocess.check_call(command, *args, **kwargs)
def popen(command, *args, **kwargs):
log_command(command)
return subprocess.Popen(command, *args, **kwargs)
def check_with_output(command, *args, **kwargs):
log_command(command)
proc = subprocess.Popen(command, *args, stdout=subprocess.PIPE, **kwargs)
stdout, _ = proc.communicate()
if proc.returncode != 0:
raise Error('Command {} returned non-zero exit status {}'.format(command, proc.returncode))
return stdout
def interpret_ready(pod):
# status:
# conditions:
# - lastProbeTime: null
# lastTransitionTime: 2016-07-06T05:29:45Z
# message: 'containers with unready status: [xdummy xvnc vnc-atari]'
# reason: ContainersNotReady
# status: "False"
# type: Ready
if 'conditions' not in pod['status']:
return False
ready = [c for c in pod['status']['conditions'] if c['type'] == 'Ready']
if not ready:
return False
return ready[0]['status'] == 'True'
def interpret_ports(containers):
# TODO: clean up hack
try:
recorder = containers['vnc-recorder']
except KeyError:
pass
else:
spec = recorder['ports'][0]
assert spec['containerPort'] == 5899
return spec['hostPort'], None
app = [k for k in containers.keys() if k.startswith('vnc-')]
assert len(app) == 1
app = app[0]
port_mapping = {}
for spec in containers[app]['ports']:
port_mapping[spec['containerPort']] = spec['hostPort']
# vnc, rewarder
return port_mapping[5900], port_mapping.get(15900)
class VNCEnvDiscovery(object):
def __init__(self):
self.context = 'sci'
self.namespace = 'gym'
self.kubectl = ['kubectl', '--context', self.context, '--namespace', self.namespace]
def discover_batches(self):
pods = check_with_output(self.kubectl + ['get', 'pods', '-o', 'json', '-l', 'type=universe'])
pods = json.loads(pods)
batches = {}
for pod in pods['items']:
if 'deletionTimestamp' in pod['metadata']:
# Pod has been deleted!
continue
batch = pod['metadata']['labels']['batch']
if batch not in batches:
batches[batch] = {'count': 0}
batches[batch]['count'] += 1
return batches
def discover(self, batch, force_ready=False):
pods = check_with_output(self.kubectl + ['get', 'pods', '-o', 'json', '-l', 'type=universe', '-l', 'batch={}'.format(batch)])
pods = json.loads(pods)
if len(pods['items']) == 0:
raise Error('Incorrect batch id: {}'.format(batch))
remotes = []
for pod in pods['items']:
name = pod['metadata']['name']
containers = {}
for container in pod['spec']['containers']:
containers[container['name']] = container
vnc_port, rewarder_port = interpret_ports(containers)
node = pod['spec'].get('nodeName')
# Not scheduled on a node yet
if node is None:
if force_ready:
raise Error('Not all pods ready: {} is not scheduled on a node yet'.format(name))
continue
address = '{}:{}'.format(node, vnc_port)
if rewarder_port is not None:
address += '+{}'.format(rewarder_port)
spec = {
'name': name,
'address': address,
'ready': interpret_ready(pod),
}
remotes.append(spec)
return remotes
vnc_env_discovery = VNCEnvDiscovery()
discover = vnc_env_discovery.discover
discover_batches = vnc_env_discovery.discover_batches
================================================
FILE: universe/pyprofile/__init__.py
================================================
# A simple in-memory stats library
#
# Inspired by statsd: http://statsd.readthedocs.io/en/v3.1/types.html#gauges
import collections
import json
import logging
import numbers
import numpy as np
import os
import six
import threading
import time
logger = logging.getLogger(__name__)
BYTES = 'bytes'
SECONDS = 'seconds'
class Error(Exception):
pass
class ExponentialAverage(object):
def __init__(self, decay=0.1):
self.decay = decay
self.last_update = None
self.last_data_decay = None
self._avg = None
def add(self, data):
assert isinstance(data, numbers.Number)
if self.last_update is None:
self._avg = data
self.last_update = time.time()
self.last_data_decay = 1
else:
now = time.time()
delta = now - self.last_update
if delta < 0:
# Time is allowed to go a little backwards (NTP update, etc)
logger.warn("Backwards delta value: {}".format(delta))
# Treat this entry as if it happened with 0 delta
delta = 0
if delta != 0:
self.last_data_decay = (1 - self.decay**delta) * 1/delta
self._avg = self.decay**delta * self._avg + self.last_data_decay * data
else:
# Don't divide by zero; just reuse the last delta. Should stack well
self._avg += self.last_data_decay * data
self.last_update = now
def avg(self):
return self._avg
class RunningVariance(object):
""" Implements Welford's algorithm for computing a running mean
and standard deviation as described at:
http://www.johndcook.com/standard_deviation.html
can take single values or iterables
Properties:
mean - returns the mean
std - returns the std
meanfull- returns the mean and std of the mean
Usage:
>>> foo = Welford()
>>> foo(range(100))
>>> foo
>>> foo([1]*1000)
>>> foo
>>> foo.mean
5.409090909090906
>>> foo.std
16.44374171455467
>>> foo.meanfull
(5.409090909090906, 0.4957974674244838)
"""
def __init__(self):
self.k = 0
self.M = 0
self.S = 0
def add(self,x):
if x is None:
return
self.k += 1
newM = self.M + (x - self.M)*1./self.k
newS = self.S + (x - self.M)*(x - newM)
self.M, self.S = newM, newS
def mean(self):
return self.M
def meanfull(self):
return self.mean, self.std/np.sqrt(self.k)
def std(self):
if self.k==1:
return 0
return np.sqrt(self.S/(self.k-1))
def __repr__(self):
return "".format(self.mean, self.std)
def pretty(d, unit):
if unit is None:
return d
elif unit == BYTES:
return pretty_bytes(d)
elif unit == SECONDS:
return pretty_seconds(d)
else:
raise Error('No such unit: {}'.format(unit))
def pretty_bytes(b):
if b is None:
return None
assert isinstance(b, numbers.Number), "Surprising type for data: {} ({!r})".format(type(b), b)
if b > 1000 * 1000:
return '{:.0f}MB'.format(b/1000.0/1000.0)
elif b > 1000:
return '{:.0f}kB'.format(b/1000.0)
else:
return '{:.0f}B'.format(b)
def pretty_seconds(t):
a_t = abs(t)
if a_t < 0.001:
return '{:.2f}us'.format(1000*1000*t)
elif a_t < 1:
return '{:.2f}ms'.format(1000*t)
else:
return '{:.2f}s'.format(t)
def thread_id():
return threading.current_thread().ident
class StackProfile(object):
def __init__(self, profile):
self.profile = profile
self.stack_by_thread = {}
self.lock = threading.Lock()
def __enter__(self):
return self
def __exit__(self, type, value, tb):
self.pop()
def push(self, event):
stack = self._current_stack()
stack.append({
'name': event,
'start': time.time(),
})
return self
def pop(self):
stack = self._current_stack()
event = stack.pop()
name = event['name']
start = event['start']
with self.profile as txn:
delta = time.time() - start
txn.timing(name, delta)
# These are now subsumed by the timers key
# txn.incr(name + '.total_time', delta, unit=SECONDS)
# txn.incr(name + '.calls')
def _current_stack(self):
id = thread_id()
try:
stack = self.stack_by_thread[id]
except KeyError:
with self.lock:
# Only current thread should be adding to this entry anyway
assert id not in self.stack_by_thread
stack = self.stack_by_thread[id] = []
return stack
class Profile(object):
def __init__(self, print_frequency=None, print_filter=None):
if print_filter is None:
print_filter = lambda event: True
self.lock = threading.RLock()
self.print_frequency = print_frequency
self.last_export = None
self.export_hooks = [self._print_export]
self.print_filter = print_filter
self._in_txn = False
self.reset()
def reset(self):
self.timers = {}
self.counters = {}
self.gauges = {}
def add_export_hook(self, hook):
self.export_hooks.append(hook)
def __enter__(self):
self.lock.acquire()
self._in_txn = True
return self
def __exit__(self, type, value, tb):
self._in_txn = False
self._print_if_needed()
self.lock.release()
def timing(self, event, time):
assert isinstance(event, six.string_types)
# return
with self.lock:
if event not in self.timers:
self.timers[event] = {
'total': 0,
'calls': 0,
'std': RunningVariance(),
}
self.timers[event]['total'] += time
self.timers[event]['calls'] += 1
self.timers[event]['std'].add(time)
self._print_if_needed()
def incr(self, event, amount=1, unit=None):
assert isinstance(event, six.string_types)
# return
with self.lock:
if event not in self.counters:
self.counters[event] = {
'total': 0,
'calls': 0,
'rate': ExponentialAverage(),
'unit': unit,
'std': RunningVariance(),
}
self.counters[event]['total'] += amount
self.counters[event]['calls'] += 1
self.counters[event]['rate'].add(amount)
self.counters[event]['std'].add(amount)
self._print_if_needed()
def gauge(self, event, value, delta=False, unit=None):
assert isinstance(event, six.string_types)
with self.lock:
if event not in self.gauges:
self.gauges[event] = {
'value': 0,
'calls': 0,
'unit': unit,
'std': RunningVariance(),
}
if delta:
self.gauges[event]['value'] += value
else:
self.gauges[event]['value'] = value
self.gauges[event]['calls'] += 1
self.gauges[event]['std'].add(value)
self._print_if_needed()
def _print_if_needed(self):
"""Assumes you hold the lock"""
if self._in_txn or self.print_frequency is None:
return
elif self.last_export is not None and \
self.last_export + self.print_frequency > time.time():
return
self.export()
def export(self, log=True, reset=True):
with self.lock:
if self.last_export is None:
self.last_export = time.time()
delta = time.time() - self.last_export
self.last_export = time.time()
timers = {}
for event, stat in self.timers.items():
timers[event] = {
'mean': stat['std'].mean(),
'std': stat['std'].std(),
'calls': stat['calls'],
'unit': 'seconds',
}
counters = {}
for counter, stat in self.counters.items():
counters[counter] = {
'calls': stat['calls'],
'std': stat['std'].std(),
'mean': stat['std'].mean(),
'unit': stat['unit'],
'total': stat['total'],
'rate': stat['rate'].avg(),
}
gauges = {}
for gauge, stat in self.gauges.items():
gauges[gauge] = {
'value': stat['value'],
'calls': stat['calls'],
'std': stat['std'].std(),
'mean': stat['std'].mean(),
'unit': stat['unit'],
}
export = {
'timers': timers,
'counters': counters,
'gauges': gauges,
'metadata': {
'period': delta,
}
}
if log:
for hook in self.export_hooks:
hook(export)
if reset:
self.reset()
return export
def _print_export(self, export):
timers = {}
for event, stat in sorted(export['timers'].items()):
if not self.print_filter(event):
continue
timers[event] = {
'mean': pretty_seconds(stat['mean']),
'std': pretty_seconds(stat['std']),
'calls': stat['calls'],
}
counters = collections.OrderedDict({})
for counter, stat in sorted(export['counters'].items()):
if not self.print_filter(counter):
continue
unit = stat['unit']
counters[counter] = {
'calls': stat['calls'],
'std': pretty(stat['std'], unit),
'mean': pretty(stat['mean'], unit),
}
gauges = collections.OrderedDict({})
for gauge, stat in sorted(export['gauges'].items()):
if not self.print_filter(gauge):
continue
unit = stat['unit']
gauges[gauge] = {
'value': pretty(stat['value'], unit),
'calls': stat['calls'],
'std': pretty(stat['std'], unit),
'mean': pretty(stat['mean'], unit),
}
# A bit of a hack, but we want this time to be as inclusive as
# possible.
export['metadata']['export_time'] = time.time() - self.last_export
# We do the explicit OrderedDict and json.dumps to order
# keys. Maybe there's a better way?
logger.info('[pyprofile] period=%s timers=%s counters=%s gauges=%s (export_time=%s)',
pretty_seconds(export['metadata']['period']),
json.dumps(timers), json.dumps(counters), json.dumps(gauges),
pretty_seconds(export['metadata']['export_time']),
)
print_frequency = os.environ.get('PYPROFILE_FREQUENCY')
if print_frequency is not None:
print_frequency = float(print_frequency)
print_prefix = os.environ.get('PYPROFILE_PREFIX')
if print_prefix is not None:
print_filter = lambda event: event.startswith(print_prefix)
else:
print_filter = None
profile = Profile(print_frequency=print_frequency, print_filter=print_filter)
stack_profile = StackProfile(profile)
push = stack_profile.push
pop = stack_profile.pop
incr = profile.incr
timing = profile.timing
gauge = profile.gauge
export = profile.export
================================================
FILE: universe/remotes/__init__.py
================================================
from universe.remotes.hardcoded_addresses import HardcodedAddresses
from universe.remotes.allocator_remote import AllocatorManager
from universe.remotes.docker_remote import DockerManager
from universe.remotes.build import build
================================================
FILE: universe/remotes/allocator_remote.py
================================================
import json
import logging
import os
import re
import requests
import six
import six.moves.urllib.parse as urlparse
import threading
import time
import gym
from gym import scoreboard
from gym.utils import reraise
from universe import error, utils
from universe.remotes import remote
if six.PY2:
import Queue as queue
else:
import queue
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
# Using gym for this
_api_key = 'tyytjgq3envte2j9yv2e-{}'.format(os.environ.get('OPENAI_USER', os.environ.get('USER')))
allocator_base = 'http://allocator.sci.openai-tech.com'
# gym_base_url = 'http://api.gym.sci.openai-tech.com'
class Stop(Exception):
pass
class RequestError(Exception):
def __init__(self, message, status_code=None):
super(RequestError, self).__init__(message)
self.message = message
if status_code is not None:
self.status_code = status_code
class FatalError(RequestError):
pass
class AllocatorManager(threading.Thread):
daemon = True
def __init__(self, client_id, base_url=allocator_base,
address_type=None, start_timeout=None, api_key=None,
runtime_id=None, params=None, placement=None,
use_recorder_ports=False,
):
super(AllocatorManager, self).__init__()
self.label = 'AllocatorManager'
self.supports_reconnect = True
self.connect_vnc = True
self.connect_rewarder = True
if address_type is None: address_type = 'public'
if address_type not in ['public', 'pod', 'private']:
raise error.Error('Bad address type specified: {}. Must be public, pod, or private.'.format(address_type))
self.client_id = client_id
self.address_type = address_type
if start_timeout is None:
start_timeout = 20 * 60
self.start_timeout = start_timeout
self.params = params
self.placement = placement
self.use_recorder_ports = use_recorder_ports
# if base_url is None:
# base_url = scoreboard.api_base
# if base_url is None:
# base_url = gym_base_url
# if api_key is None:
# api_key = scoreboard.api_key
# if api_key is None:
# raise gym.error.AuthenticationError("""You must provide an OpenAI Gym API key.
# (HINT: Set your API key using "gym.scoreboard.api_key = .." or "export OPENAI_GYM_API_KEY=..."). You can find your API key in the OpenAI Gym web interface: https://gym.openai.com/settings/profile.""")
if api_key is None:
api_key = _api_key
self._requestor = AllocatorClient(self.label, api_key, base_url=base_url)
self.base_url = base_url
# These could be overridden on a per-allocation basis, if you
# want heterogeoneous envs. We don't support those currently
# in the higher layers, but this layer could support it
# easily.
self.runtime_id = runtime_id
self.pending = {}
self.error_buffer = utils.ErrorBuffer()
self.requests = queue.Queue()
self.ready = queue.Queue()
self._reconnect_history = {}
self._sleep = 1
@classmethod
def from_remotes(cls, client_id, remotes, runtime_id, runtime_tag, start_timeout, api_key, use_recorder_ports):
parsed = urlparse.urlparse(remotes)
if not (parsed.scheme == 'http' or parsed.scheme == 'https'):
raise error.Error('AllocatorManager must start with http:// or https://: {}'.format(remotes))
base_url = parsed.scheme + '://' + parsed.netloc
if parsed.path:
base_url += '/' + parsed.path
query = urlparse.parse_qs(parsed.query)
# Intercept url-encoded params ("?n=2" and similar)
params = {}
n = query.get('n', [1])[0] # not added to params, just returned later
cpu = query.get('cpu', [None])[0]
if cpu is not None:
cpu = float(cpu)
params['cpu'] = cpu
tag = query.get('tag', [None])[0]
if tag is not None:
params['tag'] = tag # url-encoded "?tag=" gets precedence over runtimes.yml tag
else:
params['tag'] = runtime_tag
placement = query.get('address', ['public'])[0]
# anything else from the query other than the components processed above will get dropped on the floor
return cls(client_id=client_id, runtime_id=runtime_id, base_url=base_url, start_timeout=start_timeout, params=params, placement=placement, api_key=api_key, use_recorder_ports=use_recorder_ports), int(n)
def pop(self, n=None):
"""Call from main thread. Returns the list of newly-available (handle, env) pairs."""
self.error_buffer.check()
envs = []
if n is None:
while True:
try:
envs += self.ready.get(block=False)
except queue.Empty:
break
else:
sync_timeout = 10 * 60
start = time.time()
wait_time = 1
while len(envs) < n:
try:
extra_logger.info('[%s] Waiting for %d envs, currently at %d, sleeping for %d', self.label, n, len(envs), wait_time)
envs += self.ready.get(timeout=wait_time)
except queue.Empty:
self.error_buffer.check()
wait_time = min(wait_time * 2, 30)
delta = time.time() - start
if delta > sync_timeout:
raise FatalError("Waited %.0fs to obtain envs, timeout was %.0fs. (Obtained %d/%d envs.)" % (delta, sync_timeout, len(envs), n))
return envs
def allocate(self, handles, initial=False, params={}):
"""Call from main thread. Initiate a request for more environments"""
assert all(re.search('^\d+$', h) for h in handles), "All handles must be numbers: {}".format(handles)
self.requests.put(('allocate', (handles, initial, params)))
def close(self):
self.requests.put(('close', ()))
def run(self):
try:
self._run()
except Stop:
pass
except Exception as e:
self.error_buffer.record(e)
def _run(self):
while True:
self._process_requests()
self._poll()
def _process_requests(self):
while True:
try:
method, args = self.requests.get(timeout=self._sleep)
except queue.Empty:
break
else:
if method == 'allocate':
handles, initial, params = args
self._allocate(handles, initial, params)
elif method == 'close':
raise Stop
def _allocate(self, handles, initial, params):
self._sleep = 1
_params = self.params.copy()
_params.update(params)
for handle in handles:
history = self._reconnect_history.get(handle, [])
history.append(time.time())
floor = time.time() - 5 * 60
history = [entry for entry in history if entry > floor]
if len(history) > 5:
raise error.Error('Tried reallocating a fresh remote at index {} a total of {} times in the past 5 minutes (at {}). Please examine the logs to determine why the remotes keep failing.'.format(handle, len(history), history))
self._reconnect_history[handle] = history
assert all(re.search('^\d+$', h) for h in handles), "All handles must be numbers: {}".format(handles)
allocation = self.with_retries(self._requestor.allocation_create,
client_id=self.client_id,
runtime_id=self.runtime_id,
placement=self.placement,
params=_params,
handles=handles,
initial=initial,
)
news = len([entry for entry in allocation['info']['n'] if entry['new']])
extra_logger.info('[%s] Received allocation with %s new and %s existing envs: %s', self.label, news, len(allocation['info']['n']) - news, allocation)
assert len(allocation['env_n']) <= len(handles), "Received more envs than requested: allocation={} handles={}".format(allocation, handles)
_, pending = self._handle_allocation(allocation)
for env in pending:
self.pending[env['name']] = {
'handle': env['handle'],
'params': params,
'received_at': time.time()
}
def _poll(self):
self._sleep = min(20, self._sleep + 2)
if len(self.pending) == 0:
return
for name, spec in self.pending.items():
delta = time.time() - spec['received_at']
if delta > self.start_timeout:
raise error.TimeoutError('Waited {}s for {} to get an IP, which exceeds start_timeout of {}'.format(delta, name, self.start_timeout))
names = list(self.pending.keys())
# This really should be an allocation_get, but it's possible
# the pods list will be long. So it's either GET with a body,
# or POST what should really be a GET. We do the latter.
allocation = self.with_retries(self._requestor.allocation_refresh, self.client_id, names=names)
assert len(allocation['env_n']) <= len(names), "Received more envs than requested: allocation={} names={}".format(allocation, names)
# Handle any envs which have gone missing
result = set(env['name'] for env in allocation['env_n'])
dropped = [p for p in self.pending.keys() if p not in result]
if len(dropped) > 0:
logger.info('Pending remote envs %s were not returned by the allocator (only %s were returned). Assuming the missing ones have gone down and requesting replacements.', dropped, list(result))
for d in dropped:
spec = self.pending.pop(d)
self._allocate([spec['handle']], False, spec['params'])
# Handle successful allocations
self._handle_allocation(allocation, pop=True)
def _handle_allocation(self, allocation, pop=False):
ready = []
not_ready = []
for alloc_env in allocation['env_n']:
if alloc_env['status'] != 'allocated':
not_ready.append(alloc_env)
continue
if pop:
self.pending.pop(alloc_env['name'])
vnc_address = alloc_env['vnc_recorder_address'] if self.use_recorder_ports else alloc_env['vnc_address']
rewarder_address = alloc_env['rewarder_recorder_address'] if self.use_recorder_ports else alloc_env['rewarder_address']
env = remote.Remote(
name=alloc_env['name'],
handle=alloc_env['handle'],
vnc_address=vnc_address,
vnc_password=alloc_env['vnc_password'],
rewarder_address=rewarder_address,
rewarder_password=alloc_env['rewarder_password'],
)
ready.append(env)
if len(ready) > 0:
extra_logger.info('[%s] The following envs now have IPs, but still may take time to boot: %s', self.label, ready)
self.ready.put(ready)
return ready, not_ready
def with_retries(self, method, *args, **kwargs):
timeout = 20 * 60
start = time.time()
i = 0
while True:
try:
return method(*args, **kwargs)
except FatalError as e:
logger.error('[%s] %s', self.label, e)
self.error_buffer.record(e)
raise
except Exception as e:
delta = time.time() - start
if delta > timeout:
raise error.TimeoutError('Have been unable to connect to the allocator at {} for {}s. Giving up. Last error: {}'.format(self.base_url, delta, e))
i += 1
sleep = min(2**i, 60)
time.sleep(sleep)
logger.error('[%s] Error making request to allocator: %s. Will retry in %ss (and timeout in %.0fs)', self.label, e, sleep, start + timeout - time.time())
class AllocatorClient(object):
def __init__(self, label, api_key, base_url):
self.label = label
self.api_key = api_key
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({'Content-type': 'application/json'})
self.request_timeout = 80
def _handle_resp(self, resp):
try:
parsed = resp.json()
except ValueError as e:
if resp.status_code == 500:
raise RequestError(message="500 Internal Error (retrying automatically): response={}".format(resp.content), status_code=resp.status_code)
elif resp.status_code == 503:
raise RequestError(message="503 Error from server (allocator is probably overloaded): response={}".format(resp.content), status_code=resp.status_code)
elif resp.status_code == 200:
raise RequestError(message="Response from server: status_code={} response={}".format(resp.status_code, resp.content), status_code=resp.status_code)
else:
raise RequestError(message="Error from server: status_code={} response={}".format(resp.status_code, resp.content), status_code=resp.status_code)
if resp.status_code == 200:
return parsed
elif 'detail' in parsed:
raise FatalError(message=parsed['detail'], status_code=resp.status_code)
else:
raise RequestError(message='Malformed response from allocator, missing "detail" key: {}'.format(parsed), status_code=resp.status_code)
def _post_request(self, route, data, description):
url = urlparse.urljoin(self.base_url, route)
extra_logger.info('[%s] %s: POST %s: %s', self.label, description, url, json.dumps(data))
resp = self.session.post(urlparse.urljoin(self.base_url, route),
data=json.dumps(data), auth=(self.api_key, ''),
timeout=self.request_timeout,
)
return self._handle_resp(resp)
def _delete_request(self, route):
url = urlparse.urljoin(self.base_url, route)
extra_logger.info("[%s] DELETE %s", self.label, url)
resp = self.session.delete(url, auth=(self.api_key, ''), timeout=self.request_timeout)
return self._handle_resp(resp)
def _get_request(self, route):
url = urlparse.urljoin(self.base_url, route)
extra_logger.info("[%s] GET %s", self.label, url)
resp = self.session.get(url, auth=(self.api_key, ''), timeout=self.request_timeout)
return self._handle_resp(resp)
def allocation_create(self, client_id, runtime_id, handles, params={}, placement='public', initial=False):
route = '/v1/allocations'
data = {'client': client_id, 'runtime': runtime_id, 'params': params, 'handles': handles, 'initial': initial, 'placement': placement}
logger.info('Requesting %s environment%s from %s: %s', len(handles), 's' if len(handles) != 1 else '', self.base_url, data)
resp = self._post_request(route, data, description='requesting new allocation')
return resp
def allocation_refresh(self, id, names):
route = '/v1/allocations/{}'.format(id)
resp = self._post_request(route, data={'names': names}, description='refreshing existing allocation')
return resp
def allocation_delete(self, id, names):
route = '/v1/allocations/{}'.format(id)
resp = self._post_request(route, {'names': names}, 'deleting existing allocation')
return resp
================================================
FILE: universe/remotes/build.py
================================================
import re
from universe import error
from universe.remotes.allocator_remote import AllocatorManager
from universe.remotes.docker_remote import DockerManager
from universe.remotes.hardcoded_addresses import HardcodedAddresses
def build(client_id, remotes, runtime=None, start_timeout=None, **kwargs):
if isinstance(remotes, int):
remotes = str(remotes)
elif not isinstance(remotes, str):
raise error.Error('remotes argument must be a string, got {} which is of type {}'.format(remotes, type(remotes)))
if re.search('^\d+$', remotes): # an integer, like -r 20
n = int(remotes)
return DockerManager(
runtime=runtime,
start_timeout=start_timeout,
reuse=kwargs.get('reuse', False),
n=n,
), n
elif remotes.startswith('vnc://'):
return HardcodedAddresses.build(
remotes,
start_timeout=start_timeout)
elif remotes.startswith('http://') or remotes.startswith('https://'):
if runtime is None:
raise error.Error('Must provide a runtime. HINT: try creating your env instance via gym.make("flashgames.DuskDrive-v0")')
manager, n = AllocatorManager.from_remotes(
client_id,
remotes,
runtime_id=runtime.id,
runtime_tag=runtime.image.split(':')[-1],
start_timeout=start_timeout,
api_key=kwargs.get('api_key'),
use_recorder_ports=kwargs.get('use_recorder_ports', False),
)
manager.start()
return manager, n
else:
raise error.Error('Invalid remotes: {!r}. Must be an integer or must start with vnc:// or https://'.format(remotes))
================================================
FILE: universe/remotes/compose/__init__.py
================================================
================================================
FILE: universe/remotes/compose/colors.py
================================================
from __future__ import absolute_import
from __future__ import unicode_literals
NAMES = [
'grey',
'red',
'green',
'yellow',
'blue',
'magenta',
'cyan',
'white'
]
def get_pairs():
for i, name in enumerate(NAMES):
yield(name, str(30 + i))
yield('intense_' + name, str(30 + i) + ';1')
def ansi(code):
return '\033[{0}m'.format(code)
def ansi_color(code, s):
return '{0}{1}{2}'.format(ansi(code), s, ansi(0))
def make_color_fn(code):
return lambda s: ansi_color(code, s)
for (name, code) in get_pairs():
globals()[name] = make_color_fn(code)
def rainbow():
cs = ['cyan', 'yellow', 'green', 'magenta', 'red', 'blue',
'intense_cyan', 'intense_yellow', 'intense_green',
'intense_magenta', 'intense_red', 'intense_blue']
for c in cs:
yield globals()[c]
================================================
FILE: universe/remotes/compose/container.py
================================================
from __future__ import absolute_import
from __future__ import unicode_literals
from functools import reduce
import six
LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
LABEL_ONE_OFF = 'com.docker.compose.oneoff'
LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version'
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
class Container(object):
"""
Represents a Docker container, constructed from the output of
GET /containers/:id:/json.
"""
def __init__(self, client, dictionary, has_been_inspected=False):
self.client = client
self.dictionary = dictionary
self.has_been_inspected = has_been_inspected
self.log_stream = None
@classmethod
def from_ps(cls, client, dictionary, **kwargs):
"""
Construct a container object from the output of GET /containers/json.
"""
name = get_container_name(dictionary)
if name is None:
return None
new_dictionary = {
'Id': dictionary['Id'],
'Image': dictionary['Image'],
'Name': '/' + name,
}
return cls(client, new_dictionary, **kwargs)
@classmethod
def from_id(cls, client, id):
return cls(client, client.inspect_container(id), has_been_inspected=True)
@classmethod
def create(cls, client, **options):
response = client.create_container(**options)
return cls.from_id(client, response['Id'])
@property
def id(self):
return self.dictionary['Id']
@property
def image(self):
return self.dictionary['Image']
@property
def image_config(self):
return self.client.inspect_image(self.image)
@property
def short_id(self):
return self.id[:12]
@property
def name(self):
return self.dictionary['Name'][1:]
@property
def service(self):
return self.labels.get(LABEL_SERVICE)
@property
def name_without_project(self):
project = self.labels.get(LABEL_PROJECT)
if self.name.startswith('{0}_{1}'.format(project, self.service)):
return '{0}_{1}'.format(self.service, self.number)
else:
return self.name
@property
def number(self):
number = self.labels.get(LABEL_CONTAINER_NUMBER)
if not number:
raise ValueError("Container {0} does not have a {1} label".format(
self.short_id, LABEL_CONTAINER_NUMBER))
return int(number)
@property
def ports(self):
self.inspect_if_not_inspected()
return self.get('NetworkSettings.Ports') or {}
@property
def human_readable_ports(self):
def format_port(private, public):
if not public:
return private
return '{HostIp}:{HostPort}->{private}'.format(
private=private, **public[0])
return ', '.join(format_port(*item)
for item in sorted(six.iteritems(self.ports)))
@property
def labels(self):
return self.get('Config.Labels') or {}
@property
def stop_signal(self):
return self.get('Config.StopSignal')
@property
def log_config(self):
return self.get('HostConfig.LogConfig') or None
@property
def human_readable_state(self):
if self.is_paused:
return 'Paused'
if self.is_restarting:
return 'Restarting'
if self.is_running:
return 'Ghost' if self.get('State.Ghost') else 'Up'
else:
return 'Exit %s' % self.get('State.ExitCode')
@property
def human_readable_command(self):
entrypoint = self.get('Config.Entrypoint') or []
cmd = self.get('Config.Cmd') or []
return ' '.join(entrypoint + cmd)
@property
def environment(self):
def parse_env(var):
if '=' in var:
return var.split("=", 1)
return var, None
return dict(parse_env(var) for var in self.get('Config.Env') or [])
@property
def exit_code(self):
return self.get('State.ExitCode')
@property
def is_running(self):
return self.get('State.Running')
@property
def is_restarting(self):
return self.get('State.Restarting')
@property
def is_paused(self):
return self.get('State.Paused')
@property
def log_driver(self):
return self.get('HostConfig.LogConfig.Type')
@property
def has_api_logs(self):
log_type = self.log_driver
return not log_type or log_type != 'none'
def attach_log_stream(self):
"""A log stream can only be attached if the container uses a json-file
log driver.
"""
if self.has_api_logs:
self.log_stream = self.attach(stdout=True, stderr=True, stream=True)
def get(self, key):
"""Return a value from the container or None if the value is not set.
:param key: a string using dotted notation for nested dictionary
lookups
"""
self.inspect_if_not_inspected()
def get_value(dictionary, key):
return (dictionary or {}).get(key)
return reduce(get_value, key.split('.'), self.dictionary)
def get_local_port(self, port, protocol='tcp'):
port = self.ports.get("%s/%s" % (port, protocol))
return "{HostIp}:{HostPort}".format(**port[0]) if port else None
def get_mount(self, mount_dest):
for mount in self.get('Mounts'):
if mount['Destination'] == mount_dest:
return mount
return None
def start(self, **options):
return self.client.start(self.id, **options)
def stop(self, **options):
return self.client.stop(self.id, **options)
def pause(self, **options):
return self.client.pause(self.id, **options)
def unpause(self, **options):
return self.client.unpause(self.id, **options)
def kill(self, **options):
return self.client.kill(self.id, **options)
def restart(self, **options):
return self.client.restart(self.id, **options)
def remove(self, **options):
return self.client.remove_container(self.id, **options)
def create_exec(self, command, **options):
return self.client.exec_create(self.id, command, **options)
def start_exec(self, exec_id, **options):
return self.client.exec_start(exec_id, **options)
def rename_to_tmp_name(self):
"""Rename the container to a hopefully unique temporary container name
by prepending the short id.
"""
self.client.rename(
self.id,
'%s_%s' % (self.short_id, self.name)
)
def inspect_if_not_inspected(self):
if not self.has_been_inspected:
self.inspect()
def wait(self):
return self.client.wait(self.id)
def logs(self, *args, **kwargs):
return self.client.logs(self.id, *args, **kwargs)
def inspect(self):
self.dictionary = self.client.inspect_container(self.id)
self.has_been_inspected = True
return self.dictionary
def attach(self, *args, **kwargs):
return self.client.attach(self.id, *args, **kwargs)
def __repr__(self):
return '' % (self.name, self.id[:6])
def __eq__(self, other):
if type(self) != type(other):
return False
return self.id == other.id
def __hash__(self):
return self.id.__hash__()
def get_container_name(container):
if not container.get('Name') and not container.get('Names'):
return None
# inspect
if 'Name' in container:
return container['Name']
# ps
shortest_name = min(container['Names'], key=lambda n: len(n.split('/')))
return shortest_name.split('/')[-1]
================================================
FILE: universe/remotes/compose/log_printer.py
================================================
# Forked from docker-compose
from __future__ import absolute_import
from __future__ import unicode_literals
import sys
import os
import threading
from collections import namedtuple
from itertools import cycle
from threading import Thread
from six.moves import _thread as thread
from six.moves.queue import Empty
from six.moves.queue import Queue
from universe.remotes.compose import colors, utils
from universe.remotes.compose.signals import ShutdownException
from universe.remotes.compose.utils import split_buffer
def build(containers, service_names, **kwargs):
monochrome = not os.isatty(1)
presenters = build_log_presenters(service_names, monochrome)
printer = LogPrinter(containers, presenters, **kwargs)
printer.start()
class LogPresenter(object):
def __init__(self, prefix_width, color_func):
self.prefix_width = prefix_width
self.color_func = color_func
def present(self, container, line):
prefix = container.name_without_project.ljust(self.prefix_width)
return '{prefix} {line}'.format(
prefix=self.color_func(prefix + ' |'),
line=line)
def build_log_presenters(service_names, monochrome):
"""Return an iterable of functions.
Each function can be used to format the logs output of a container.
"""
prefix_width = max_name_width(service_names)
def no_color(text):
return text
for color_func in cycle([no_color] if monochrome else colors.rainbow()):
yield LogPresenter(prefix_width, color_func)
def max_name_width(service_names, max_index_width=3):
"""Calculate the maximum width of container names so we can make the log
prefixes line up like so:
db_1 | Listening
web_1 | Listening
"""
return max(len(name) for name in service_names) + max_index_width
class LogPrinter(threading.Thread):
"""Print logs from many containers to a single output stream."""
daemon = True
def __init__(self,
containers,
presenters,
output=sys.stdout,
cascade_stop=False,
log_args=None):
super(LogPrinter, self).__init__(name='LogPrinter')
self.containers = containers
self.presenters = presenters
self.output = utils.get_output_stream(output)
self.cascade_stop = cascade_stop
self.log_args = log_args or {}
def run(self):
if not self.containers:
return
queue = Queue()
thread_args = queue, self.log_args
thread_map = build_thread_map(self.containers, self.presenters, thread_args)
for line in consume_queue(queue, self.cascade_stop):
remove_stopped_threads(thread_map)
if not line:
if not thread_map:
# There are no running containers left to tail, so exit
return
# We got an empty line because of a timeout, but there are still
# active containers to tail, so continue
continue
try:
self.output.write(line)
self.output.flush()
except ValueError:
# ValueError: I/O operation on closed file
break
def remove_stopped_threads(thread_map):
for container_id, tailer_thread in list(thread_map.items()):
if not tailer_thread.is_alive():
thread_map.pop(container_id, None)
def build_thread(container, presenter, queue, log_args):
tailer = Thread(
target=tail_container_logs,
args=(container, presenter, queue, log_args))
tailer.daemon = True
tailer.start()
return tailer
def build_thread_map(initial_containers, presenters, thread_args):
return {
container.id: build_thread(container, next(presenters), *thread_args)
for container in initial_containers
}
class QueueItem(namedtuple('_QueueItem', 'item is_stop exc')):
@classmethod
def new(cls, item):
return cls(item, None, None)
@classmethod
def exception(cls, exc):
return cls(None, None, exc)
@classmethod
def stop(cls):
return cls(None, True, None)
def tail_container_logs(container, presenter, queue, log_args):
generator = get_log_generator(container)
try:
for item in generator(container, log_args):
queue.put(QueueItem.new(presenter.present(container, item)))
except Exception as e:
queue.put(QueueItem.exception(e))
return
if log_args.get('follow'):
queue.put(QueueItem.new(presenter.color_func(wait_on_exit(container))))
queue.put(QueueItem.stop())
def get_log_generator(container):
if container.has_api_logs:
return build_log_generator
return build_no_log_generator
def build_no_log_generator(container, log_args):
"""Return a generator that prints a warning about logs and waits for
container to exit.
"""
yield "WARNING: no logs are available with the '{}' log driver\n".format(
container.log_driver)
def build_log_generator(container, log_args):
# if the container doesn't have a log_stream we need to attach to container
# before log printer starts running
if container.log_stream is None:
stream = container.logs(stdout=True, stderr=True, stream=True, **log_args)
else:
stream = container.log_stream
return split_buffer(stream)
def wait_on_exit(container):
exit_code = container.wait()
return "%s exited with code %s\n" % (container.name, exit_code)
def start_producer_thread(thread_args):
producer = Thread(target=watch_events, args=thread_args)
producer.daemon = True
producer.start()
def watch_events(thread_map, event_stream, presenters, thread_args):
for event in event_stream:
if event['action'] == 'stop':
thread_map.pop(event['id'], None)
if event['action'] != 'start':
continue
if event['id'] in thread_map:
if thread_map[event['id']].is_alive():
continue
# Container was stopped and started, we need a new thread
thread_map.pop(event['id'], None)
thread_map[event['id']] = build_thread(
event['container'],
next(presenters),
*thread_args)
def consume_queue(queue, cascade_stop):
"""Consume the queue by reading lines off of it and yielding them."""
while True:
try:
item = queue.get(timeout=0.1)
except Empty:
yield None
continue
# See https://github.com/docker/compose/issues/189
except thread.error:
raise ShutdownException()
if item.exc:
raise item.exc
if item.is_stop:
if cascade_stop:
raise StopIteration
else:
continue
yield item.item
================================================
FILE: universe/remotes/compose/progress_stream.py
================================================
from __future__ import absolute_import
from __future__ import unicode_literals
from universe.remotes.compose import utils
class StreamOutputError(Exception):
pass
def stream_output(output, stream):
is_terminal = hasattr(stream, 'isatty') and stream.isatty()
stream = utils.get_output_stream(stream)
all_events = []
lines = {}
diff = 0
for event in utils.json_stream(output):
all_events.append(event)
is_progress_event = 'progress' in event or 'progressDetail' in event
if not is_progress_event:
print_output_event(event, stream, is_terminal)
stream.flush()
continue
if not is_terminal:
continue
# if it's a progress event and we have a terminal, then display the progress bars
image_id = event.get('id')
if not image_id:
continue
if image_id in lines:
diff = len(lines) - lines[image_id]
else:
lines[image_id] = len(lines)
stream.write("\n")
diff = 0
# move cursor up `diff` rows
stream.write("%c[%dA" % (27, diff))
print_output_event(event, stream, is_terminal)
if 'id' in event:
# move cursor back down
stream.write("%c[%dB" % (27, diff))
stream.flush()
return all_events
def print_output_event(event, stream, is_terminal):
if 'errorDetail' in event:
raise StreamOutputError(event['errorDetail']['message'])
terminator = ''
if is_terminal and 'stream' not in event:
# erase current line
stream.write("%c[2K\r" % 27)
terminator = "\r"
elif 'progressDetail' in event:
return
if 'time' in event:
stream.write("[%s] " % event['time'])
if 'id' in event:
stream.write("%s: " % event['id'])
if 'from' in event:
stream.write("(from %s) " % event['from'])
status = event.get('status', '')
if 'progress' in event:
stream.write("%s %s%s" % (status, event['progress'], terminator))
elif 'progressDetail' in event:
detail = event['progressDetail']
total = detail.get('total')
if 'current' in detail and total:
percentage = float(detail['current']) / float(total) * 100
stream.write('%s (%.1f%%)%s' % (status, percentage, terminator))
else:
stream.write('%s%s' % (status, terminator))
elif 'stream' in event:
stream.write("%s%s" % (event['stream'], terminator))
else:
stream.write("%s%s\n" % (status, terminator))
def get_digest_from_pull(events):
for event in events:
status = event.get('status')
if not status or 'Digest' not in status:
continue
_, digest = status.split(':', 1)
return digest.strip()
return None
def get_digest_from_push(events):
for event in events:
digest = event.get('aux', {}).get('Digest')
if digest:
return digest
return None
================================================
FILE: universe/remotes/compose/signals.py
================================================
from __future__ import absolute_import
from __future__ import unicode_literals
import signal
class ShutdownException(Exception):
pass
def shutdown(signal, frame):
raise ShutdownException()
def set_signal_handler(handler):
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
def set_signal_handler_to_shutdown():
set_signal_handler(shutdown)
================================================
FILE: universe/remotes/compose/utils.py
================================================
from __future__ import absolute_import
from __future__ import unicode_literals
import codecs
import hashlib
import json
import json.decoder
import six
json_decoder = json.JSONDecoder()
def get_output_stream(stream):
if six.PY3:
return stream
return codecs.getwriter('utf-8')(stream)
def stream_as_text(stream):
"""Given a stream of bytes or text, if any of the items in the stream
are bytes convert them to text.
This function can be removed once docker-py returns text streams instead
of byte streams.
"""
for data in stream:
if not isinstance(data, six.text_type):
data = data.decode('utf-8', 'replace')
yield data
def line_splitter(buffer, separator=u'\n'):
index = buffer.find(six.text_type(separator))
if index == -1:
return None
return buffer[:index + 1], buffer[index + 1:]
def split_buffer(stream, splitter=None, decoder=lambda a: a):
"""Given a generator which yields strings and a splitter function,
joins all input, splits on the separator and yields each chunk.
Unlike string.split(), each chunk includes the trailing
separator, except for the last one if none was found on the end
of the input.
"""
splitter = splitter or line_splitter
buffered = six.text_type('')
for data in stream_as_text(stream):
buffered += data
while True:
buffer_split = splitter(buffered)
if buffer_split is None:
break
item, buffered = buffer_split
yield item
if buffered:
yield decoder(buffered)
def json_splitter(buffer):
"""Attempt to parse a json object from a buffer. If there is at least one
object, return it and the rest of the buffer, otherwise return None.
"""
try:
obj, index = json_decoder.raw_decode(buffer)
rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end():]
return obj, rest
except ValueError:
return None
def json_stream(stream):
"""Given a stream of text, return a stream of json objects.
This handles streams which are inconsistently buffered (some entries may
be newline delimited, and others are not).
"""
return split_buffer(stream, json_splitter, json_decoder.decode)
def json_hash(obj):
dump = json.dumps(obj, sort_keys=True, separators=(',', ':'))
h = hashlib.sha256()
h.update(dump.encode('utf8'))
return h.hexdigest()
def microseconds_from_time_nano(time_nano):
return int(time_nano % 1000000000 / 1000)
def build_string_dict(source_dict):
return dict((k, str(v if v is not None else '')) for k, v in source_dict.items())
================================================
FILE: universe/remotes/docker_remote.py
================================================
from __future__ import absolute_import
import base64
import logging
import os
import pipes
import sys
import threading
import uuid
import time, random
import docker
import six.moves.urllib.parse as urlparse
from gym.utils import closer
from universe import error
from universe.remotes import healthcheck, remote
from universe import error, utils
from universe.remotes.compose import container, log_printer, progress_stream
logger = logging.getLogger(__name__)
docker_closer = closer.Closer()
def random_alphanumeric(length=14):
buf = []
while len(buf) < length:
entropy = base64.encodestring(uuid.uuid4().bytes).decode('utf-8')
bytes = [c for c in entropy if c.isalnum()]
buf += bytes
return ''.join(buf)[:length]
def pretty_command(command):
return ' '.join(pipes.quote(c) for c in command)
class DockerManager(object):
def __init__(self, runtime, n, reuse=False, start_timeout=None):
super(DockerManager, self).__init__()
self.runtime = runtime
self.supports_reconnect = False
self.connect_vnc = True
self.connect_rewarder = True
self._assigner = PortAssigner(reuse=reuse)
self._popped = False
self.lock = threading.Lock()
self.envs = []
self._n = n
if start_timeout is None:
start_timeout = 2 * self._n + 5
self.start_timeout = start_timeout
self._start()
def allocate(self, handles, initial=False, params={}):
self._handles = handles
def pop(self, n=None):
"""Call from main thread. Returns the list of newly-available (handle, env) pairs."""
if self._popped:
assert n is None
return []
self._popped = True
envs = []
for i, instance in enumerate(self.instances):
env = remote.Remote(
handle=self._handles[i],
vnc_address='{}:{}'.format(instance.host, instance.vnc_port),
vnc_password='openai',
rewarder_address='{}:{}'.format(instance.host, instance.rewarder_port),
rewarder_password='openai',
)
envs.append(env)
return envs
def _start(self):
self.instances = [DockerInstance(self._assigner, self.runtime, label=str(i)) for i in range(self._n)]
[instance.start() for instance in self.instances]
if int(os.environ.get('OPENAI_REMOTE_VERBOSE', '1')) > 0:
self.start_logging(self.instances)
self.healthcheck(self.instances)
def close(self):
with self.lock:
[instance.close() for instance in self.instances]
def start_logging(self, instances):
containers = [instance._container for instance in instances]
labels = [str(instance.label) for instance in instances]
if all(instance.reusing for instance in instances):
# All containers are being reused, so only bother showing
# a subset of the backlog.
tail = 0
else:
# At least one container is new, so just show
# everything. It'd be nice to have finer-grained control,
# but this would require patching the log printer.
tail = 'all'
log_printer.build(containers, labels, log_args={'tail': tail})
def healthcheck(self, instances):
# Wait for boot
healthcheck.run(
['{}:{}'.format(instance.assigner.info['host'], instance.vnc_port) for instance in instances],
['{}:{}'.format(instance.assigner.info['host'], instance.rewarder_port) for instance in instances],
start_timeout=30,
)
def get_client():
"""
Set DOCKER_HOST (and probably DOCKER_TLS_VERIFY and DOCKER_CERT_PATH) to connect to a docker instance through TCP.
Leave DOCKER_HOST unset and it will use the default, typically unix:/var/run/docker.sock
It also needs to know how to connect to ports on the docker container after creating it.
Set DOCKER_NET_HOST to provide an IP address to connect to the VNC ports on
otherwise if DOCKER_HOST has a hostname, it will connect to the VNC ports using that name.
otherwise it connects using localhost
"""
info = {}
host = os.environ.get('DOCKER_HOST')
net_host = os.environ.get('DOCKER_NET_HOST')
client_api_version = os.environ.get('DOCKER_API_VERSION')
if not client_api_version:
client_api_version = "auto"
# IP to use for started containers
if net_host:
info['host'] = net_host
elif host:
info['host'] = urlparse.urlparse(host).netloc.split(':')[0]
else:
info['host'] = 'localhost'
verify = os.environ.get('DOCKER_TLS_VERIFY') == '1'
if verify: # use TLS
assert_hostname = None
cert_path = os.environ.get('DOCKER_CERT_PATH')
if cert_path:
client_cert = (os.path.join(cert_path, 'cert.pem'), os.path.join(cert_path, 'key.pem'))
ca_cert = os.path.join(cert_path, 'ca.pem')
else:
client_cert = ca_cert = None
tls_config = docker.tls.TLSConfig(
client_cert=client_cert,
ca_cert=ca_cert,
verify=verify,
assert_hostname=assert_hostname,
)
return docker.Client(base_url=host, tls=tls_config, version=client_api_version), info
else:
return docker.Client(base_url=host, version=client_api_version), info
class PortAssigner(object):
def __init__(self, reuse=False):
self.reuse = reuse
self.instance_id = 'universe-' + random_alphanumeric(length=6)
self.client, self.info = get_client()
self._next_port = 5900
self._refresh_ports()
def _refresh_ports(self):
ports = {}
for container in self.client.containers():
for port in container['Ports']:
# {u'IP': u'0.0.0.0', u'Type': u'tcp', u'PublicPort': 5000, u'PrivatePort': 500}
if port['Type'] == 'tcp' and 'PublicPort' in port:
ports[port['PublicPort']] = container['Id']
logger.info('Ports used: %s', ports.keys())
self._ports = ports
def allocate_ports(self, num):
if self.reuse and self._next_port in self._ports:
vnc_id = self._ports[self._next_port]
rewarder_id = self._ports.get(self._next_port+10000)
# Reuse an existing docker container if it exists
if (self._next_port+10000) not in self._ports:
raise error.Error("Port {} was allocated but {} was not. This indicates unexpected state with spun-up VNC docker instances.".format(self._next_port, self._next_port+1))
elif vnc_id != rewarder_id:
raise error.Error("Port {} is exposed from {} while {} is exposed from {}. Both should come from a single Docker instance running your environment.".format(vnc_id, self._next_port, rewarder_id, self._next_port+10000))
base = self._next_port
self._next_port += 1
return base, base+10000, vnc_id
elif not self.reuse:
# Otherwise, allocate find the lowest free pair of
# ports. This doesn't work for the reuse case since on
# restart we won't remember where we spun up our
# containers.
while self._next_port in self._ports or (self._next_port+10000) in self._ports:
self._next_port += 1
base = self._next_port
self._next_port += 1
# And get started!
return base, base+10000, None
class DockerInstance(object):
def __init__(self, assigner, runtime, label='main'):
self._docker_closer_id = docker_closer.register(self)
self.label = label
self.assigner = assigner
self.name='{}-{}'.format(self.assigner.instance_id, self.label),
self.runtime = runtime
self._container_id = None
self._closed = False
self._container = None
self.host = self.assigner.info['host']
self.vnc_port = None
self.rewarder_port = None
self.reusing = None
self.started = False
def start(self, attempts=None):
if attempts is None:
# If we're reusing, we don't scan through ports for a free
# one.
if not self.assigner.reuse:
attempts = 20
else:
attempts = 1
for attempt in range(attempts):
self._spawn()
e = self._start()
if e is None:
return
time.sleep(random.uniform(1.0, 5.0))
self.assigner._refresh_ports()
raise error.Error('[{}] Could not start container after {} attempts. Last error: {}'.format(self.label, attempts, e))
def _spawn(self):
if self.runtime.image is None:
raise error.Error('No image specified')
assert self._container_id is None
self.vnc_port, self.rewarder_port, self._container_id = self.assigner.allocate_ports(2)
if self._container_id is not None:
logger.info('[%s] Reusing container %s on ports %s and %s', self.label, self._container_id[:12], self.vnc_port, self.rewarder_port)
self.reusing = True
self.started = True
return
self.reusing = False
logger.info('[%s] Creating container: image=%s. Run the same thing by hand as: %s',
self.label,
self.runtime.image,
pretty_command(self.runtime.cli_command(self.vnc_port, self.rewarder_port)))
try:
container = self._spawn_container()
except docker.errors.NotFound as e:
# Looks like we need to pull the image
assert 'No such image' in e.explanation.decode('utf-8'), 'Expected NotFound error message message to include "No such image", but it was: {}. This is probably just a bug in this assertion and the assumption was incorrect'.format(e.explanation)
logger.info('Image %s not present locally; pulling', self.runtime.image)
self._pull_image()
# If we called pull_image from multiple processes (as we do with universe-starter-agent A3C)
# these will all return at the same time. We probably all got the same port numbers before the pull started,
# so wait a short random time and refresh our port numbers
time.sleep(random.uniform(0.5, 2.5))
self.assigner._refresh_ports()
self.vnc_port, self.rewarder_port, self._container_id = self.assigner.allocate_ports(2)
if self._container_id is not None:
logger.info('[%s] Reusing container %s on ports %s and %s', self.label, self._container_id[:12], self.vnc_port, self.rewarder_port)
self.reusing = True
self.started = True
return
# Try spawning again.
container = self._spawn_container()
self._container_id = container['Id']
def _pull_image(self):
output = self.client.pull(self.runtime.image, stream=True)
return progress_stream.get_digest_from_pull(
progress_stream.stream_output(output, sys.stdout))
# docker-compose uses this:
# try:
# except StreamOutputError as e:
# if not ignore_pull_failures:
# raise
# else:
# log.error(six.text_type(e))
def _spawn_container(self):
# launch instance, and refresh if error
container = self.client.create_container(
image=self.runtime.image,
command=self.runtime.command,
# environment=self.runtime.environment,
name=self.name,
host_config=self.client.create_host_config(
port_bindings={
5900: self.vnc_port,
15900: self.rewarder_port,
},
**self.runtime.host_config),
labels={
'com.openai.automanaged': 'true',
}
)
return container
def _start(self):
# Need to start up the container!
if not self.started:
logger.debug('[%s] Starting container: id=%s', self.label, self._container_id)
try:
self.client.start(container=self._container_id)
except docker.errors.APIError as e:
if 'port is already allocated' in str(e.explanation):
logger.info('[%s] Could not start container: %s', self.label, e)
self._remove()
return e
else:
raise
else:
self.started = True
self._container = container.Container.from_id(self.client, self._container_id)
return None
def _remove(self):
logger.info("Killing and removing container: id=%s", self._container_id)
try:
self.client.remove_container(container=self._container_id, force=True)
except docker.errors.APIError as e:
# This seems to happen occasionally when we try to delete a container immediately after creating it.
# But although we get an error trying to remove it, it usually goes away shortly
# A typical error message is
# Driver aufs failed to remove root filesystem 0015803583d91741d25fce28ae0ef540b436853d1c90061caacaef97e3682403: \
# rename /var/lib/docker/aufs/mnt/69a72854511f1fbb9d7cb0ef0ce0787e573af0887c1213ba3a0c3a0cfd71efd2 \
# /var/lib/docker/aufs/mnt/69a72854511f1fbb9d7cb0ef0ce0787e573af0887c1213ba3a0c3a0cfd71efd2-removing: \
# device or resource busy
# Just proceed as if it had gone away
if 'device or resource busy' in str(e.explanation):
logger.info("[%s] Could not remove container: %s. You can always kill all automanaged environments on this Docker daemon via: docker rm -f $(docker ps -q -a -f 'label=com.openai.automanaged=true')", self.label, e)
self._container_id = None
return e
else:
raise
self._container_id = None
def __del__(self):
self.close()
def close(self):
if self._closed:
return
docker_closer.unregister(self._docker_closer_id)
# Make sure 1. we were the onse who started it, 2. it's
# actually been started, and 3. we're meant to kill it.
if self._container_id and not self.assigner.reuse:
self._remove()
self._closed = True
@property
def client(self):
return self.assigner.client
if __name__ == '__main__':
logging.getLogger().setLevel(logging.INFO)
from universe.runtimes import registration
# docker run --name test --rm -ti -p 5900:5900 -p 15900:15900 quay.io/openai/universe.gym-core
instance = DockerManager(
runtime=registration.runtime_spec('gym-core'),
n=2,
)
instance.start()
import ipdb;ipdb.set_trace()
================================================
FILE: universe/remotes/hardcoded_addresses.py
================================================
import logging
import os
import re
import six.moves.urllib.parse as urlparse
from universe import error, utils
from universe.remotes import remote
logger = logging.getLogger(__name__)
class HardcodedAddresses(object):
@classmethod
def build(cls, remotes, **kwargs):
parsed = urlparse.urlparse(remotes)
if parsed.scheme != 'vnc':
raise error.Error('HardcodedAddresses must be initialized with a string starting with vnc://: {}'.format(remotes))
addresses = parsed.netloc.split(',')
query = urlparse.parse_qs(parsed.query)
# We could support per-backend passwords, but no need for it
# right now.
password = query.get('password', [utils.default_password()])[0]
vnc_addresses, rewarder_addresses = parse_remotes(addresses)
res = cls(vnc_addresses, rewarder_addresses, vnc_password=password, rewarder_password=password, **kwargs)
return res, res.available_n
def __init__(self, vnc_addresses, rewarder_addresses, vnc_password, rewarder_password, start_timeout=None):
if vnc_addresses is not None:
self.available_n = len(vnc_addresses)
elif rewarder_addresses is not None:
self.available_n = len(rewarder_addresses)
else:
assert False
self.supports_reconnect = False
self.connect_vnc = vnc_addresses is not None
self.connect_rewarder = rewarder_addresses is not None
if rewarder_addresses is None:
logger.info("No rewarder addresses were provided, so this env cannot connect to the remote's rewarder channel, and cannot send control messages (e.g. reset)")
self.vnc_addresses = vnc_addresses
self.vnc_password = vnc_password
self.rewarder_addresses = rewarder_addresses
self.rewarder_password = rewarder_password
if start_timeout is None:
start_timeout = 2 * self.available_n + 5
self.start_timeout = start_timeout
self._popped = False
def pop(self, n=None):
if self._popped:
assert n is None
return []
self._popped = True
remotes = []
for i in range(self.available_n):
if self.vnc_addresses is not None:
vnc_address = self.vnc_addresses[i]
else:
vnc_address = None
if self.rewarder_addresses is not None:
rewarder_address = self.rewarder_addresses[i]
else:
rewarder_address = None
name = self._handles[i]
env = remote.Remote(
handle=self._handles[i],
vnc_address=vnc_address,
vnc_password=self.vnc_password,
rewarder_address=rewarder_address,
rewarder_password=self.rewarder_password,
)
remotes.append(env)
return remotes
def allocate(self, handles, initial=False, params={}):
if len(handles) > self.available_n:
raise error.Error('Requested {} handles, but only have {} envs'.format(len(handles), self.available_n))
self.n = len(handles)
self._handles = handles
def close(self):
pass
def parse_remotes(remotes):
# Parse a list of remotes of the form:
#
# address:vnc_port+rewarder_port (e.g. localhost:5900+15900)
#
# either vnc_port or rewarder_port can be omitted, but not both
all_vnc = None
all_rewarder = None
vnc_addresses = []
rewarder_addresses = []
for remote in remotes:
# Parse off +, then :
if '+' in remote:
if all_vnc == False:
raise error.Error('Either all or no remotes must have rewarders: {}'.format(remotes))
all_vnc = True
remote, rewarder_port = remote.split('+')
if not re.match(r'^[0-9]+$', rewarder_port):
raise error.Error('Rewarder port must be an integer, not `{}`: {}'.format(rewarder_port, remotes))
rewarder_port = int(rewarder_port)
else:
if all_vnc == True:
raise error.Error('Either all or no remotes must have rewarders: {}'.format(remotes))
all_vnc = False
rewarder_port = None
if ':' in remote:
if all_rewarder == False:
raise error.Error('Either all or no remotes must have a VNC port: {}'.format(remotes))
all_rewarder = True
remote, vnc_port = remote.split(':')
if not re.match(r'^[0-9]+$', vnc_port):
raise error.Error('VNC port must be an integer, not `{}`: {}'.format(vnc_port, remotes))
vnc_port = int(vnc_port)
else:
if all_rewarder == True:
raise error.Error('Either all or no remotes must have a VNC port: {}'.format(remotes))
all_rewarder = False
vnc_port = None
all_rewarder = False
host = remote
if not re.match(r'^[-a-zA-Z0-9\.\_]+$', host):
raise error.Error('Invalid hostname for remote: {}'.format(remotes))
if rewarder_port is not None:
rewarder_address = '{}:{}'.format(host, rewarder_port)
rewarder_addresses.append(rewarder_address)
if vnc_port is not None:
vnc_address = '{}:{}'.format(host, vnc_port)
vnc_addresses.append(vnc_address)
if not all_vnc and not all_rewarder:
raise error.Error('You must provide either rewarder or a VNC port: {}'.format(remotes))
if not vnc_addresses:
vnc_addresses = None
if not rewarder_addresses:
rewarder_addresses = None
return vnc_addresses, rewarder_addresses
================================================
FILE: universe/remotes/healthcheck.py
================================================
import errno
import logging
import select
import socket
import time
from universe import error, utils
from gym.utils import reraise
logger = logging.getLogger(__name__)
def run(vnc_addresses, rewarder_addresses, timeout=None, start_timeout=None):
healthcheck = Healthcheck(vnc_addresses, rewarder_addresses, timeout=timeout, start_timeout=start_timeout)
healthcheck.run()
def host_port(address, default_port=None):
split = address.split(':')
if len(split) == 1:
host = split[0]
port = default_port
else:
host, port = split
port = int(port)
return host, port
class Healthcheck(object):
def __init__(self, vnc_addresses, rewarder_addresses, timeout=None, start_timeout=None):
self.timeout = timeout or (4 * len(vnc_addresses) + 20)
self.start_timeout = start_timeout
start_time = time.time()
self.sockets = {}
for address in vnc_addresses:
self._register_vnc(address, start_time)
for address in rewarder_addresses:
self._register_rewarder(address, start_time)
def _register_vnc(self, address, start_time=None):
if start_time is None:
start_time = time.time()
host, port = host_port(address, default_port=5900)
while True:
# In VNC, the server sends bytes upon connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
except (socket.error, socket.gaierror) as e:
# ECONNREFUSED: VNC env hasn't come up yet
# ETIMEDOUT: the packets can't be delivered yet, such as can happen on kubernetes
# gaierror: can't resolve the address yet, which can also happen on kubernetes
expected = socket.errno.ECONNREFUSED == e.errno or socket.errno.ETIMEDOUT == e.errno or isinstance(e, socket.gaierror)
if self.start_timeout is None or not expected:
reraise(suffix='while connecting to VNC server {}'.format(address))
logger.info('VNC server %s did not come up yet (error: %s). Sleeping for 1s.', address, e)
time.sleep(1)
else:
break
if time.time() - start_time > self.start_timeout:
raise error.Error('VNC server {} did not come up within {}s'.format(address, self.start_timeout))
self.sockets[sock] = ('vnc', address)
def _register_rewarder(self, address, start_time=None):
if start_time is None:
start_time = time.time()
host, port = host_port(address, default_port=15900)
while True:
# In WebSockets, the server sends bytes once we've upgraded the protocol
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
except (socket.error, socket.gaierror) as e:
# ECONNREFUSED: VNC env hasn't come up yet
# ETIMEDOUT: the packets can't be delivered yet, such as can happen on kubernetes
# gaierror: can't resolve the address yet, which can also happen on kubernetes
expected = socket.errno.ECONNREFUSED == e.errno or socket.errno.ETIMEDOUT == e.errno or isinstance(e, socket.gaierror)
if self.start_timeout is None or not expected:
reraise(suffix='while connecting to Rewarder server {}'.format(address))
logger.info('Rewarder server %s did not come up yet (error: %s). Sleeping for 1s.', address, e)
time.sleep(1)
else:
break
if time.time() - start_time > self.start_timeout:
raise error.Error('Rewarder server {} did not come up within {}s'.format(address, self.start_timeout))
# Send a websocket handshake.
# https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
#
# The port 10003 is an arbitrary port that we don't actually connect to, but needs to be a valid part
# e.g Host: 127.0.0.1:GARBAGE results in the following error: (invalid port 'GARBAGE' in HTTP Host header '127.0.0.1:GARBAGE')
sock.send(b'GET / HTTP/1.1\r\nHost: 127.0.0.1:10003\r\nUpgrade: WebSocket\r\nConnection:Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\nauthorization: ' + utils.basic_auth_encode('openai').encode('utf-8') + b'\r\nopenai-observer: true\r\n\r\n')
self.sockets[sock] = ('rewarder', address)
def run(self):
target = time.time() + self.timeout
while self.sockets:
remaining = target - time.time()
if remaining < 0:
break
ready, _, _ = select.select(self.sockets.keys(), [], [], remaining)
# Go through the readable sockets
remote_closed = False
for sock in ready:
type, address = self.sockets.pop(sock)
# Connection was closed; try again.
#
# This is guaranteed not to block.
try:
recv = sock.recv(1)
except socket.error as e:
if e.errno == errno.ECONNRESET:
recv = b''
else:
raise
if recv == b'':
logger.info('Remote closed: address=%s', address)
remote_closed = True
if type == 'rewarder':
self._register_rewarder(address)
else:
self._register_vnc(address)
else:
logger.debug('Healthcheck passed for %s %s', type, address)
sock.close()
if remote_closed:
sleep = 1
logger.info('At least one sockets was closed by the remote. Sleeping %ds...', sleep)
time.sleep(sleep)
if self.sockets:
raise error.Error('Not all servers came up within {}s: {}'.format(self.timeout, list(self.sockets.values())))
================================================
FILE: universe/remotes/remote.py
================================================
class Remote(object):
def __init__(self, handle, vnc_address, vnc_password, rewarder_address, rewarder_password, name=None):
self.name = name
self.handle = handle
self.vnc_address = vnc_address
self.vnc_password = vnc_password
self.rewarder_address = rewarder_address
self.rewarder_password = rewarder_password
def __str__(self):
return 'Remote<{}:{}>'.format(self.handle, self.name)
def __repr__(self):
return str(self)
================================================
FILE: universe/rewarder/__init__.py
================================================
from universe.rewarder.rewarder_session import RewarderSession
from universe.rewarder.env_status import EnvStatus, compare_ids
from universe.rewarder.merge import merge_n, merge_infos, merge_reward_n, merge_observation_n
from universe.rewarder.reward_buffer import RewardBuffer
================================================
FILE: universe/rewarder/connection_timer.py
================================================
import os
import re
import signal
import time
from universe import error
from universe.twisty import reactor
from twisted.internet import defer, protocol
import twisted.internet.error
import logging
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
class ConnectionTimer(protocol.Protocol):
def connectionMade(self):
self.transport.loseConnection()
def start(endpoint):
start = time.time()
return endpoint.connect(
protocol.ClientFactory.forProtocol(ConnectionTimer)
).addCallback(lambda _: time.time() - start)
def measure_clock_skew(label, host):
cmd = ['ntpdate', '-q', '-p', '8', host]
extra_logger.info('[%s] Starting network calibration with %s', label, ' '.join(cmd))
skew = Clockskew(label, cmd)
# TODO: search PATH for this?
process = reactor.spawnProcess(skew, '/usr/sbin/ntpdate', cmd, {})
# process = reactor.spawnProcess(skew, '/bin/sleep', ['sleep', '2'], {})
t = float(os.environ.get('UNIVERSE_NTPDATE_TIMEOUT', 20))
def timeout():
if process.pid:
logger.error('[%s] %s call timed out after %ss; killing the subprocess. This is ok, but you could have more accurate timings by enabling UDP port 123 traffic to your env. (Alternatively, you can try increasing the timeout by setting environment variable UNIVERSE_NTPDATE_TIMEOUT=10.)', label, ' '.join(cmd), t)
process.signalProcess(signal.SIGKILL)
process.reapProcess()
# TODO: make this part of the connection string
reactor.callLater(t, timeout)
return skew.deferred
class Clockskew(protocol.ProcessProtocol):
def __init__(self, label, cmd):
self.label = label
self._cmd = cmd
self.deferred = defer.Deferred()
self.out = []
self.err = []
def outReceived(self, data):
self.out.append(data)
def errReceived(self, data):
self.err.append(data)
def processExited(self, reason):
if isinstance(reason.value, twisted.internet.error.ProcessDone):
out = b''.join(self.out).decode('utf-8')
match = re.search('offset ([\d.-]+) sec', out)
if match is not None:
offset = float(match.group(1))
self.deferred.callback(offset)
else:
self.deferred.errback(error.Error('Could not parse offset: %s', out))
else:
err = b''.join(self.err)
self.deferred.errback(error.Error('{} failed with status {}: stderr={!r}'.format(self._cmd, reason.value.exitCode, err)))
class ConnectionTimerException(Exception):
pass
================================================
FILE: universe/rewarder/env_status.py
================================================
import logging
import threading
logger = logging.getLogger()
def parse_episode_id(episode_id):
if episode_id is None:
return -1
return int(episode_id)
def generate_episode_id(parsed):
if parsed == -1:
return None
return str(parsed)
def compare_ids(a, b):
if a == b:
return 0
elif a is None:
return -1
elif b is None:
return 1
elif parse_episode_id(a) < parse_episode_id(b):
return -1
else:
return 1
class EnvStatus(object):
def __init__(self, label=None, primary=True):
self.cv = threading.Condition()
self._env_id = None
self._env_state = None
self._episode_id = '0'
self._fps = None
self.label = label or 'EnvStatus'
self.primary = primary
def env_info(self):
with self.cv:
return {
'env_state': self._env_state,
'env_id': self._env_id,
'episode_id': self._episode_id,
'fps': self._fps,
}
def set_env_info(self, env_state=None, env_id=None, episode_id=None, bump_past=None, fps=None):
"""Atomically set the environment state tracking variables.
"""
with self.cv:
if env_id is None:
env_id = self._env_id
if env_state is None:
env_state = self._env_state
if fps is None:
fps = self._fps
self.cv.notifyAll()
old_episode_id = self._episode_id
if self.primary:
current_id = parse_episode_id(self._episode_id)
# Bump when changing from resetting -> running
if bump_past is not None:
bump_past_id = parse_episode_id(bump_past)
current_id = max(bump_past_id+1, current_id+1)
elif env_state == 'resetting':
current_id += 1
self._episode_id = generate_episode_id(current_id)
assert self._fps or fps
elif episode_id is False:
# keep the same episode_id: this is just us proactive
# setting the state to resetting after a done=True
pass
else:
assert episode_id is not None, "No episode_id provided. This likely indicates a misbehaving server, which did not send an episode_id"
self._episode_id = episode_id
self._fps = fps
logger.info('[%s] Changing env_state: %s (env_id=%s) -> %s (env_id=%s) (episode_id: %s->%s, fps=%s)', self.label, self._env_state, self._env_id, env_state, env_id, old_episode_id, self._episode_id, self._fps)
self._env_state = env_state
if env_id is not None:
self._env_id = env_id
return self.env_info()
@property
def episode_id(self):
with self.cv:
return self._episode_id
@property
def env_state(self):
with self.cv:
return self._env_state
@env_state.setter
def env_state(self, value):
# TODO: Validate env_state
self.set_env_info(value)
@property
def env_id(self):
with self.cv:
return self._env_id
@env_id.setter
def env_id(self, value):
self.set_env_info(None, env_id=value)
@property
def fps(self):
with self.cv:
return self._fps
def wait_for_env_state_change(self, start_state):
with self.cv:
while True:
if self._env_state != start_state:
return self.env_info()
self.cv.wait(timeout=10)
================================================
FILE: universe/rewarder/merge.py
================================================
from universe import error
import six
def merge_infos(info1, info2):
"""We often need to aggregate together multiple infos. Most keys can
just be clobbered by the new info, but e.g. any keys which contain
counts should be added. The merge schema is indicated by the key
namespace.
Namespaces:
- stats.timers: Timing
- stats.gauges: Gauge values
- stats.*: Counts of a quantity
"""
for key, value in six.iteritems(info2):
if key in info1 and key.startswith('stats'):
if key.startswith('stats.timers'):
# timer
info1[key] += value
elif key.startswith('stats.gauges'):
# gauge
info1[key] = value
else:
# counter
info1[key] += value
else:
info1[key] = value
def merge_reward_n(accum_reward_n, reward_n):
for i in range(len(reward_n)):
if reward_n[i] is not None:
# Add rewards
accum_reward_n[i] += reward_n[i]
def merge_done_n(accum_done_n, done_n):
for i in range(len(done_n)):
# Copy over done if the episode is indeed none
if done_n[i]:
accum_done_n[i] = done_n[i]
def _merge_observation(accum_observation, observation):
"""
Old visual observation is discarded, because it is outdated frame.
Text observations are merged, because they are messages sent from the rewarder.
"""
if observation is None:
# We're currently masking. So accum_observation probably
# belongs to the previous episode. We may lose a "text"
# observation from the previous episode, but that's ok.
return None
elif accum_observation is None:
# Nothing to merge together
return observation
accum_observation['vision'] = observation.get('vision')
accum_observation['text'] = accum_observation.get('text', []) + observation.get('text', [])
return accum_observation
def merge_observation_n(accum_observation_n, observation_n):
# Merge observations.
for i in range(len(accum_observation_n)):
accum_observation_n[i] = _merge_observation(accum_observation_n[i], observation_n[i])
def merge_n(
accum_observation_n, accum_reward_n, accum_done_n, accum_info,
observation_n, reward_n, done_n, info,
):
# Merge observation/reward/done
merge_observation_n(accum_observation_n, observation_n)
merge_reward_n(accum_reward_n, reward_n)
merge_done_n(accum_done_n, done_n)
# Merge together infos. We deep merge the 'n' key and do a
# simple merge on everything else.
accum_info_n = accum_info['n']
for accum_info_i, info_i in zip(accum_info_n, info['n']):
merge_infos(accum_info_i, info_i)
merge_infos(accum_info, info)
accum_info['n'] = accum_info_n
================================================
FILE: universe/rewarder/remote.py
================================================
# loaded inside of the environments
import logging
import os
from universe import pyprofile
import sys
is_py2 = sys.version[0] == '2'
if is_py2:
import Queue as queue
else:
import queue as queue
import threading
import time
import ujson
import collections
from autobahn.twisted import websocket
from universe.twisty import reactor
from universe import error, utils
logger = logging.getLogger(__name__)
class Exit(Exception):
pass
class RewarderProtocol(websocket.WebSocketServerProtocol):
connections = None
def onConnect(self, request):
if not os.path.exists('/usr/local/openai/privileged_state/password'):
raise error.Error('No such file: /usr/local/openai/privileged_state/password. (HINT: did the init script run /app/universe-envs/base/openai-setpassword?)')
with open('/usr/local/openai/privileged_state/password') as f:
password = f.read().strip()
self._message_id = 0
self._request = request
self._observer = request.headers.get('openai-observer') == 'true'
self.password = password
logger.info('Client connecting: peer=%s observer=%s', request.peer, self._observer)
def authenticate(self, request):
# Ugly, but it'll have to do for now.
authorization = request.headers.get('authorization')
if authorization is None:
logger.info('REJECT REASON: No authorization header supplied: %s', request.headers)
self.reject('No authorization header supplied. You must supply a basic authentication header.')
return
basic = utils.basic_auth_decode(authorization)
if basic is None:
logger.info('REJECT REASON: Invalid basic auth header: %s', request.headers)
self.reject('Could not parse authorization header. You must supply a basic authentication header.')
return
username, password = basic
if username != self.password:
logger.info('REJECT REASON: Invalid password: %r (%s expected; %s)', username, self.password, request.headers)
self.reject('Invalid password: {!r}. If you are using the allocator, you should see your password in the logs; if spinning up an environment by hand, it defaults to "openai". Connect as vnc://:?password=.'.format(username))
return
def onOpen(self):
logger.info('WebSocket connection established')
# Need to wait until onOpen to send messages
self.authenticate(self._request)
self.factory.agent_conn._register(self, observer=self._observer)
# Inform the agent about the current env status
env_info = self.factory.agent_conn.env_status.env_info()
# Immediately upon connection, let the agent know the current
# status.
self.factory.agent_conn.send_env_describe_from_env_info(
env_info,
)
def onMessage(self, payload, isBinary):
if not self.factory.agent_conn.check_message(self):
return
assert not isBinary, "Binary websocket not supported"
payload = ujson.loads(payload)
context = {
'start': time.time(),
'conn': self,
}
latency = context['start'] - payload['headers']['sent_at']
pyprofile.incr('rewarder_protocol.messages')
pyprofile.incr('rewarder_protocol.messages.{}'.format(payload['method']))
pyprofile.timing('rewarder_protocol.latency.rtt.skew_unadjusted', 2*latency)
if latency < 0:
pyprofile.incr('rewarder_protocol.latency.rtt.skew_unadjusted.negative')
if payload['method'] == 'v0.env.reset':
logger.info('Received reset message: %s', payload)
self.factory.agent_conn.control_buffer.recv_rpc(context, payload)
elif payload['method'] == 'v0.control.ping':
logger.debug('Received ping message: %s', payload)
parent_message_id = payload['headers']['message_id']
headers = {'parent_message_id': parent_message_id}
self.send_message('v0.reply.control.ping', {}, headers)
else:
logger.warn('Received unsupported message: %s', payload)
def onClose(self, wasClean, code, reason):
logger.info('WebSocket connection closed: %s', reason)
self.factory.agent_conn._unregister(self)
def send_message(self, method, body, headers):
id = self._message_id
self._message_id += 1
new_headers = {
'message_id': id,
'sent_at': time.time(),
}
if headers:
new_headers.update(headers)
payload = {
'method': method,
'body': body,
'headers': new_headers,
}
# This is a bit ugly, but decide how much we care
if (method != 'v0.reply.control.ping' and 'parent_message_id' in new_headers) or\
method == 'v0.connection.close':
logger.info('Sending rewarder message: %s', payload)
else:
logger.debug('Sending rewarder message: %s', payload)
self.sendMessage(ujson.dumps(payload).encode('utf-8'), False)
def reject(self, message):
self.send_message('v0.connection.close', {'message': message}, {})
self.sendClose(code=1000, reason=message)
self.transport.loseConnection()
class ControlBuffer(object):
def __init__(self, cv):
self.buf = queue.Queue()
self.cv = cv
def recv_rpc(self, context, payload):
"""Call from any thread"""
logger.debug("Adding RPC payload to ControlBuffer queue: %s", payload)
self.buf.put(('rpc', (context, payload)))
with self.cv:
self.cv.notifyAll()
def client_disconnect(self, conn, stats):
self.buf.put(('client_disconnect', (conn, stats)))
def get(self, *args, **kwargs):
"""Call from main thread."""
payload = self.buf.get(*args, **kwargs)
logger.debug("Removing RPC payload from ControlBuffer queue: %s", payload)
return payload
class AgentConn(object):
def __init__(self, env_status, cv, control_buffer, error_buffer, idle_timeout=None, exclusive=False):
self.error_buffer = error_buffer
self.env_status = env_status
self.control_buffer = control_buffer
self.cv = cv
self.conns = {}
self.exclusive = exclusive
self.idle_timeout = idle_timeout
self.last_disconnect_time = time.time()
self._idle_message_interval = 10 # for logging
def active_clients(self):
return [conn for conn, stats in self.conns.items() if stats['active']]
def listen(self, port=15900):
logger.info('Starting Rewarder on port=%s', port)
factory = websocket.WebSocketServerFactory()
factory.agent_conn = self
factory.protocol = RewarderProtocol
reactor.callFromThread(reactor.listenTCP, port, factory)
def check_message(self, conn):
with self.cv:
self.conns[conn]['messages'] += 1
if self.conns[conn]['active']:
return True
elif self.conns[conn]['observer']:
logger.info('CONNECTION STATUS: Marking connection as active: observer=%s peer=%s total_conns=%d', True, conn._request.peer, len(self.conns))
self.conns[conn]['active'] = True
return True
else:
# Note: if this exceptions, Autobahn will end up capturing
# the errors since its hooks are called via
# maybeDeferred. This won't always print the prettiest
# stack trace.
# This conn is neither active or an observer - before setting
# active, let's see if there are any existing active,
# non-observer conns.
active = len([o for o in self.conns.values() if not o['observer'] and o['active']])
if active > 0:
# Already full up, sorry!
logger.info('CONNECTION STATUS: Dropping new connection since already have %d non-observer conns (%d conns total)', active, len(self.conns))
# Sometimes connecting clients will time out before
# the connection is fully established, but the
# rewarder would still count the session as having
# come up. This, we try to wait until as late as
# possible to decide if a client is active.
conn.reject('The rewarder already has an active client. (HINT: if you obtained your environment through the allocator, make sure to run .configure(client_id=...) with a different client_id for each concurrent worker.)')
return False
else:
logger.info('CONNECTION STATUS: Marking connection as active: observer=%s peer=%s total_conns=%d', False, conn._request.peer, len(self.conns))
self.conns[conn]['active'] = True
return True
def _register(self, conn, observer=False):
with self.cv:
self.conns[conn] = {'messages': 0, 'observer': observer, 'active': False}
self.cv.notifyAll()
def _unregister(self, conn):
with self.cv:
try:
stats = self.conns.pop(conn)
except KeyError:
stats = None
else:
self.cv.notifyAll()
if stats is not None and stats['active']:
self.last_disconnect_time = time.time()
active = self.active_clients()
logger.info('[%s] Active client disconnected (sent %d messages). Still have %d active clients left', utils.thread_name(), stats['messages'], len(active))
else:
logger.info('[%s] Non-active client disconnected', utils.thread_name())
self.control_buffer.client_disconnect(conn, stats)
def _broadcast(self, method, body, headers=None, conn=None):
if conn:
conns = [conn]
else:
conns = self.conns
for conn in conns:
conn.send_message(method, body, headers)
def send_env_text(self, text, episode_id):
''' text channel to communicate with the agent '''
reactor.callFromThread(self._send_env_text, text, episode_id)
def _send_env_text(self, text, episode_id):
self._broadcast('v0.env.text', {
'text': text
}, {'episode_id': episode_id})
def send_env_observation(self, observation, episode_id):
reactor.callFromThread(self._send_env_observation, observation, episode_id)
def _send_env_observation(self, observation, episode_id, conn=None):
self._broadcast('v0.env.observation', {
'observation': observation,
}, {'episode_id': episode_id}, conn=conn)
def send_env_reward(self, reward, done, info, episode_id):
pyprofile.incr('agent_conn.reward', reward)
if done:
pyprofile.incr('agent_conn.done')
reactor.callFromThread(self._send_env_reward, reward, done, info, episode_id)
def _send_env_reward(self, reward, done, info, episode_id):
self._broadcast('v0.env.reward', {
'reward': reward,
'done': done,
'info': info,
}, {'episode_id': episode_id})
def send_env_describe_from_env_info(self, env_info):
assert env_info['fps'] is not None, "Missing fps: {}".format(env_info)
self.send_env_describe(env_info['env_id'], env_info['env_state'], episode_id=env_info['episode_id'], fps=env_info['fps'])
def send_env_describe(self, env_id, env_state, episode_id, fps, headers=None, parent_message_id=None, parent_context=None):
reactor.callFromThread(self._send_env_describe, env_id, env_state, episode_id, fps, headers, parent_message_id, parent_context)
def _send_env_describe(self, env_id, env_state, episode_id, fps, headers=None, parent_message_id=None, parent_context=None):
conn = None
if headers is None:
headers = {}
if parent_message_id is not None:
headers['parent_message_id'] = parent_message_id
headers['parent_runtime'] = time.time() - parent_context['start']
conn = parent_context['conn']
headers['episode_id'] = episode_id
assert fps is not None
# TODO: decide how to handle multiple concurrent envs
self._broadcast('v0.env.describe', {
'env_id': env_id,
'env_state': env_state,
'fps': fps,
}, headers, conn)
def send_reply_error(self, *args, **kwargs):
reactor.callFromThread(self._send_reply_error, *args, **kwargs)
def _send_reply_error(self, message, parent_message_id, parent_context):
headers = {}
headers['parent_message_id'] = parent_message_id
headers['parent_runtime'] = time.time() - parent_context['start']
conn = parent_context['conn']
# TODO: decide how to handle multiple concurrent envs
self._broadcast('v0.reply.error', {
'message': message,
}, headers, conn)
def send_reply_env_reset(self, *args, **kwargs):
reactor.callFromThread(self._send_reply_env_reset, *args, **kwargs)
def _send_reply_env_reset(self, parent_message_id, parent_context, episode_id):
headers = {}
headers['parent_message_id'] = parent_message_id
headers['parent_runtime'] = time.time() - parent_context['start']
headers['episode_id'] = episode_id
conn = parent_context['conn']
# TODO: decide how to handle multiple concurrent envs
self._broadcast('v0.reply.env.reset', {}, headers, conn)
def check_status(self):
with self.cv:
if self.idle_timeout is None:
return
active = self.active_clients()
if len(active) == 0:
now = time.time()
idle_duration = now - self.last_disconnect_time
if self.idle_timeout is not None:
utils.periodic_log(
self, 'idle_timeout',
'No active clients for %.2fs (total client: %d); will exit due to idle timeout after %.0fs',
idle_duration, len(self.conns), self.idle_timeout, frequency=self._idle_message_interval)
else:
utils.periodic_log(
self, 'idle_for',
'No active clients for %.2fs (total client: %d)',
idle_duration, len(self.conns), frequency=self._idle_message_interval)
if self.idle_timeout is not None and idle_duration > self.idle_timeout:
self.error_buffer.record(Exit('EXIT CAUSE: idle timeout exceeded after {:.2f} seconds'.format(idle_duration)), wrap=False)
class RewardLogger(object):
def __init__(self):
self.reset(log=False, episode_stats=True)
def reset(self, log=True, episode_stats=True):
if log:
self._log()
self.last_print = time.time()
# Could just maintain summary statistics, but we're not going
# to have more than fps rewards at once, so it's fine to just
# keep them all around.
self.reward = []
self.done = False
self.info = {}
self.count = 0
if episode_stats:
if log:
self._log_reset()
self.episode_reward = 0
self.episode_count = 0
self.episode_start = time.time()
def record(self, reward, done, info):
self.reward.append(reward)
self.done = self.done or done
self.info.update(info)
self.count += 1
self.episode_reward += reward
self.episode_count += 1
if time.time() - self.last_print > 1:
self._log()
self.reset(log=False, episode_stats=False)
def _log_reset(self):
logger.info('[%s] Ending previous episode: episode_reward=%s episode_count=%s episode_duration=%.2f', utils.thread_name(), self.episode_reward, self.episode_count, time.time() - self.episode_start)
def _log(self):
if 'rewarder.profile' in self.info:
self.info['rewarder.profile'] = '<{} bytes>'.format(len(str(self.info['rewarder.profile'])))
if len(self.reward) > 0:
min_reward = min(self.reward)
max_reward = max(self.reward)
else:
min_reward = '(empty)'
max_reward = '(empty)'
logger.info('[%s] Over past %.2fs, sent %s reward messages to agent: reward=%s reward_min=%s reward_max=%s done=%s info=%s',
utils.thread_name(), time.time() - self.last_print, self.count,
sum(self.reward), min_reward, max_reward,
self.done, self.info)
================================================
FILE: universe/rewarder/reward_buffer.py
================================================
import logging
import threading
import time
from universe import error
from universe.rewarder import env_status, merge
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
class RewardState(object):
def __init__(self, label, episode_id):
self.label = label
self.count = 0
self.reward = 0.
self.text = []
self.done = False
self.info = {}
self._observation = None
self._episode_id = episode_id
self._env_state = None
def push_time(self, remote_time, local_time):
# Sometimes helpful diagnostic info
self.info['reward_buffer.remote_time'] = remote_time
self.info['reward_buffer.local_time'] = local_time
def set_env_info(self, env_state):
self._env_state = env_state
def push_text(self, text):
self.text.append(text)
def push_done(self, done, info):
# Consider yourself done whenever a reward crosses episode
# boundaries.
self.done = self.done or done
if done:
info['reward_buffer.done_time'] = time.time()
self.push_info(info)
def push_info(self, info):
merge.merge_infos(self.info, info)
def push(self, reward, done, info):
# extra_logger.debug('[%s] RewardState: pushing reward %s to episode_id %s', self.label, reward, self._episode_id)
self.count += 1
self.reward += reward
# Consider yourself done whenever a reward crosses episode
# boundaries.
self.push_done(done, info)
def pop_text(self):
text = self.text
self.text = []
return text
def pop_info(self):
info = self.info
self.info = {}
info['env.text'] = self.pop_text()
if self._observation is not None:
# Only used for the debugging gym-core envs with
# rewarder_observation set.
info['rewarder.observation'] = (self._observation, self._episode_id)
info['env_status.episode_id'] = self._episode_id
info['env_status.env_state'] = self._env_state
return info
def pop(self):
info = self.pop_info()
count = self.count
reward = self.reward
done = self.done
self.count = 0
self.reward = 0.
self.done = False
info['stats.reward.count'] = count
extra_logger.debug('[%s] RewardState: popping reward %s from episode_id %s', self.label, reward, self._episode_id)
return reward, done, info
def set_observation(self, observation):
self._observation = observation
# Buffers up incoming rewards
class RewardBuffer(object):
def __init__(self, label):
self.cv = threading.Condition()
self.label = label
self._current_episode_id = None
self._reward_state = {}
self._masked = True
self._remote_env_state = None
self._remote_env_id = None
self._remote_episode_id = None
self._remote_fps = None
def reward_state(self, episode_id):
try:
return self._reward_state[episode_id]
except:
extra_logger.info('[%s] RewardBuffer: Creating new RewardState for episode_id=%s', self.label, episode_id)
reward_state = self._reward_state[episode_id] = RewardState(self.label, episode_id)
if self._current_episode_id is None and not self._masked:
extra_logger.info('[%s] RewardBuffer advancing: No active episode, so activating episode_id=%s', self.label, episode_id)
self._current_episode_id = episode_id
self._drop_below(episode_id)
if episode_id is not None and not self._masked:
# If we're masked we'll be dropping everything below the reset ID anyway
valid = self._valid_ids()
for id in valid:
if id == episode_id:
continue
state = self._reward_state[id]
if state.done:
continue
extra_logger.info('[%s] RewardBuffer received message for episode_id=%s but no done=True message for %s. Artificially marking %s as done=True.', self.label, episode_id, id, id)
state.push_done(True, {'env_status.artificial.done': True})
return reward_state
def set_env_info(self, env_state, env_id, episode_id, fps):
with self.cv:
if self._remote_env_state is not None:
extra_logger.info('[%s] RewardBuffer changing env_state: %s (env_id=%s) -> %s (env_id=%s) (episode_id: %s->%s, fps=%s, masked=%s, current_episode_id=%s)', self.label, self._remote_env_state, self._remote_env_id, env_state, env_id, self._remote_episode_id, episode_id, fps, self._masked, self._current_episode_id)
else:
extra_logger.info('[%s] RewardBuffer: Initial env_state: %s (env_id=%s) (episode_id: %s, fps=%s, masked=%s, current_episode_id=%s)', self.label, env_state, env_id, episode_id, fps, self._masked, self._current_episode_id)
self._remote_env_state = env_state
self._remote_env_id = env_id
self._remote_episode_id = episode_id
self._remote_fps = fps
self.reward_state(episode_id).set_env_info(env_state)
def set_observation(self, episode_id, observation):
with self.cv:
self.reward_state(episode_id).set_observation(observation)
def push_time(self, episode_id, remote_time, local_time):
with self.cv:
self.reward_state(episode_id).push_time(remote_time, local_time)
def push_text(self, episode_id, text):
with self.cv:
self.reward_state(episode_id).push_text(text)
self.cv.notifyAll()
def push_info(self, episode_id, info):
# Just send some info
with self.cv:
self.reward_state(episode_id).push_info(info)
def push(self, episode_id, reward, done, info):
with self.cv:
self.reward_state(episode_id).push(reward, done, info)
self.cv.notifyAll()
def pop(self, peek=False):
with self.cv:
self.cv.notifyAll()
if peek:
# This happens when a higher layer wants to poll for
# new observations being ready, but doesn't want to
# pop any rewards.
max_id = self._max_id()
reward_state = self.reward_state(self._current_episode_id)
peek_state = self.reward_state(max_id)
peek_id = peek_state._episode_id
peek_state = peek_state._env_state
if self._masked:
assert reward_state._episode_id is None
assert reward_state._env_state is None
peek_id = None
peek_state = None
return 0, False, {
'peek': True,
'env_status.episode_id': reward_state._episode_id,
'env_status.env_state': reward_state._env_state,
'env_status.peek.episode_id': peek_id,
'env_status.peek.env_state': peek_state,
}
reward, done, info = self.reward_state(self._current_episode_id).pop()
if done:
# We return the *observation* from the new,
# reward/done from the old, and a merged info with
# keys from the new taking precedence.
self._advance()
new_state = self.reward_state(self._current_episode_id)
try:
info['env_status.complete.episode_id'] = info['env_status.episode_id']
except KeyError:
pass
try:
info['env_status.complete.env_state'] = info['env_status.env_state']
except KeyError:
pass
info['env_status.episode_id'] = new_state._episode_id
info['env_status.env_state'] = new_state._env_state
new_text = self.reward_state(self._current_episode_id).pop_text()
if len(info['env.text']) > 0:
extra_logger.info('[%s] RewardBuffer dropping env.text for completed episode %s: %s', self.label, info['env_status.episode_id'], info['env.text'])
info['env.text'] = new_text
return reward, done, info
def mask(self):
with self.cv:
extra_logger.info('[%s] RewardBuffer advancing: masking until reset completes; setting current_episode_id=None', self.label)
self._masked = True
self._current_episode_id = None
def reset(self, episode_id):
with self.cv:
extra_logger.info('[%s] RewardBuffer advancing: unmasking after explicit reset: episode_id=%s', self.label, episode_id)
self._masked = False
self._drop_below(episode_id, quiet=True)
self._current_episode_id = episode_id
self.push_info(episode_id, {'env_status.reset.episode_id': episode_id})
def _max_id(self):
valid_ids = self._valid_ids()
if len(valid_ids) > 0:
parsed = max(env_status.parse_episode_id(k) for k in valid_ids)
return env_status.generate_episode_id(parsed)
else:
return None
def _valid_ids(self):
return [r for r in self._reward_state.keys() if r is not None]
def _advance(self):
completed_episode_id = self._current_episode_id
del self._reward_state[completed_episode_id]
if None in self._reward_state:
extra_logger.warn('[%s] WARNING: RewardBuffer: while advancing from %s, None was in reward state: %s', self.label, completed_episode_id, self._reward_state)
max_id = self._max_id()
if max_id is not None:
self._current_episode_id = max_id
if env_status.compare_ids(completed_episode_id, self._current_episode_id) >= 0:
extra_logger.info("[%s] RewardBuffer advancing: setting episode_id=None until new data received. Rare condition reached where message for old environment received after new one: completed_episode_id=%r self._current_episode_id=%r (%r). This is ok, but something we may want to fix in the future", self.label, completed_episode_id, self._current_episode_id, self._reward_state)
self._current_episode_id = None
else:
extra_logger.info('[%s] RewardBuffer advancing: has data for next episode: %s->%s', self.label, completed_episode_id, self._current_episode_id)
self._drop_below(self._current_episode_id)
else:
extra_logger.info('[%s] RewardBuffer advancing: setting episode_id=None until new data received (was episode_id=%s)', self.label, completed_episode_id)
self._current_episode_id = None
def _drop_below(self, episode_id, quiet=False):
dropped = set()
for stored_id in self._reward_state:
if env_status.compare_ids(stored_id, episode_id) < 0:
dropped.add(stored_id)
if len(dropped) > 0:
if quiet:
log = extra_logger.debug
else:
log = extra_logger.info
log('[%s] RewardBuffer: dropping stale episode data: dropped=%s episode_id=%s', self.label, dropped, episode_id)
for stored_id in dropped:
del self._reward_state[stored_id]
def wait_for_step(self, error_buffer=None, timeout=None):
# TODO: this might be cleaner using channels
with self.cv:
start = time.time()
while True:
if self.count != 0:
return
elif timeout is not None and time.time() - start > timeout:
raise error.Error('No rewards received in {}s'.format(timeout))
if error_buffer:
error_buffer.check()
self.cv.wait(timeout=0.5)
================================================
FILE: universe/rewarder/reward_proxy_server.py
================================================
import json
import logging
import os
import time
from autobahn.twisted import websocket
from universe.twisty import reactor
from twisted.internet import endpoints
logger = logging.getLogger(__name__)
class RewardServerClient(websocket.WebSocketClientProtocol, object):
def __init__(self):
super(RewardServerClient, self).__init__()
self.id = -1
self.proxy_server = None
self._connected = False
def onConnect(self, request):
self.id = self.factory.proxy_server.id
logger.info('[RewardProxyClient] [%d] Connected to rewarder', self.id)
self.proxy_server = self.factory.proxy_server
self._connected = True
buffered = self.proxy_server.pop_buffer()
logger.info('[RewardProxyClient] [%d] Flushing %d buffered messages', self.id, len(buffered))
for msg in buffered:
self.sendMessage(msg)
def onOpen(self):
logger.info('[RewardProxyClient] [%d] Rewarder websocket connection established', self.id)
def onMessage(self, msg, isBinary):
logger.debug('[RewardProxyClient] [%d] Received message from server: %s', self.id, msg)
self.proxy_server.sendMessage(msg)
# Record the message
self.proxy_server.record_message(msg.decode('utf-8'), from_rewarder=True)
# # Process the message for recording
# method, headers, body = unpack_message(msg)
#
# if method == "env.reward":
# # {"body":{"info":{"episode":0},"reward":0.0,"done":false},
# # "headers":{"sent_at":1473126129.231828928,"message_id":207},
# # "method":"env.reward"}
def onClose(self, wasClean, code, reason):
logger.info('[RewardProxyClient] [%d] Rewarder websocket connection closed: %s', self.id, reason)
def close(self):
logger.info('[RewardProxyClient] [%d] Closing connection', self.id)
self.transport.loseConnection()
class RewardProxyServer(websocket.WebSocketServerProtocol, object):
_next_id = 0
_n_open_files = 0
@classmethod
def next_id(cls):
id = cls._next_id
cls._next_id += 1
return id
def __init__(self):
super(RewardProxyServer, self).__init__()
self.id = self.next_id()
self.client = None
self.file = None # We do not open open the file until we have established an end-to-end connection
self.buffered = []
self._closed = False
def pop_buffer(self):
"""Called by the client once it's ready to start sending messages.
"""
buffered = self.buffered
self.buffered = []
return buffered
def begin_recording(self):
"""
Open the file and write the metadata header to describe this recording. Called after we establish an end-to-end connection
This uses Version 1 of our protocol
Version 0 can be seen here: https://github.com/openai/universe/blob/f85a7779c3847fa86ec7bb513a1da0d3158dda78/bin/recording_agent.py
"""
logger.info("[RewardProxyServer] [%d] Starting recording", self.id)
if self._closed:
logger.error(
"[RewardProxyServer] [%d] Attempted to start writing although client connection is already closed. Aborting", self.id)
self.close()
return
if self._n_open_files != 0:
logger.error("[RewardProxyServer] [%d] WARNING: n open rewards files = %s. This is unexpected. Dropping connection.", self.id, self._n_open_files)
self.close()
return
logfile_path = os.path.join(self.factory.logfile_dir, 'rewards.demo')
logger.info('Recording to {}'.format(logfile_path))
self.file = open(logfile_path, 'w')
self._n_open_files += 1
logger.info("[RewardProxyServer] [%d] n open rewards files incremented: %s", self.id, self._n_open_files)
self.file.write(json.dumps({
'version': 1,
'_debug_version': '0.0.1', # Give this an internal version for debugging corrupt reward.demo files # TODO, pull this from setup.py or the host docker image
}))
self.file.write('\n')
self.file.flush()
logger.info("[RewardProxyServer] [%d] Wrote version number", self.id)
def onConnect(self, request):
logger.info('[RewardProxyServer] [%d] Client connecting: %s', self.id, request.peer)
self._request = request
def onOpen(self):
logger.info("[RewardProxyServer] [%d] Websocket connection established", self.id)
self.connect_upstream()
def connect_upstream(self, tries=1, max_attempts=7):
if self._closed:
logger.info("[RewardProxyServer] [%d] Attempted to connect upstream although client connection is already closed. Aborting",
self.id)
return
remote = getattr(self.factory, 'rewarder_address', 'localhost:15900')
endpoint = endpoints.clientFromString(reactor, 'tcp:' + remote)
client_factory = websocket.WebSocketClientFactory('ws://' + remote)
headers = {'authorization': self._request.headers['authorization']}
if self._request.headers.get('openai-observer'):
headers['openai-observer'] = self._request.headers.get('openai-observer')
client_factory.headers = headers
client_factory.protocol = RewardServerClient
client_factory.proxy_server = self
client_factory.endpoint = endpoint
logger.info("[RewardProxyServer] [%d] Connecting to upstream %s (try %d/%d)", self.id, remote, tries, max_attempts)
def _connect_callback(client):
logger.info('[RewardProxyServer] [%d] Upstream connection %s established', self.id, remote)
self.client = client
if self.factory.logfile_dir:
self.begin_recording()
def _connect_errback(reason):
if tries < max_attempts:
# Somewhat arbitrary exponential backoff: should be
# pretty rare, and indicate that we're just starting
# up.
delay = 1.5 ** tries
logger.info('[RewardProxyServer] [%d] Connection to %s failed: %s. Try %d/%d; going to retry in %fs', self.id, remote, reason, tries, max_attempts, delay)
reactor.callLater(
delay, self.connect_upstream,
tries=tries+1, max_attempts=max_attempts)
else:
logger.error('[RewardProxyServer] [%d] Connection to %s failed: %s. Completed %d/%d atttempts; disconnecting.', self.id, remote, reason, tries, max_attempts)
self.transport.loseConnection()
endpoint.connect(client_factory).addCallbacks(_connect_callback, _connect_errback)
def close(self):
logger.info('[RewardProxyServer] [%d] Closing...', self.id)
self.transport.loseConnection()
def onClose(self, wasClean, code, reason):
logger.info('[RewardProxyServer] [%d] Client connection closed: %s', self.id, reason)
if self.client:
self.client.close()
if self.file:
self.file.close()
self._closed = True
def onMessage(self, msg, binary):
logger.debug('[RewardProxyServer] [%d] Received message from client: %s', self.id, msg)
# Pass the message on to the client
if self.client and self.client._connected:
self.client.sendMessage(msg)
else:
self.buffered.append(msg)
self.record_message(msg.decode('utf-8'), from_rewarder=False)
def record_message(self, msg, from_rewarder):
"""Record a message to our rewards.demo file if it is has been opened"""
if self.file:
# Include an authoritative timestamp (because the `sent_at` from the server is likely to be different
timestamped_message = {
'timestamp': time.time(),
'message': json.loads(msg),
'from_rewarder': from_rewarder,
}
self.file.write(json.dumps(timestamped_message))
self.file.write('\n')
self.file.flush()
================================================
FILE: universe/rewarder/rewarder_client.py
================================================
import logging
from universe import pyprofile
import time
import ujson
from autobahn.twisted import websocket
from twisted.internet import defer
from universe import error
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
class RemoteError(error.Error):
pass
class RewarderClient(websocket.WebSocketClientProtocol):
def __init__(self):
super(RewarderClient, self).__init__()
self._closed = False
self._close_message = None
self._connected = False
self._requests = {}
self._reset = None
self._initial_reset = False
self._connection_result = defer.Deferred()
def send_reset(self, env_id, seed, fps, episode_id):
self._initial_reset = True
self._reset = {
'env_id': env_id,
'fps': fps,
'episode_id': episode_id,
}
return self.send('v0.env.reset', {
'seed': seed,
'env_id': env_id,
'fps': fps,
}, {'episode_id': episode_id}, expect_reply=True)
def _finish_reset(self, episode_id):
extra_logger.info('[%s] Running finish_reset: %s', self.factory.label, episode_id)
self.reward_buffer.reset(episode_id)
def onConnect(self, request):
self._message_id = 0
self._requests = {}
self.reward_buffer = self.factory.reward_buffer
assert not self._connection_result.called
self._connection_result.callback(self)
self._connected = True
def waitForWebsocketConnection(self):
return self._connection_result
def send(self, method, body, headers=None, expect_reply=False):
if headers is None:
headers = {}
if self._closed:
error_message = "Can't send message to closed connection"
if self._close_message:
error_message += ": {}".format(self._close_message)
e = error.Error(error_message)
if expect_reply:
return defer.fail(e)
else:
raise e
id = self._message_id
self._message_id += 1
new_headers = {
'message_id': id,
'sent_at': time.time(),
}
new_headers.update(headers)
payload = {
'method': method,
'body': body,
'headers': new_headers,
}
extra_logger.info('[%s] Sending message to rewarder: %s', self.factory.label, payload)
self.sendMessage(ujson.dumps(payload).encode('utf-8'), False)
if expect_reply:
d = defer.Deferred()
self._requests[id] = (payload, d)
return d
else:
return None
def _manual_recv(self, method, body, headers={}):
"""Used in the tests"""
headers.setdefault('sent_at', time.time())
return self.recv(self._make_context(), {'method': method, 'body': body, 'headers': headers})
def recv(self, context, response):
method = response['method']
body = response['body']
headers = response['headers']
remote_time = headers['sent_at']
local_time = context['start']
episode_id = headers.get('episode_id')
if episode_id is not None:
self.reward_buffer.push_time(episode_id, remote_time, local_time)
# Gets called by RewarderClient
if method == 'v0.env.reward':
episode_id = headers['episode_id']
reward = body['reward']
done = body['done']
info = body['info']
extra_logger.debug('[%s] Received %s: reward=%s done=%s info=%s episode_id=%s', self.factory.label, method, reward, done, info, episode_id)
pyprofile.incr('rewarder_client.reward', reward)
if done:
pyprofile.incr('rewarder_client.done')
self.reward_buffer.push(episode_id, reward, done, info)
elif method == 'v0.env.text':
episode_id = headers['episode_id']
text = body['text']
extra_logger.debug('[%s] Received %s: text=%s episode_id=%s', self.factory.label, method, text, episode_id)
self.reward_buffer.push_text(episode_id, text)
elif method == 'v0.env.observation':
episode_id = headers['episode_id']
jsonable = body['observation']
extra_logger.debug('[%s] Received %s: observation=%s episode_id=%s', self.factory.label, method, jsonable, episode_id)
self.reward_buffer.set_observation(episode_id=episode_id, observation=jsonable)
elif method == 'v0.env.describe':
episode_id = headers['episode_id']
env_id = body['env_id']
env_state = body['env_state']
fps = body['fps']
extra_logger.info('[%s] Received %s: env_id=%s env_state=%s episode_id=%s',
self.factory.label, method, env_id, env_state, episode_id)
self.reward_buffer.set_env_info(env_state, env_id=env_id, episode_id=episode_id, fps=fps)
elif method == 'v0.reply.env.reset':
episode_id = headers['episode_id']
self._finish_reset(episode_id)
elif method in ['v0.reply.error', 'v0.reply.control.ping']:
assert headers.get('parent_message_id') is not None
elif method == 'v0.connection.close':
assert headers.get('parent_message_id') is None
logger.debug('Server hanging up: %s', body['message'])
self._close_message = body['message']
e = error.Error(body['message'])
self.factory.record_error(e)
else:
logger.error('Unrecognized websocket method: method=%s body=%s headers=%s (consider adding to rewarder_state.py)', method, body, headers)
return
parent_id = headers.get('parent_message_id')
if parent_id is not None:
try:
spec = self._requests.pop(parent_id)
except KeyError:
logger.error('[%s] Received extra reply to %d; ignoring: method=%s body=%s headers=%s ', self.factory.label, parent_id, method, body, headers)
else:
request, d = spec
if method != 'v0.reply.error':
d.callback((context, request, response))
else:
e = RemoteError('[{}] Remote error: {}'.format(self.factory.label, body['message']))
d.errback(e)
def _make_context(self):
return {'start': time.time()}
def onMessage(self, payload, isBinary):
extra_logger.debug('[%s] Received payload: %s', self.factory.label, payload)
assert not isBinary
payload = ujson.loads(payload)
context = self._make_context()
latency = context['start'] - payload['headers']['sent_at']
pyprofile.incr('rewarder_protocol.messages')
pyprofile.incr('rewarder_protocol.messages.{}'.format(payload['method']))
# Double latency to model RTT
pyprofile.timing('rewarder_protocol.latency.rtt.skew_unadjusted', 2*latency)
if latency < 0:
pyprofile.incr('rewarder_protocol.latency.rtt.skew_unadjusted.negative')
self.recv(context, payload)
def onClose(self, wasClean, code, reason):
if self._close_message:
return
if not self._connected:
assert not self._connection_result.called
self._connection_result.errback(error.ConnectionError(reason))
return
if not self._closed:
error_message = 'Lost connection: {} (clean={} code={})'.format(reason, wasClean, code)
reason = error.Error(error_message)
# TODO: it's not an error if we requested it
self.factory.record_error(reason)
else:
reason = error.Error("We closed the connection: {}".format(reason))
for request, d in self._requests.values():
d.errback(reason)
def close(self, code=1000, reason=None):
self._closed = True
extra_logger.info('[%s] Client closing websocket connection because of call to close(code=%s, reason=%s)', self.factory.label, code, reason)
self.sendClose(code, reason)
self.transport.loseConnection()
================================================
FILE: universe/rewarder/rewarder_session.py
================================================
from autobahn.twisted import websocket
import logging
import numpy as np
import threading
import time
from twisted.python import failure
from twisted.internet import defer, endpoints
import twisted.internet.error
from universe import utils
from universe.twisty import reactor
from universe.rewarder import connection_timer, env_status, reward_buffer, rewarder_client
from universe.utils import display
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
def _ping(client):
return client.send('v0.control.ping', {}, expect_reply=True)
class RewarderSession(object):
def __init__(self):
self.lock = threading.RLock()
self.i = 0
# Mutated by main thread exclusively
self.names_by_id = {}
self.reward_buffers = {}
self.env_statuses = {}
self.errors = {}
self.networks = {}
self.clients = {}
def close(self, name=None, reason=u'closed by RewarderSession.close'):
if name is None:
names = list(self.names_by_id.values())
else:
logger.info('[%s] Closing rewarder connection', name)
names = [name]
self.ids_by_name = {name: id for id, name in self.names_by_id.items()}
for name in names:
with self.lock:
id = self.ids_by_name.pop(name, None)
if id is None:
# already closed
continue
del self.names_by_id[id]
del self.reward_buffers[id]
del self.env_statuses[id]
self.errors.pop(id, None)
network = self.networks.pop(id)
network.close()
client = self.clients.pop(id, None)
if client is not None:
reactor.callFromThread(client.close, reason=reason)
def connect(self, name, address, label, password, env_id=None, seed=None, fps=60,
start_timeout=None, observer=False, skip_network_calibration=False):
if name in self.reward_buffers:
self.close(name, reason='closing previous connection to reconnect with the same name')
network = Network()
self.names_by_id[self.i] = name
self.reward_buffers[self.i] = reward_buffer.RewardBuffer(label)
self.env_statuses[self.i] = env_status.EnvStatus(label=label, primary=False)
self.networks[self.i] = network
reactor.callFromThread(self._connect,
name=name,
address=address,
env_id=env_id,
seed=seed,
fps=fps,
i=self.i,
network=network,
env_status=self.env_statuses[self.i],
reward_buffer=self.reward_buffers[self.i],
label=label,
start_timeout=start_timeout,
password=password,
observer=observer,
skip_network_calibration=skip_network_calibration,
)
self.i += 1
return network
def _already_closed(self, i):
# Lock must be held
return i not in self.names_by_id
# Call only from Twisted thread
# TODO: probably time to convert to kwargs
@defer.inlineCallbacks
def _connect(self, name, address, env_id, seed, fps, i, network, env_status, reward_buffer,
label, password, start_timeout,
observer, skip_network_calibration,
attempt=0, elapsed_sleep_time=0,
):
endpoint = endpoints.clientFromString(reactor, 'tcp:'+address)
factory = websocket.WebSocketClientFactory('ws://'+address)
factory.protocol = rewarder_client.RewarderClient
assert password, "Missing password: {} for rewarder session".format(password)
factory.headers = {'authorization': utils.basic_auth_encode(password), 'openai-observer': 'true' if observer else 'false'}
factory.i = i
# Various important objects
factory.endpoint = endpoint
factory.env_status = env_status
factory.reward_buffer = reward_buffer
# Helpful strings
factory.label = label
factory.address = address
# Arguments to always send to the remote reset call
factory.arg_env_id = env_id
factory.arg_fps = fps
def record_error(e):
if isinstance(e, failure.Failure):
e = e.value
# logger.error('[%s] Recording rewarder error: %s', factory.label, e)
with self.lock:
# drop error on the floor if we're already closed
if self._already_closed(factory.i):
extra_logger.info('[%s] Ignoring error for already closed connection: %s', label, e)
elif factory.i not in self.clients:
extra_logger.info('[%s] Received error for connection which has not been fully initialized: %s', label, e)
# We could handle this better, but right now we
# just mark this as a fatal error for the
# backend. Often it actually is.
self.errors[factory.i] = e
else:
extra_logger.info('[%s] Recording fatal error for connection: %s', label, e)
self.errors[factory.i] = e
def retriable_error(e, error_message):
if isinstance(e, failure.Failure):
e = e.value
if self._already_closed(factory.i):
logger.error('[%s] Got error, but giving up on reconnecting, since %d already disconnected', factory.label, factory.i)
return
# Also need to handle DNS errors, so let's just handle everything for now.
#
# reason.trap(twisted.internet.error.ConnectError, error.ConnectionError)
if elapsed_sleep_time < start_timeout:
sleep = min((2 * attempt+1), 10)
logger.error('[%s] Waiting on rewarder: %s. Retry in %ds (slept %ds/%ds): %s', factory.label, error_message, sleep, elapsed_sleep_time, start_timeout, e)
reactor.callLater(
sleep, self._connect, name=name, address=address,
env_id=env_id, seed=seed, fps=fps, i=i, network=network,
env_status=env_status, reward_buffer=reward_buffer, label=label,
attempt=attempt+1, elapsed_sleep_time=elapsed_sleep_time+sleep,
start_timeout=start_timeout, password=password,
observer=observer, skip_network_calibration=skip_network_calibration,
)
else:
logger.error('[%s] %s. Retries exceeded (slept %ds/%ds): %s', factory.label, error_message, elapsed_sleep_time, start_timeout, e)
record_error(e)
factory.record_error = record_error
try:
retry_msg = 'establish rewarder TCP connection'
client = yield endpoint.connect(factory)
extra_logger.info('[%s] Rewarder TCP connection established', factory.label)
retry_msg = 'complete WebSocket handshake'
yield client.waitForWebsocketConnection()
extra_logger.info('[%s] Websocket client successfully connected', factory.label)
if not skip_network_calibration:
retry_msg = 'run network calibration'
yield network.calibrate(client)
extra_logger.info('[%s] Network calibration complete', factory.label)
retry_msg = ''
if factory.arg_env_id is not None:
# We aren't picky about episode ID: we may have
# already receieved an env.describe message
# telling us about a resetting environment, which
# we don't need to bump post.
#
# tl;dr hardcoding 0.0 here avoids a double reset.
reply = yield self._send_env_reset(client, seed=seed, episode_id='0')
else:
# No env_id requested, so we just proceed without a reset
reply = None
# We're connected and have measured the
# network. Mark everything as ready to go.
with self.lock:
if factory.i not in self.names_by_id:
# ID has been popped!
logger.info('[%s] Rewarder %d started, but has already been closed', factory.label, factory.i)
client.close(reason='RewarderSession: double-closing, client was closed while RewarderSession was starting')
elif reply is None:
logger.info('[%s] Attached to running environment without reset', factory.label)
else:
context, req, rep = reply
logger.info('[%s] Initial reset complete: episode_id=%s', factory.label, rep['headers']['episode_id'])
self.clients[factory.i] = client
except Exception as e:
if retry_msg:
retriable_error(e, 'failed to ' + retry_msg)
else:
record_error(e)
def pop_errors(self):
errors = {}
with self.lock:
if self.errors:
for i, error in self.errors.items():
name = self.names_by_id[i]
errors[name] = error
self.errors.clear()
return errors
def reset(self, seed=None, env_id=None):
with self.lock:
for i, reward_buffer in self.reward_buffers.items():
reward_buffer.mask()
reactor.callFromThread(self._reset, seed=seed, env_id=env_id)
def _reset(self, seed=None, env_id=None):
with self.lock:
for client in self.clients.values():
d = self._send_env_reset(client, seed=seed, env_id=env_id)
# Total hack to capture the variable in the closure
def callbacks(client):
def success(reply): pass
def fail(reason): client.factory.record_error(reason)
return success, fail
success, fail = callbacks(client)
d.addCallback(success)
d.addErrback(fail)
def _send_env_reset(self, client, seed=None, episode_id=None, env_id=None):
if episode_id is None:
episode_id = client.factory.env_status.episode_id
logger.info('[%s] Sending reset for env_id=%s fps=%s episode_id=%s', client.factory.label, client.factory.arg_env_id, client.factory.arg_fps, episode_id)
return client.send_reset(
env_id=client.factory.arg_env_id if env_id is None else env_id,
seed=seed,
fps=client.factory.arg_fps,
episode_id=episode_id)
def pop(self, warn=True, peek_d=None):
reward_d = {}
done_d = {}
info_d = {}
err_d = self.pop_errors()
for i, reward_buffer in self.reward_buffers.items():
name = self.names_by_id[i]
reward, done, info = reward_buffer.pop(peek_d.get(name))
reward_d[name] = reward
done_d[name] = done
info_d[name] = info
# TODO: use FPS here rather than 60
if warn and any(info.get('stats.reward.count', 0) > 60 for info in info_d.values()):
logger.warn('WARNING: returning more than 60 aggregated rewards: %s. Either your agent is not keeping up with the framerate, or you should have called ".reset()" to clear pending rewards and reset the environments to a known state.',
{name: '{} (episode_id={})'.format(info['stats.reward.count'], info.get('env_status.episode_id')) for name, info in info_d.items()})
return reward_d, done_d, info_d, err_d
def wait(self, timeout=None):
deadline = time.time() + timeout
for client in self.clients:
if timeout is not None:
remaining_timeout = deadline - time.time()
else:
remaining_timeout = None
client.reward_buffer.wait_for_step(timeout=remaining_timeout)
# Hack to test actions over websockets
# TODO: Carve websockets out of rewarder pkg (into vnc_env? - and move this there)
def send_action(self, action_n, env_id):
reactor.callFromThread(self._send_action, env_id, action_n)
return self.pop_errors()
def _send_action(self, env_id, action_n):
with self.lock:
for n, client in zip(action_n, self.clients.values()):
self._send_env_action(client, env_id, action_n[n])
def _send_env_action(self, client, env_id, action_n):
if len(action_n) == 0:
# Hack to skip empty actions. TODO: Find source (throttle?) and fix
return
message = {
'env_id': env_id,
'action': action_n,
}
client.send('v0.agent.action', message, expect_reply=False)
def rewards_count(self):
# TODO: any reason to lock these?
return [client.reward_buffer.count for client in self.clients]
def pop_observation(self):
return [client.reward_buffer.pop_observation() for client in self.clients]
# def _connection_time(self):
# deferreds = []
# for client in self.clients:
# endpoint = client.factory.endpoint
# d = connection_timer.start(endpoint)
# deferreds.append(d)
# d = defer.DeferredList(deferreds, fireOnOneErrback=True, consumeErrors=True)
# return d
# Run this in Twisty therad
class Network(object):
def __init__(self):
self.connection_samples = 10
self.application_ping_samples = 10
self.connection_time_m = None
self.lock = threading.Lock()
self.recalibrate = None
self.client = None
self._ntpdate_reversed_clock_skew = None
self._reversed_clock_skew = None
def active(self):
with self.lock:
return self._reversed_clock_skew is not None
# Used by external consumers
def reversed_clock_skew(self):
with self.lock:
if self._ntpdate_clock_skew is not None:
return self._ntpdate_reversed_clock_skew
else:
return self._reversed_clock_skew
def _report(self):
connection_time = display.display_timestamps(self.connection_time_m)
if self._ntpdate_clock_skew is not None:
ntpdate_clock_skew = display.display_timestamp(self._ntpdate_clock_skew[0])
else:
ntpdate_clock_skew = None
clock_skew = display.display_timestamps_pair(self.clock_skew_m)
application_rtt = display.display_timestamps(self.application_rtt_m)
request_overhead = display.display_timestamps(self.request_overhead_m)
response_overhead = display.display_timestamps(self.response_overhead_m)
extra_logger.info('[%s] Network calibration: ntpdate_clock_skew=%s clock_skew=%s connection_time=%s application_rtt=%s request_overhead=%s response_overhead=%s',
self.client.factory.label, ntpdate_clock_skew, clock_skew, connection_time, application_rtt,
request_overhead, response_overhead)
def _start(self):
def calibrate():
d = defer.Deferred()
def fail(reason):
logger.error('[%s] Could not recalibrate network: %s', self.client.factory.label, reason)
d.addErrback(fail)
self._start_measure_connection_time(d)
self._start()
self.recalibrate = reactor.callLater(5 * 60, calibrate)
def close(self):
if self.recalibrate:
try:
self.recalibrate.cancel()
except twisted.internet.error.AlreadyCalled:
pass
# Called externally
def calibrate(self, client):
d = defer.Deferred()
def success(res):
# If we succeed, kick off the periodic 5 minute
# recalibrations.
self._start()
return res
d.addCallback(success)
self.client = client
# Kinda a hack. Idea is to try using the ntpdate -q offset if
# we can.
skew = self._start_measure_clock_skew()
def succeed(offset):
with self.lock:
self._ntpdate_clock_skew = np.array([offset, offset])
self._ntpdate_reversed_clock_skew = np.array([-offset, -offset])
self._start_measure_connection_time(d)
skew.addCallback(succeed)
def fail(reason):
with self.lock:
self._ntpdate_clock_skew = None
self._ntpdate_reversed_clock_skew = None
extra_logger.info('[%s] Could not determine clock skew with ntpdate; falling back to application-level ping: %s', self.client.factory.label, reason.value)
self._start_measure_connection_time(d)
skew.addErrback(fail)
return d
def _start_measure_connection_time(self, d):
connection_time_m = np.zeros(self.connection_samples)
self._measure_connection_time(d, connection_time_m, 0)
def _measure_connection_time(self, d, connection_time_m, i):
extra_logger.debug('[%s] Measuring connection time (%d/%d)', self.client.factory.label, i+1, len(connection_time_m))
endpoint = self.client.factory.endpoint
timer = connection_timer.start(endpoint)
def success(delta):
connection_time_m[i] = delta
if i+1 < len(connection_time_m):
self._measure_connection_time(d, connection_time_m, i+1)
else:
self.connection_time_m = connection_time_m
self._start_measure_application_ping(d)
def fail(reason):
d.errback(reason)
timer.addCallback(success)
timer.addErrback(fail)
def _start_measure_application_ping(self, d=None):
clock_skew_m = np.zeros((self.application_ping_samples, 2))
request_overhead_m = np.zeros((self.application_ping_samples))
response_overhead_m = np.zeros((self.application_ping_samples))
application_rtt_m = np.zeros((self.application_ping_samples))
self._measure_application_ping(d, clock_skew_m, request_overhead_m, response_overhead_m, application_rtt_m, 0)
def _measure_application_ping(self, d, clock_skew_m, request_overhead_m, response_overhead_m, application_rtt_m, i):
extra_logger.debug('[%s] Issuing an application-level ping (%d/%d)', self.client.factory.label, i+1, len(clock_skew_m))
start = time.time()
ping = _ping(self.client)
def success(res):
context, request, response = res
end = time.time()
request_sent_at = request['headers']['sent_at'] # local
response_sent_at = response['headers']['sent_at'] # remote
response_received_at = context['start'] # local
# We try to put bounds on clock skew by subtracting
# local and remote times, for local and remote events
# that are causally related.
#
# For example, suppose that the following local/remote
# logical timestamps apply to a request (for a system
# with clock skew of 100):
#
# request_sent local: 0 remote: 100
# request_recieved local: 1 remote: 101
# response_sent local: 2 remote: 102
# response_received local: 3 remote: 103
#
# Then:
#
# # Remote event *after* local is upper bound
# request_recieved.remote - request_sent.local = 101
# # Remote event *before* local is lower bound
# response_sent.remote - response_received.local = 102 - 3 = 99
#
# There's danger of further clock drift over time, but
# we don't need these to be fully accurate, and this
# should be fine for now.
clock_skew_m[i, :] = (response_sent_at-response_received_at, response_sent_at-request_sent_at)
request_overhead_m[i] = request_sent_at - start
response_overhead_m[i] = end - response_received_at
application_rtt_m[i] = response_received_at - request_sent_at
if i+1 < len(clock_skew_m):
self._measure_application_ping(d, clock_skew_m, request_overhead_m, response_overhead_m, application_rtt_m, i+1)
else:
self.clock_skew_m = clock_skew_m
self.request_overhead_m = request_overhead_m
self.response_overhead_m = response_overhead_m
self.application_rtt_m = application_rtt_m
self._report()
self._update_exposed_metrics()
# Ok, all done!
if d is not None:
d.callback(self)
ping.addCallback(success)
ping.addErrback(d.errback)
def _update_exposed_metrics(self):
with self.lock:
self._clock_skew = self.clock_skew_m.mean(axis=0) # add to local time to get remote time, as (min, max) values
self._reversed_clock_skew = -self._clock_skew[[1, 0]] # add to remote time to get local time, in format (min, max)
def _start_measure_clock_skew(self):
host = self.client.factory.address.split(':')[0]
return connection_timer.measure_clock_skew(self.client.factory.label, host)
================================================
FILE: universe/rewarder/tests/test_reward_buffer.py
================================================
from universe.rewarder import reward_buffer
def test_prereset():
buf = reward_buffer.RewardBuffer('buf')
buf.push('1', 2, False, {'key': 'value'})
reward, done, info = buf.pop()
assert reward == 0
assert done is False
print(info)
def test_mask_peek():
buf = reward_buffer.RewardBuffer('buf')
buf.set_env_info('running', 'test-v0', '1', fps=60)
buf.push('1', 1, False, {'key': 'value'})
reward, done, info = buf.pop(peek=True)
assert info['env_status.episode_id'] is None
assert info['env_status.env_state'] is None
assert info['env_status.peek.episode_id'] is None
assert info['env_status.peek.env_state'] is None
def test_single():
buf = reward_buffer.RewardBuffer('buf')
buf.reset('1')
buf.push('1', 1, False, {'key': 'value'})
reward, done, info = buf.pop()
assert reward == 1.0
assert done is False
assert info['key'] == 'value'
assert info['env_status.episode_id'] == '1'
assert info['env_status.reset.episode_id'] == '1'
assert info['env.text'] == []
def test_multiple():
buf = reward_buffer.RewardBuffer('buf')
buf.reset('1')
buf.push('1', 1, False, {'key': 'value1'})
buf.push_text('1', 'text1')
buf.push('2', 2, False, {'key': 'value2'})
buf.push_text('2', 'text2')
buf.push_text('2', 'text3')
reward, done, info = buf.pop()
assert reward == 1.0 # old
assert done is True # old
assert info['key'] == 'value1', 'Info: {}'.format(info) # old
assert info['env_status.episode_id'] == '2', 'got: {}, expected: {}'.format(info['env_status.episode_id'], '1')
assert info['env_status.complete.episode_id'] == '1'
assert info['env_status.reset.episode_id'] == '1'
assert info['env.text'] == ['text2', 'text3'] # new
reward, done, info = buf.pop()
assert reward == 2.0 # new
assert done is False
assert info['key'] == 'value2'
assert info['env_status.episode_id'] == '2'
assert 'env_status.reset.episode_id' not in info
assert info['env.text'] == []
def test_double_reset():
buf = reward_buffer.RewardBuffer('buf')
buf.reset('1')
buf.set_env_info('running', 'test-v0', '1', fps=60)
buf.push('1', 1, False, {'key': 'value1'})
buf.set_env_info('resetting', 'test-v0', '2', fps=60)
buf.push('2', 20, False, {'key': 'value2'})
reward, done, info = buf.pop(peek=True)
assert reward == 0
assert done == False
assert 'env_status.artificial.done' not in info
assert info['env_status.episode_id'] == '1'
assert info['env_status.env_state'] == 'running'
assert info['env_status.peek.episode_id'] == '2'
assert info['env_status.peek.env_state'] == 'resetting'
buf.set_env_info('running', 'test-v0', '2', fps=60)
reward, done, info = buf.pop(peek=True)
assert reward == 0
assert done == False
assert 'env_status.artificial.done' not in info
assert info['env_status.episode_id'] == '1'
assert info['env_status.env_state'] == 'running'
assert info['env_status.peek.episode_id'] == '2'
assert info['env_status.peek.env_state'] == 'running'
================================================
FILE: universe/runtimes/.agignore
================================================
flashgames.json
================================================
FILE: universe/runtimes/__init__.py
================================================
import os
import yaml
from universe.runtimes.registration import register_runtime
with open(os.path.join(os.path.dirname(__file__), '../runtimes.yml')) as f:
spec = yaml.load(f)
# If you have a local repo, do something like
# export OPENAI_DOCKER_REPO=docker.openai.com (this one only for openai folks)
docker_repo = os.environ.get('OPENAI_DOCKER_REPO', 'quay.io/openai')
register_runtime(
id='gym-core',
kind='docker',
image=docker_repo + '/universe.gym-core:{}'.format(spec['gym-core']['tag']),
)
register_runtime(
id='flashgames',
kind='docker',
image=docker_repo + '/universe.flashgames:{}'.format(spec['flashgames']['tag']),
host_config={
'privileged': True,
'cap_add': ['SYS_ADMIN'],
'ipc_mode': 'host',
},
default_params={'cpu': 3.9, 'livestream_url': None},
server_registry_file=os.path.join(os.path.dirname(__file__), 'flashgames.json'),
)
register_runtime(
id='world-of-bits',
kind='docker',
image=docker_repo + '/universe.world-of-bits:{}'.format(spec['world-of-bits']['tag']),
host_config={
'privileged': True,
'cap_add': ['SYS_ADMIN'],
'ipc_mode': 'host'
})
register_runtime(
id='vnc-windows',
kind='windows',
)
del spec
================================================
FILE: universe/runtimes/flashgames.json
================================================
{"flashgames.StealthboundLevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.StealthboundLevelPack-v0", "human_url": "http://www.games68.com/games.php?id=5000085", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cede3579d6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Scribble2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Scribble2-v0", "human_url": "http://armorgames.com/play/52", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/scribble-2-52.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Sirtet-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Sirtet-v0", "human_url": "http://www.games68.com/games.php?id=5000067", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce9beca345.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StreetRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.StreetRace-v0", "human_url": "http://www.kongregate.com/games/fightclub69/street-race", "regions": null, "height": 300, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn2.kongcdn.com/game_icons/0044/5801/250x200.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalacticGems2-v0": {"readme_header": null, "categories": ["puzzle", "match 3"], "rewarder": false, "autostart": false, "id": "flashgames.GalacticGems2-v0", "human_url": "http://www.kongregate.com/games/MikRad/galactic-gems-2", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0016/0474/live/Galactic_Gems_2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SistersOfNoMercy-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SistersOfNoMercy-v0", "human_url": "http://publishers.spilgames.com/en/game/Sisters-Of-No-Mercy,576742227280295889", "regions": null, "height": 880, "width": 660, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://games.cdn.spilcloud.com/s/sistersOfNoMercy_final.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Popopop2-v0": {"readme_header": null, "categories": ["puzzle", "bubble", "mouse only"], "rewarder": false, "autostart": false, "id": "flashgames.Popopop2-v0", "human_url": "http://www.kongregate.com/games/Rob_Almighty/popopop-2", "regions": null, "height": 600, "width": 620, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0005/6988/live/Popopop2.3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterRun-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MonsterRun-v0", "human_url": "http://www.gamesbutler.com/game/4042/monster-run/", "regions": null, "height": 420, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/b793232686691e2c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Kinetikz3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Kinetikz3-v0", "human_url": "http://armorgames.com/play/3291", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/kinetikz-3-3291.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CircuitSuperCarsRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CircuitSuperCarsRacing-v0", "human_url": "http://www.willinggames.com/racing-games/Circuit-Super-Cars-Racing.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/d/file/20140319/988b598450345d60587bb54afd459965.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GunnerMayhem-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "shooting", "2 player", "2pg", "highscore", "shooter", "survive"], "rewarder": false, "autostart": false, "id": "flashgames.GunnerMayhem-v0", "human_url": "http://old.2pg.com/game/gunner-mayhem/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/G/gunner-mayhem.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JellySnake-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.JellySnake-v0", "human_url": "http://www.gamesforwebsites.com/game/jelly-snake", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39935/game.swf?1397469707", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GasSand-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.GasSand-v0", "human_url": "http://www.miniclip.com/games/gas-and-sand/en/", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "", "enable_internet": false, "serve_from_domain": null}, "flashgames.PunchBallJump-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PunchBallJump-v0", "human_url": "http://old.2pg.com/game/punch-ball-jump/play/", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/P/punch-ball-jump.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FiveTil-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FiveTil-v0", "human_url": "http://armorgames.com/play/43", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/five-til-43.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EasterBunnyEggs-v0": {"readme_header": null, "categories": ["match-3", "action"], "rewarder": false, "autostart": false, "id": "flashgames.EasterBunnyEggs-v0", "human_url": "http://publishers.spilgames.com/en/game/Easter-Bunny-Eggs,576742227280291408", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://games.cdn.spilcloud.com/container_df9aa4793b5/1396454361_easter_bunny_eggs_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl5-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SweetTooth-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SweetTooth-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/sweet-tooth", "regions": null, "height": 390, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3129/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AssembleBots-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AssembleBots-v0", "human_url": "http://www.games68.com/games.php?id=25225", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae260366384.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MexicoRex-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MexicoRex-v0", "human_url": "http://www.games68.com/games.php?id=25637", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb3bd1663.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonkeyGems-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": true, "id": "flashgames.MonkeyGems-v0", "human_url": "http://www.arkadium.com/games/monkey-gems/", "regions": null, "height": 444, "width": 640, "extra_files": {"relative": {"files": ["data/monkeygems_config.xml", "data/monkeygems_leveldata_en-US.xml", "data/monkeygems_sounds.xml", "data/levels/level0.xml", "data/levels/level2.xml", "data/resources.swf", "data/levels/minigame2.xml", "data/levels/level3.xml", "data/levels/level4.xml", "data/levels/level5.xml", "data/levels/level6.xml", "data/levels/level7.xml", "data/levels/level8.xml", "data/levels/level9.xml", "data/levels/level10.xml", "data/External_Sounds/bonus_point_tally.mp3", "data/External_Sounds/button_press.mp3", "data/External_Sounds/cocbomb_explode.mp3", "data/External_Sounds/cocbomb_pickup.mp3", "data/External_Sounds/cocobomb_tick.mp3", "data/External_Sounds/fruit_hit.mp3", "data/External_Sounds/gem_added_1.mp3", "data/External_Sounds/gem_added_2.mp3", "data/External_Sounds/intro_movie.mp3", "data/External_Sounds/match_1.mp3", "data/External_Sounds/match_2.mp3", "data/External_Sounds/match_3.mp3", "data/External_Sounds/match_4.mp3", "data/External_Sounds/match_5.mp3", "data/External_Sounds/match_6.mp3", "data/External_Sounds/match_7.mp3", "data/External_Sounds/monkey_noise_1.mp3", "data/External_Sounds/monkey_noise_2.mp3", "data/External_Sounds/monkey_noise_3.mp3", "data/External_Sounds/monkey_noise_4.mp3", "data/External_Sounds/monkey_noise_5.mp3", "data/External_Sounds/multiplier_activated.mp3", "data/External_Sounds/music/gameLoop.mp3", "data/External_Sounds/music/introAmbience.mp3", "data/External_Sounds/music/introMusic.mp3", "data/External_Sounds/music/jg_sting_lose.mp3", "data/External_Sounds/music/jg_sting_neutral.mp3", "data/External_Sounds/music/jg_sting_win.mp3", "data/External_Sounds/music/minigameLoop.mp3", "data/External_Sounds/music/rolling_loop.mp3", "data/External_Sounds/placecard_fall.mp3", "data/External_Sounds/play_button.mp3", "data/External_Sounds/points_bonus.mp3", "data/External_Sounds/reverse_activated.mp3", "data/External_Sounds/rollover.mp3", "data/External_Sounds/slow_activated.mp3", "data/External_Sounds/snake_head_tail_match.mp3", "data/External_Sounds/taget_pickup.mp3", "data/External_Sounds/throw_gem_1.mp3", "data/External_Sounds/throw_gem_2.mp3", "data/External_Sounds/throw_gem_3.mp3", "data/External_Sounds/transition.mp3", "data/External_Sounds/ui.mp3", "data/External_Sounds/warning_sound.mp3", "config.txt", "data/external_images/levels/background.jpg", "data/logger_config.xml", "data/external_loggers/gameplay-logger.swf", "data/external_loggers/google-analytics-logger.swf", "data/external_loggers/gameplay-logger.swf", "data/levels/level0.xml", "data/levels/level2.xml", "data/levels/minigame2.xml", "data/levels/level3.xml", "data/levels/level4.xml", "data/levels/minigame2.xml", "data/levels/level5.xml", "data/levels/level6.xml", "data/levels/level7.xml", "data/levels/level8.xml", "data/levels/minigame2.xml", "data/levels/level9.xml", "data/levels/level10.xml", "data/external_images/levels/background.jpg", "data/external_images/levels/background10.jpg", "data/external_images/levels/background2.jpg", "data/external_images/levels/background3.jpg", "data/external_images/levels/background4.jpg", "data/external_images/levels/background5.jpg", "data/external_images/levels/background6.jpg", "data/external_images/levels/background7.jpg", "data/external_images/levels/background8.jpg", "data/external_images/levels/background9.jpg", "data/external_images/levels/foreground.png", "data/external_images/levels/level10-front.png", "data/external_images/levels/level3-front.png", "data/external_images/levels/level4-front.png", "data/external_images/levels/level5-front.png", "data/external_images/levels/level7-front.png", "data/external_images/minigame/background.jpg"], "base": "http://amsarkadium-a.akamaihd.net/assets/global/game/monkey-gems/b26a4e9b-b507-4d60-94c5-b3911e1bf0d9"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://amsarkadium-a.akamaihd.net/assets/global/game/monkey-gems/b26a4e9b-b507-4d60-94c5-b3911e1bf0d9/monkey-gems.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CitySkyTyping-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CitySkyTyping-v0", "human_url": "http://www.gamesforwebsites.com/game/city-sky-typing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24129/game.swf?1359377887", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheBoomlandsWorldWars-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.TheBoomlandsWorldWars-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/the-boomlands-world-wars", "regions": null, "height": 650, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38711/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlobsStory-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlobsStory-v0", "human_url": "http://publishers.spilgames.com/en/game/Blob's-Story-2,576742227280292724", "regions": null, "height": 720, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/Blobs_story/Blob.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MushroomFarmDefender-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.MushroomFarmDefender-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/mushroom-farm-defender", "regions": null, "height": 500, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15198/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Match3Adventure-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Match3Adventure-v0", "human_url": "http://www.gamesforwebsites.com/game/match-3-adventure", "regions": null, "height": 610, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/21904/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PenguinCubes-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.PenguinCubes-v0", "human_url": "http://publishers.spilgames.com/en/game/Penguin-Cubes,576742227280289226", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/penguincubes.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeavyLegion2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HeavyLegion2-v0", "human_url": "http://1000webgames.com/play-9770-Heavy-Legion-2.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/heavylegion2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl9-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl9-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Autoattack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Autoattack-v0", "human_url": "http://www.games68.com/games.php?id=5000090", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cee8bd3c0d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Retron-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Retron-v0", "human_url": "http://armorgames.com/play/512", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/retron-512.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PerilousJourney2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PerilousJourney2-v0", "human_url": "http://www.games68.com/games.php?id=25606", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea9e735df.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FluffRush-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FluffRush-v0", "human_url": "http://www.gamesbutler.com/game/6453/fluff-rush/", "regions": null, "height": 400, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/9ff5e14c5b05ddc3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Tumblestump2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Tumblestump2-v0", "human_url": "http://www.miniclip.com/games/tumblestump-2/en/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/tumblestump-2/en/Tumblestump2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlappyAdventure-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlappyAdventure-v0", "human_url": "http://www.gamesforwebsites.com/game/flappy-adventure", "regions": null, "height": 650, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/124560/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Conquerium-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Conquerium-v0", "human_url": "http://www.kongregate.com/games/BerzerkStudio/conquerium", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/4716/live/embeddable_174716.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RunNGun-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.RunNGun-v0", "human_url": "http://www.miniclip.com/games/run-n-gun/en/", "regions": null, "height": 400, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/run-n-gun/en/rungun.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BombThePiratePigs-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BombThePiratePigs-v0", "human_url": "http://www.games68.com/games.php?id=25200", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2562a31e5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CemeteryRoad-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.CemeteryRoad-v0", "human_url": "http://www.kongregate.com/games/fightclub69/cemetery-road", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn3.kongcdn.com/game_icons/0048/7983/250x200.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.ExperimentalShooter2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.ExperimentalShooter2-v0", "human_url": "http://publishers.spilgames.com/en/game/Experimental-Shooter-2,576742227280289294", "regions": null, "height": 700, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/e/Experimental_shooter/experimental_shooter2_final.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JollySwipe-v0": {"readme_header": null, "categories": ["puzzle", "1 player", "flash", "shooting", "matching", "monsters", "free", "idnet", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.JollySwipe-v0", "human_url": "http://www.y8.com/games/jolly_swipe", "regions": null, "height": 640, "width": 360, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/91995/original/jolly_swipe.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TtRacer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TtRacer-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/tt-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0018/7756/live/embeddable_187756.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RhythmSnake-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RhythmSnake-v0", "human_url": "http://armorgames.com/play/101", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/rhythm-snake-101.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Fizzion-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Fizzion-v0", "human_url": "http://www.gamesforwebsites.com/game/fizzion", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130403/game.swf?1424948746", "enable_internet": false, "serve_from_domain": null}, "flashgames.Alien-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Alien-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/alien", "regions": null, "height": 400, "width": 300, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/295/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Contra3TheAlienWars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Contra3TheAlienWars-v0", "human_url": "http://www.games68.com/games.php?id=25154", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2490c41ed.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AcidFactory-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.AcidFactory-v0", "human_url": "http://www.miniclip.com/games/acid-factory/en/", "regions": null, "height": 501, "width": 782, "extra_files": {"relative": {"files": ["cave1.txt"], "base": "http://www.miniclip.com/games/acid-factory/en"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/acid-factory/en/acidfactory.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LongJump-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.LongJump-v0", "human_url": "http://www.gamesforwebsites.com/game/long-jump", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22907/game.swf?1344258815", "enable_internet": false, "serve_from_domain": null}, "flashgames.TwinkleStarRush-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TwinkleStarRush-v0", "human_url": "http://publishers.spilgames.com/en/game/Twinkle-Star-Rush,576742227280285062", "regions": null, "height": 680, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/Twinkle_star_rush/twinkle_star_rush.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Weirdville-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.Weirdville-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/weirdville/", "regions": null, "height": 449, "width": 611, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/weirdville/weirdville.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AtvRide-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AtvRide-v0", "human_url": "http://1000webgames.com/play-10038-ATV-Ride.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/atvride.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Dropblox-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Dropblox-v0", "human_url": "http://armorgames.com/play/779", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/dropblox-779.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WreckRoad-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.WreckRoad-v0", "human_url": "http://turbonuke.com/games.php?game=wreckroad", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://turbonuke.com/flashgames/monstertruckfever.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Minicarting-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.Minicarting-v0", "human_url": "http://www.dailygames.com/games/minicarting.html", "regions": null, "height": 419, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.dailygames.com/games/minicarting.html", "enable_internet": false, "serve_from_domain": null}, "flashgames.ExtremeAirWars-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ExtremeAirWars-v0", "human_url": "http://www.gamesforwebsites.com/game/extreme-air-wars", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39716/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Wheelers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.Wheelers-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/wheelers", "regions": null, "height": 300, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/1341/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl8-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl13-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl13-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MusicStomp-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MusicStomp-v0", "human_url": "http://armorgames.com/play/1363", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/music-stomp-1363.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlowerGuardian-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.FlowerGuardian-v0", "human_url": "http://publishers.spilgames.com/en/game/Flower-Guardian,576742227280290484", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/f/Flowerguardian/FlowerGuardian.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IsoblockerMaster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IsoblockerMaster-v0", "human_url": "http://www.y8.com/games/isoblocker_master", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/101661/original/isoblocker_master.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BushRoyalRampage-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.BushRoyalRampage-v0", "human_url": "http://www.miniclip.com/games/bush-royal-rampage/en/", "regions": null, "height": 455, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/bush-royal-rampage/en/bushroyalrampage.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Sundrops-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Sundrops-v0", "human_url": "http://notdoppler.com/sundrops.php", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/sundrops.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TowerCollapseDeluxe-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TowerCollapseDeluxe-v0", "human_url": "http://www.gamesforwebsites.com/game/tower-collapse-deluxe", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130844/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalaxyDefender-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GalaxyDefender-v0", "human_url": "http://armorgames.com/play/2929", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/galaxy-defender-2929.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JamesTheSpaceZebra-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.JamesTheSpaceZebra-v0", "human_url": "http://armorgames.com/play/207/james-the-space-zebra", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/james-the-space-zebr-207.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MiniMachines-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.MiniMachines-v0", "human_url": "http://www.willinggames.com/racing-games/Mini-Machines.html", "regions": null, "height": 480, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/flash10/Mini-Machines.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl2-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperCandyGems-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SuperCandyGems-v0", "human_url": "http://www.gamesforwebsites.com/game/super-candy-gems", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133479/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MotoTrialMania-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MotoTrialMania-v0", "human_url": "http://www.games68.com/games.php?id=25573", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee9eeb99ab.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HighSpeedChase-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.HighSpeedChase-v0", "human_url": "http://www.johnnytwoshoes.com/game/highspeedchase", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.addictinggames.com/newGames/car-games/highspeedchase/highspeedchase.swf?c=18", "enable_internet": false, "serve_from_domain": null}, "flashgames.CableCapers2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.CableCapers2-v0", "human_url": "http://www.miniclip.com/games/cable-capers-2/en/", "regions": null, "height": 436, "width": 546, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/cable-capers-2/en/cablecapers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperbikeRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SuperbikeRacer-v0", "human_url": "http://notdoppler.com/superbikeracer.php", "regions": null, "height": 480, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/superbikeracer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpinSprint-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpinSprint-v0", "human_url": "http://armorgames.com/play/1379", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/spin-sprint-1379.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ElainesBakery-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ElainesBakery-v0", "human_url": "http://publishers.spilgames.com/en/game/Elaine's-Bakery,576742227280289065", "regions": null, "height": 700, "width": 420, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/e/elains_bakery.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PouJetpack-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PouJetpack-v0", "human_url": "http://www.gamesforwebsites.com/game/pou-jetpack", "regions": null, "height": 700, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/128690/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AlchemySwap-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AlchemySwap-v0", "human_url": "http://www.games68.com/games.php?id=25158", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae24a30f566.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HalloweenJam-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HalloweenJam-v0", "human_url": "http://armorgames.com/play/1385", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/halloween-jam-1385.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MineHero-v0": {"readme_header": null, "categories": ["2 player", "arcade", "kids", "our", "1 player", "2 players", "2pg", "bombs", "bubbles", "free", "fun", "hero", "keyboard", "kids", "mine", "pixelated"], "rewarder": false, "autostart": false, "id": "flashgames.MineHero-v0", "human_url": "http://old.2pg.com/game/mine-hero/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/M/mine_hero_2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Jumprunner-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Jumprunner-v0", "human_url": "http://www.gamesbutler.com/game/7786/jumprunner/", "regions": null, "height": 240, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/72bd3d7c7668233b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DumperRush-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.DumperRush-v0", "human_url": "http://www.cartitans.com/game/dumper-rush/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/dumper-rush.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SchoolBusRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SchoolBusRacing-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/school-bus-racing", "regions": null, "height": 430, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38524/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BalloonHero-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BalloonHero-v0", "human_url": "http://www.y8.com/games/balloon_hero", "regions": null, "height": 460, "width": 690, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/91742/original/balloon_hero.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MarshmallowsEscape-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MarshmallowsEscape-v0", "human_url": "http://www.games68.com/games.php?id=5000189", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e2e291f339.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Astroman-v0": {"readme_header": null, "categories": ["2 player", "action", "our", "2 player", "2pg", "action", "astroman", "astronaut", "collecting", "space", "spaceship"], "rewarder": false, "autostart": false, "id": "flashgames.Astroman-v0", "human_url": "http://old.2pg.com/game/astroman/play/", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/A/astroman.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CandyMatch-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CandyMatch-v0", "human_url": "http://www.neongames.com/game/Candy+Match", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/snowqueen.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DotsRevamped-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DotsRevamped-v0", "human_url": "http://notdoppler.com/dots-revamped.php", "regions": null, "height": 400, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/dots-revamped.swf?1", "enable_internet": false, "serve_from_domain": null}, "flashgames.PumpkinCollector-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PumpkinCollector-v0", "human_url": "http://www.gamesforwebsites.com/game/pumpkin-collector", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133872/game.swf?1445252260", "enable_internet": false, "serve_from_domain": null}, "flashgames.V8MuscleCars3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.V8MuscleCars3-v0", "human_url": "http://insanehero.com/mygames/V8MuscleCars3/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/V8MuscleCars3/v8musclecars3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CharlieTheDuck-v0": {"readme_header": null, "categories": ["jump and run"], "rewarder": false, "autostart": false, "id": "flashgames.CharlieTheDuck-v0", "human_url": "http://www.platformgames.com/game/Charlie+the+Duck", "regions": null, "height": 369, "width": 495, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.platformgames.com/uploaded/swf/charlietheduck.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl15-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl15-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dRallyFever-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.3dRallyFever-v0", "human_url": "http://www.cartitans.com/game/3d-rally-fever/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-rally-fever.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Zevil2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Zevil2-v0", "human_url": "http://publishers.spilgames.com/en/game/Zevil-2,576742227280295814", "regions": null, "height": 700, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/Zevil2/zevil_2_securex.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NukeDefense-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.NukeDefense-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/nuke-defense", "regions": null, "height": 524, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/35163/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RollTheCluster-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.RollTheCluster-v0", "human_url": "http://www.gamesforwebsites.com/game/roll-the-cluster", "regions": null, "height": 480, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129856/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlyAwayRabbit2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FlyAwayRabbit2-v0", "human_url": "http://www.gamesbutler.com/game/4314/fly-away-rabbit-2/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/647d27d84a1350d5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl5-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchJong-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchJong-v0", "human_url": "http://www.gamesforwebsites.com/game/match-jong", "regions": null, "height": 600, "width": 360, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/131164/game.swf?1430134103", "enable_internet": false, "serve_from_domain": null}, "flashgames.Aerorumble-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Aerorumble-v0", "human_url": "http://armorgames.com/play/10431", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/aerorumble-10431.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DeathDiceOverdose-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DeathDiceOverdose-v0", "human_url": "http://www.gamesbutler.com/game/2449/death-dice-overdose/", "regions": null, "height": 450, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/deathdicea.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HappyEasterEggs-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.HappyEasterEggs-v0", "human_url": "http://www.neongames.com/game/Happy+Easter+Eggs", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/happyeastereggs.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WishTotemsLevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WishTotemsLevelPack-v0", "human_url": "http://www.gamesbutler.com/game/15499/wish-totems-level-pack/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/wishTotems_lvlgb.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl12-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl12-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Popopop-v0": {"readme_header": null, "categories": ["puzzle", "bubble", "mouse only"], "rewarder": false, "autostart": false, "id": "flashgames.Popopop-v0", "human_url": "http://www.kongregate.com/games/Rob_Almighty/popopop", "regions": null, "height": 600, "width": 620, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0003/7854/live/Popopop_Final9_.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AlienTransporter-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AlienTransporter-v0", "human_url": "http://armorgames.com/play/17954", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/alien-transporter-17954.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperbikeExtreme-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SuperbikeExtreme-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/superbike-extreme", "regions": null, "height": 430, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/22044/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ParkingFury-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ParkingFury-v0", "human_url": "http://www.games68.com/games.php?id=5000152", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e0de592b1a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperPuzzlePlatformer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperPuzzlePlatformer-v0", "human_url": "http://www.gamesbutler.com/game/5840/super-puzzle-platformer/", "regions": null, "height": 400, "width": 320, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/14336e24782f011f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ToyRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ToyRacers-v0", "human_url": "http://www.cartitans.com/game/toy-racers/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/toy-racers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ArkanoidGame-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ArkanoidGame-v0", "human_url": "http://www.y8.com/games/arkanoid_game", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/44622/original/arkanoid_game.swf", "enable_internet": false, "serve_from_domain": null}, "internet.SlitherIOEasy-v0": {"categories": ["mmo"], "height": 300, "width": 502, "server_timestep_limit": null, "tags": {}, "id": "internet.SlitherIOEasy-v0", "rewarder": true, "url": "http://slither.io/", "enable_internet": true, "autostart": true}, "flashgames.SliceTheBoxRemaster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SliceTheBoxRemaster-v0", "human_url": "http://armorgames.com/play/15584", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/slice-the-box-remast-15584.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DualDimension-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DualDimension-v0", "human_url": "http://armorgames.com/play/17972", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/dual-dimension-17972.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleTanksTd15-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleTanksTd15-v0", "human_url": "http://armorgames.com/play/6338", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/bubble-tanks-td-15-6338.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PiggyWiggy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PiggyWiggy-v0", "human_url": "http://notdoppler.com/piggywiggy.php", "regions": null, "height": 460, "width": 690, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/piggywiggy.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Flashcycle2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Flashcycle2-v0", "human_url": "http://www.games68.com/games.php?id=5000072", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ceac150b02.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SkyQuest-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SkyQuest-v0", "human_url": "http://notdoppler.com/skyquest.php", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/skyquest.swf?15", "enable_internet": false, "serve_from_domain": null}, "flashgames.SkyIsland-v0": {"readme_header": null, "categories": ["sparsereward"], "rewarder": false, "autostart": false, "id": "flashgames.SkyIsland-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/skyisland/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/skyisland/skyisland.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.DeepForest3dRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.DeepForest3dRace-v0", "human_url": "http://www.cartitans.com/game/deep-forest-3d-race/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/deep-forest-3d-race.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BunnyCannon-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BunnyCannon-v0", "human_url": "http://notdoppler.com/bunnycannon.php", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/bunnycannon.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacer-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Offroaders-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.Offroaders-v0", "human_url": "http://notdoppler.com/offroaders.php", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/offroaders.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ElClassico-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ElClassico-v0", "human_url": "http://www.games68.com/games.php?id=25190", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25455c248.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BuildBalance2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BuildBalance2-v0", "human_url": "http://www.games68.com/games.php?id=25581", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea38e7f62.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MasterDifference-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MasterDifference-v0", "human_url": "http://www.games68.com/games.php?id=5000175", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c39120de.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl16-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl16-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JungleEagle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.JungleEagle-v0", "human_url": "http://www.gamesforwebsites.com/game/jungle-eagle", "regions": null, "height": 500, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/125342/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Canopy-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Canopy-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/canopy/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/canopy/canopy.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.RacerKartz-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RacerKartz-v0", "human_url": "http://old.2pg.com/game/racer-kartz/play/", "regions": null, "height": 520, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/racerkartz2player.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Goldextraction-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Goldextraction-v0", "human_url": "http://www.gamesforwebsites.com/game/goldextraction", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/128693/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MummyMadness-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MummyMadness-v0", "human_url": "http://www.gamesbutler.com/game/25597/mummy-madness/", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/edd7926ed359da07.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RacingSupercarChampionship-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.RacingSupercarChampionship-v0", "human_url": "http://www.willinggames.com/racing-games/Racing-Supercar-Championship.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/d/file/20160217/80bebcdefd7ffc44ae72c904855e1953.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dUrbanMadness2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.3dUrbanMadness2-v0", "human_url": "http://www.cartitans.com/game/3d-urban-madness-2/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/upload/games/1876/7308727498.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HappyBallz-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HappyBallz-v0", "human_url": "http://1000webgames.com/play-7033-Happy-Ballz.html", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/sponsor-1000webgames-HappyBallz.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmigoPancho4Travel-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AmigoPancho4Travel-v0", "human_url": "http://publishers.spilgames.com/en/game/Amigo-Pancho-4:-Travel,576742227280290755", "regions": null, "height": 690, "width": 460, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/a/Amigo_Pancho4/AmigoPancho4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ObamaAlienDefense-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.ObamaAlienDefense-v0", "human_url": "http://www.miniclip.com/games/obama-alien-defense/en/", "regions": null, "height": 400, "width": 590, "extra_files": {"relative": {"files": ["levels/game.intro.swf", "component.txt", "levels/newyork.json", "avatarloader.txt", "levels/london.json", "levels/newyork.intro.swf", "levels/newyork.assets.swf", "levels/shared.assets.swf", "levels/global.assets.swf", "levels/global.sounds.swf"], "base": "http://www.miniclip.com/games/obama-alien-defense/en"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/obama-alien-defense/en/obama-alien-defense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DinoMeatHunt3Extra-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DinoMeatHunt3Extra-v0", "human_url": "http://www.games68.com/games.php?id=25397", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae31b572e41.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SaveTheDummyHolidays-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SaveTheDummyHolidays-v0", "human_url": "http://www.gamesbutler.com/game/18677/save-the-dummy-holidays/", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/save-the-dumhol3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IceRun-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IceRun-v0", "human_url": "http://www.kongregate.com/games/rumblesushi/ice-run", "regions": null, "height": 422, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/7488/live/embeddable_167488.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Zed-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Zed-v0", "human_url": "http://www.miniclip.com/games/zed/en/", "regions": null, "height": 390, "width": 546, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/zed/en/zed.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZooRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ZooRacer-v0", "human_url": "http://www.kongregate.com/games/citizenphil/zoo-racer", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0011/2729/live/zoo_racer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchToEnjoyLevelPack-v0": {"readme_header": null, "categories": ["puzzle", "match 3", "mouse only"], "rewarder": false, "autostart": false, "id": "flashgames.MatchToEnjoyLevelPack-v0", "human_url": "http://www.kongregate.com/games/charstudio/match-to-enjoy-level-pack", "regions": null, "height": 527, "width": 620, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0015/5971/live/game_match_to_enjoy_level_pack_mathnook_kong.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BigWheelsTrial-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BigWheelsTrial-v0", "human_url": "http://1000webgames.com/play-10106-Big-Wheels-Trial.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/bigwheelstrial.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RapaNui-v0": {"readme_header": null, "categories": [], "rewarder": true, "autostart": false, "id": "flashgames.RapaNui-v0", "human_url": "http://www.neongames.com/game/Rapa+Nui", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/rapanui.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NOfficialWebVersion-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NOfficialWebVersion-v0", "human_url": "http://www.kongregate.com/games/MetanetSoftware/n", "regions": null, "height": 600, "width": 792, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0002/4483/live/embeddable_24483.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GsSoccerWorldCup-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GsSoccerWorldCup-v0", "human_url": "http://www.games68.com/games.php?id=25201", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2565284bf.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchTheBugz-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.MatchTheBugz-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/match-the-bugz", "regions": null, "height": 420, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3023/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AlienAssault-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.AlienAssault-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/alien-assault", "regions": null, "height": 392, "width": 670, "extra_files": {"absolute": ["http://x.fogdev.com/api/libv3-5.swf"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38203/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TastyFruits-v0": {"readme_header": null, "categories": ["match-3", "fun"], "rewarder": false, "autostart": false, "id": "flashgames.TastyFruits-v0", "human_url": "http://publishers.spilgames.com/en/game/Tasty-Fruits,576742227280291236", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/Tasty_Fruits/tasty_fruits.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StrikeForceKitty-v0": {"readme_header": null, "categories": ["1 player", "flash", "action", "fish", "series", "collecting", "animal", "kitten", "cat", "running", "free", "idnet", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.StrikeForceKitty-v0", "human_url": "http://www.y8.com/games/strikeforce_kitty", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/76347/original/strikeforce_kitty.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UltimateLegend-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.UltimateLegend-v0", "human_url": "http://www.games68.com/games.php?id=25233", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae261bbc4ce.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ExploreTheCandies-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ExploreTheCandies-v0", "human_url": "http://www.gamesforwebsites.com/game/explore-the-candies", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/134961/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BoatDrive-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BoatDrive-v0", "human_url": "http://1000webgames.com/play-10030-Boat-Drive.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/boatdrive.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Hearts-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Hearts-v0", "human_url": "http://armorgames.com/play/838", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/hearts-838.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NinjaPandaCouple-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "1 player", "2 players", "2playergames", "animal", "free", "fun", "journey", "kids", "ninja", "panda", "run", "runner", "running", "speed", "timing"], "rewarder": false, "autostart": false, "id": "flashgames.NinjaPandaCouple-v0", "human_url": "http://old.2pg.com/game/ninja-panda-couple/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/ninjapandacouple2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Match3PresentBoxSaga-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Match3PresentBoxSaga-v0", "human_url": "http://www.gamesforwebsites.com/game/match-3--present-box-saga", "regions": null, "height": 720, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/131263/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Hamsterball-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Hamsterball-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/hamsterball", "regions": null, "height": 440, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/14992/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WarBerlinIdle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WarBerlinIdle-v0", "human_url": "http://www.games68.com/games.php?id=25373", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae30e48d439.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleMover-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleMover-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Mover,576742227280284474", "regions": [{"coordinates": [474, 139, 505, 99], "type": "noclick"}], "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_mover/bubble_mover.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.F1RacingChallenge-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.F1RacingChallenge-v0", "human_url": "http://www.willinggames.com/racing-games/F1-Racing-Challenge.html", "regions": null, "height": 375, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/d/file/20140206/af8f5c023cea506ac794cda339fb820f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BoxBlocks-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BoxBlocks-v0", "human_url": "http://www.y8.com/games/box_blocks", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/121559/original/box_blocks.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl4-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LuxUltimate-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "1 player", "2 players", "2pg", "flash", "highscore", "pixelated", "runner", "running"], "rewarder": true, "autostart": false, "id": "flashgames.LuxUltimate-v0", "human_url": "http://old.2pg.com/game/lux-ultimate/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/lux_ultimate_2pg_1375621235.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Knockers-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Knockers-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/knockers", "regions": null, "height": 440, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/245/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Business-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Business-v0", "human_url": "http://www.games68.com/games.php?id=25442", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae342d06573.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl3-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CattlepultPlayerPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CattlepultPlayerPack-v0", "human_url": "http://armorgames.com/play/356", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/cattlepult-player-pa-356.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PenguinSkate2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PenguinSkate2-v0", "human_url": "http://armorgames.com/play/506", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/penguin-skate-2-506.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TouchTheBubbles4-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TouchTheBubbles4-v0", "human_url": "http://www.gamesforwebsites.com/game/touch-the-bubbles-4", "regions": null, "height": 495, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38928/game.swf?1379932825", "enable_internet": false, "serve_from_domain": null}, "flashgames.Mimelet-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.Mimelet-v0", "human_url": "http://www.miniclip.com/games/mimelet/en/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.miniclip.com/games/mimelet/en/mimelet.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlashRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FlashRace-v0", "human_url": "http://www.y8.com/games/flash_race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/671/original/flash_race.dcr?1462198647", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl8-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EasterEggsChallenge-v0": {"readme_header": null, "categories": ["1 player", "flash", "girl", "matching", "animal", "rabbit", "free", "match 3", "idnet", "idnet highscore", "idnet achievements"], "rewarder": true, "autostart": true, "id": "flashgames.EasterEggsChallenge-v0", "human_url": "http://www.y8.com/games/easter_eggs_challenge", "regions": [{"coordinates": [212, 59, 128, 106], "type": "noclick"}, {"coordinates": [17, 48, 128, 47], "type": "noclick"}], "height": 570, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109875/original/easter_eggs_challenge.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Tosuta-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Tosuta-v0", "human_url": "http://armorgames.com/play/309", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/to-suta-309.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IdleChop-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IdleChop-v0", "human_url": "http://www.games68.com/games.php?id=5000093", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cef013a1f8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombieTdReborn-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": true, "autostart": false, "id": "flashgames.ZombieTdReborn-v0", "human_url": "http://publishers.spilgames.com/en/game/Zombie-TD:-Reborn,576742227280285285", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/z/zombie_tower_defense/Zombie_tower_defense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HiredHeroes-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HiredHeroes-v0", "human_url": "http://www.kongregate.com/games/Badim/hired-heroes", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0015/9794/live/embeddable_159794.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleHitValentine-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleHitValentine-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Hit-Valentine,576742227280284223", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_hit_valentine/bubble_hit_valentine.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmashTheSwine-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SmashTheSwine-v0", "human_url": "http://old.2pg.com/game/smash-the-swine/play/", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/S/smash-the-swine.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FpaWorld1Remix-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FpaWorld1Remix-v0", "human_url": "http://www.games68.com/games.php?id=5000114", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/game-1466499647.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalacticGems-v0": {"readme_header": null, "categories": ["puzzle", "match 3"], "rewarder": true, "autostart": false, "id": "flashgames.GalacticGems-v0", "human_url": "http://www.kongregate.com/games/MikRad/galactic-gems", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0012/9386/live/Galactic_Gems.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacer2012-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.FormulaRacer2012-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer-2012", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0014/2671/live/FormulaRacer2012.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FredFigglehorn-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.FredFigglehorn-v0", "human_url": "http://www.miniclip.com/games/fred-figglehorn/en/", "regions": null, "height": 400, "width": 938, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/fred-figglehorn/en/fred.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RhythmBlasterV2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RhythmBlasterV2-v0", "human_url": "http://armorgames.com/play/102", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/rhythm-blaster-v2-102.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChickCannont-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ChickCannont-v0", "human_url": "http://www.gamesforwebsites.com/game/chick-cannon-2", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38904/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WarHeroes-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WarHeroes-v0", "human_url": "http://www.games68.com/games.php?id=25403", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae3208ee8a3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KamikazeRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.KamikazeRace-v0", "human_url": "http://www.kongregate.com/games/orbgames/kamikaze-race", "regions": null, "height": 400, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://assets.kongregate.com/gamez/0004/0198/live/kamikaze-race.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cooliobeat-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Cooliobeat-v0", "human_url": "http://armorgames.com/play/104", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/cooliobeat-104.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SliceTheBox-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SliceTheBox-v0", "human_url": "http://armorgames.com/play/14864", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/slice-the-box-14864.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl6-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlackAndWhiteEscapeTheOffice-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlackAndWhiteEscapeTheOffice-v0", "human_url": "http://www.games68.com/games.php?id=25570", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee9e1be0a3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GunExpress-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.GunExpress-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/gun-express", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0008/6188/live/GunExpress.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PiratesAndCannons-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PiratesAndCannons-v0", "human_url": "http://www.gamesbutler.com/game/25336/pirates-and-cannons/", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/0cca539b4e158630.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ParallelLevels-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ParallelLevels-v0", "human_url": "http://armorgames.com/play/15784", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/parallel-levels-15784.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PinataWarriors-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PinataWarriors-v0", "human_url": "http://old.2pg.com/game/pinata-warriors/play/", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/pinata-warriors.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UrbanMicroRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.UrbanMicroRacers-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/urban-micro-racers", "regions": null, "height": 500, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/22001/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DisasterWillStrikeUltimateDisaster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DisasterWillStrikeUltimateDisaster-v0", "human_url": "http://www.games68.com/games.php?id=5000049", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce76ae376d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cleopatra-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Cleopatra-v0", "human_url": "http://www.games68.com/games.php?id=25157", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae249d461e7.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarsVsRobots-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CarsVsRobots-v0", "human_url": "http://publishers.spilgames.com/en/game/Cars-vs.-Robots,576742227280285426", "regions": null, "height": 720, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/c/cars_vs_robots/Cars_vs_robots.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cattlepult-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Cattlepult-v0", "human_url": "http://armorgames.com/play/208", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/cattlepult-208.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AWeekendAtTweetys-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AWeekendAtTweetys-v0", "human_url": "http://www.games68.com/games.php?id=5000278", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ec2568de50.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaXspeed3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaXspeed3d-v0", "human_url": "http://www.y8.com/games/formula_xspeed_3d", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/92201/original/formula_xspeed_3d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PlaneRace-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PlaneRace-v0", "human_url": "http://1000webgames.com/play-9179-Plane-Race.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/planerace.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeroRoofTop-v0": {"readme_header": null, "categories": ["1 player", "flash", "mouse skill", "running", "jumping", "free", "idnet", "idnet highscore"], "rewarder": true, "autostart": false, "id": "flashgames.HeroRoofTop-v0", "human_url": "http://www.y8.com/games/hero_roof_top", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/119142/original/hero_roof_top.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.YepisJourney-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.YepisJourney-v0", "human_url": "http://www.gamesforwebsites.com/game/yepis-journey", "regions": null, "height": 640, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38546/game.swf?1374486404", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl10-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl10-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl14-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl14-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HungryLittlePenguins-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HungryLittlePenguins-v0", "human_url": "http://1000webgames.com/play-7839-Hungry-Little-Penguins.html", "regions": null, "height": 375, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/hungry-little-penguins.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RingsideHero-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RingsideHero-v0", "human_url": "http://www.games68.com/games.php?id=25167", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae24e1cd888.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl4-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BoxingLiveRound2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BoxingLiveRound2-v0", "human_url": "http://www.games68.com/games.php?id=5000016", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c1765ed094.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HalloweenExplorer-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.HalloweenExplorer-v0", "human_url": "http://www.gamesforwebsites.com/game/halloween-explorer", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133265/game.swf?1442315789", "enable_internet": false, "serve_from_domain": null}, "flashgames.FallDamage-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FallDamage-v0", "human_url": "http://armorgames.com/play/12289", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/fall-damage-12289.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ParachuteRetrospect-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ParachuteRetrospect-v0", "human_url": "http://armorgames.com/play/50", "regions": null, "height": 550, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/parachute-retrospect-50.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombieDemolisher3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ZombieDemolisher3-v0", "human_url": "http://www.games68.com/games.php?id=25617", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeadb4dd65.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalaxyMission-v0": {"readme_header": null, "categories": ["match-3", "action"], "rewarder": false, "autostart": false, "id": "flashgames.GalaxyMission-v0", "human_url": "http://publishers.spilgames.com/en/game/Galaxy-Mission,576742227280295981", "regions": null, "height": 540, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/galaxymission_1908/galaxymission_1908.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DolphinVolleyball-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DolphinVolleyball-v0", "human_url": "http://old.2pg.com/game/dolphin-volleyball/play/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/D/dolphin_volleyball.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GravityGuy-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.GravityGuy-v0", "human_url": "http://www.miniclip.com/games/gravity-guy/en/", "regions": null, "height": 501, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/gravity-guy/en/gravityguy.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MidnightMiner-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MidnightMiner-v0", "human_url": "http://www.games68.com/games.php?id=25335", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2faa41fb4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Paintwars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Paintwars-v0", "human_url": "http://www.games68.com/games.php?id=25393", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae3192c6579.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChristmasBubbles-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.ChristmasBubbles-v0", "human_url": "http://publishers.spilgames.com/en/game/Christmas-Bubbles,576742227280290287", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash//c/Christmas_bubbles/christmasbubbles.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl11-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl11-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalacticGems2LevelPack-v0": {"readme_header": null, "categories": ["puzzle", "match 3"], "rewarder": false, "autostart": false, "id": "flashgames.GalacticGems2LevelPack-v0", "human_url": "http://www.kongregate.com/games/MikRad/galactic-gems-2-level-pack", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://assets.kongregate.com/gamez/0017/0262/live/Galactic_Gems_2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GiantsAndDwarvesTd-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GiantsAndDwarvesTd-v0", "human_url": "http://www.kongregate.com/games/LabuGames/giants-and-dwarves-td", "regions": null, "height": 500, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/9695/live/embeddable_169695.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Growbox-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Growbox-v0", "human_url": "http://armorgames.com/play/4613", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/growbox-4613.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombieTowerDefenseReborn-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ZombieTowerDefenseReborn-v0", "human_url": "http://publishers.spilgames.com/en/game/Zombie-Tower-Defense:-Reborn,576742227280285143", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/z/zombie_tower_defense/Zombie_tower_defense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VectorRunner-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.VectorRunner-v0", "human_url": "http://www.kongregate.com/games/DigYourOwnGrave/vector-runner", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://assets.kongregate.com/gamez/0001/9779/live/VectorRunner_KONG_secure.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PrincessToTheRescue-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.PrincessToTheRescue-v0", "human_url": "http://publishers.spilgames.com/en/game/Princess-to-the-Rescue,576742227280287927", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/Princess%20To%20Rescue.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BirdsFeeding-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BirdsFeeding-v0", "human_url": "http://www.gamesforwebsites.com/game/birds-feeding", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/93042/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UrbanFatburner-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.UrbanFatburner-v0", "human_url": "http://publishers.spilgames.com/en/game/Urban-Fatburner,576742227280284530", "regions": null, "height": 700, "width": 420, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/u/UrbanFatburner.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RabbitRustler-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.RabbitRustler-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/rabbitrustler/", "regions": null, "height": 450, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/rabbitrustler/rabbitrustler.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.MazeEye-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MazeEye-v0", "human_url": "http://www.games68.com/games.php?id=5000194", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570eafdee309f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WarOfTheShard-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WarOfTheShard-v0", "human_url": "http://www.kongregate.com/games/WakefieldStudios/war-of-the-shard", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0020/1620/live/embeddable_201620.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AnotherLife2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AnotherLife2-v0", "human_url": "http://www.games68.com/games.php?id=25202", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae256b03607.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Blosics2LevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Blosics2LevelPack-v0", "human_url": "http://notdoppler.com/blosics2levelpack.php", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/blosics2levelpack.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BulletFury-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BulletFury-v0", "human_url": "http://1000webgames.com/play-9982-Bullet-Fury.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/bulletfury.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TattooArtist-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TattooArtist-v0", "human_url": "http://armorgames.com/play/515", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/tattoo-artist-515.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClaustrophobiumFourStepsFromDeath-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ClaustrophobiumFourStepsFromDeath-v0", "human_url": "http://armorgames.com/play/15986", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/laustrophobium-in-fo-15986.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stand-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Stand-v0", "human_url": "http://www.games68.com/games.php?id=5000176", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c42c5b46.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stalingrad2-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.Stalingrad2-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/stalingrad-2", "regions": null, "height": 480, "width": 630, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/14961/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ToonEscapeSpookHouse-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ToonEscapeSpookHouse-v0", "human_url": "http://www.games68.com/games.php?id=25526", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8f38ee12.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AliceNixsAdventure-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.AliceNixsAdventure-v0", "human_url": "http://publishers.spilgames.com/en/game/Alice-&%20Nix%E2%80%99s%20Adventure,576742227280284532", "regions": null, "height": 600, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/a/AliceAndNix.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.1001ArabianNights-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.1001ArabianNights-v0", "human_url": "http://publishers.spilgames.com/en/game/1001-Arabian-Nights,576742227280287096", "regions": null, "height": 510, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/0/1001_arabian_nights/1001_arabian_nights.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchAndSpell-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchAndSpell-v0", "human_url": "http://www.gamesforwebsites.com/game/match-and-spell", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/135829/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpanishLiga2016-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpanishLiga2016-v0", "human_url": "http://www.games68.com/games.php?id=25265", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae26cc586b7.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubblePop-v0": {"readme_header": null, "categories": ["2 player", "action", "kids", "our", "1 player", "2 players", "2pg", "bubbles", "collecting", "free", "fun", "ghosts", "keyboard", "kids", "pop"], "rewarder": false, "autostart": false, "id": "flashgames.BubblePop-v0", "human_url": "http://old.2pg.com/game/bubble-pop/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/B/bubble_pop_2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl7-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JamesTheCircusZebra-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": true, "id": "flashgames.JamesTheCircusZebra-v0", "human_url": "http://armorgames.com/play/206/james-the-circus-zebra", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/james-the-circus-zeb-206.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.InsaneCircle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.InsaneCircle-v0", "human_url": "http://www.games68.com/games.php?id=25633", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb31a1dc1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl13-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl13-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BaldEagleJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BaldEagleJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/bald-eagle-jigsaw-puzzle", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17094/game.swf?1366132894", "enable_internet": false, "serve_from_domain": null}, "flashgames.EpicBattleFantasy4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EpicBattleFantasy4-v0", "human_url": "http://www.kongregate.com/games/kupo707/epic-battle-fantasy-4", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/7318/live/embeddable_167318.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Blosics2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Blosics2-v0", "human_url": "http://notdoppler.com/blosics2.php", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/blosics2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RubbleRacer-v0": {"readme_header": null, "categories": ["obstacles"], "rewarder": false, "autostart": false, "id": "flashgames.RubbleRacer-v0", "human_url": "http://www.andkon.com/arcade/obstacles/rubbleracer/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/obstacles/rubbleracer/rubbleracer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Helixteus-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Helixteus-v0", "human_url": "http://www.games68.com/games.php?id=5000094", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cef078ac69.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Phit-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Phit-v0", "human_url": "http://armorgames.com/play/21", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/phit-21.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Infinitix-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Infinitix-v0", "human_url": "http://armorgames.com/play/12818", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/infinitix-12818.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacer-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PaintWars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PaintWars-v0", "human_url": "http://www.games68.com/games.php?id=25393", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae3192c6579.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Free_to_use-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Free_to_use-v0", "human_url": "http://armorgames.com/play/12712", "regions": null, "height": "", "width": "", "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/rocket-santa-12712.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VanguardWars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.VanguardWars-v0", "human_url": "http://www.games68.com/games.php?id=25431", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae338f0a396.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DrinkBeerNeglectFamily-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DrinkBeerNeglectFamily-v0", "human_url": "http://www.gamesbutler.com/game/24301/drink-beer-neglect-family/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/4196a64b1b90ccc4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EatToWin-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EatToWin-v0", "human_url": "http://old.2pg.com/game/eat-to-win/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/eattowin.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OceanMatch-v0": {"readme_header": null, "categories": [], "rewarder": true, "autostart": false, "id": "flashgames.OceanMatch-v0", "human_url": "http://www.neongames.com/game/Ocean+Match", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/oceanmatch.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HoldTheFort-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HoldTheFort-v0", "human_url": "http://www.games68.com/games.php?id=25512", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8a1dde6b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlashsBounty-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FlashsBounty-v0", "human_url": "http://www.games68.com/games.php?id=5000075", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ceb5ff084c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RocketBootsInc-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RocketBootsInc-v0", "human_url": "http://www.gamesbutler.com/game/18363/rocket-boots-inc/", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/rocketbootmanv.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpeedBusters-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.SpeedBusters-v0", "human_url": "http://www.cartitans.com/game/speed-busters/", "regions": null, "height": 458, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/speed-busters.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NuttyBoom-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NuttyBoom-v0", "human_url": "http://publishers.spilgames.com/en/game/Nutty-Boom,576742227280285491", "regions": null, "height": 720, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/n/Nutty_boom/Nutty_Boom.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PuzzleRescuePrime-v0": {"readme_header": null, "categories": ["mouse only", "match 3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.PuzzleRescuePrime-v0", "human_url": "http://armorgames.com/play/15809", "regions": null, "height": 540, "width": 580, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/puzzle-rescue-prime-15809.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DigToChina-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DigToChina-v0", "human_url": "http://www.gamesbutler.com/game/19566/dig-to-china/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/DigToChina_mb.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ColorZapper-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ColorZapper-v0", "human_url": "http://1000webgames.com/play-8601-Color-Zapper.html", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/colorzapper.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ArmyPursuit-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ArmyPursuit-v0", "human_url": "http://www.cartitans.com/game/army-pursuit/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/army-pursuit.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EuroKicks2016-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EuroKicks2016-v0", "human_url": "http://www.games68.com/games.php?id=25488", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee7469e9d5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalacticGems2NewFrontiers-v0": {"readme_header": null, "categories": ["puzzle", "match 3", "mouse only"], "rewarder": true, "autostart": false, "id": "flashgames.GalacticGems2NewFrontiers-v0", "human_url": "http://www.kongregate.com/games/MikRad/galactic-gems-2-new-frontiers", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0020/8507/live/Galactic_Gems_2_NF.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RedBeard-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.RedBeard-v0", "human_url": "http://www.miniclip.com/games/red-beard/en/", "regions": null, "height": 366, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/red-beard/en/redbeard.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MusicSmash-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MusicSmash-v0", "human_url": "http://armorgames.com/play/1362", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/music-smash-1362.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StreetRace2Nitro-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.StreetRace2Nitro-v0", "human_url": "http://insanehero.com/mygames/StreetRace2/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/StreetRace2/streetrace2nitro.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Bike-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Bike-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TractorTrial-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TractorTrial-v0", "human_url": "http://1000webgames.com/play-8867-Tractor-Trial.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tractortrial.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GrappleCat-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.GrappleCat-v0", "human_url": "http://www.gamesforwebsites.com/game/grapple-cat", "regions": null, "height": 405, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129904/game.swf?1420731083", "enable_internet": false, "serve_from_domain": null}, "flashgames.BikeTrial2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BikeTrial2-v0", "human_url": "http://1000webgames.com/play-7937-Bike-Trial-2.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/biketrial2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheBravestHunter-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheBravestHunter-v0", "human_url": "http://www.kongregate.com/games/artlogicgames/the-bravest-hunter", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/7765/live/embeddable_177765.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GoKart3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.GoKart3d-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/go-kart-three-d", "regions": null, "height": 358, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/24482/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KartRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.KartRacing-v0", "human_url": "http://www.dailygames.com/games/kart-racing.html", "regions": null, "height": 369, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.dailygames.com/games/kart-racing.html", "enable_internet": false, "serve_from_domain": null}, "flashgames.TowerOfPisa-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TowerOfPisa-v0", "human_url": "http://www.gamesforwebsites.com/game/tower-of-pisa", "regions": null, "height": 650, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/138996/game.swf?1468934560", "enable_internet": false, "serve_from_domain": null}, "flashgames.Colorwars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Colorwars-v0", "human_url": "http://armorgames.com/play/105", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/colorwars-105.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FeedMeMoar-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FeedMeMoar-v0", "human_url": "http://www.kongregate.com/games/vinchkovsky/feed-me-moar", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/3713/live/embeddable_173713.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GemPop-v0": {"readme_header": null, "categories": ["1 player", "flash", "jewel", "free", "idnet", "bubble shooter", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.GemPop-v0", "human_url": "http://www.y8.com/games/gem_pop", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/110217/original/gem_pop.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl11-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl11-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Zombality-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Zombality-v0", "human_url": "http://www.miniclip.com/games/zombality/en/", "regions": null, "height": 420, "width": 853, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/zombality/en/Zombality.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GemstoneCastle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.GemstoneCastle-v0", "human_url": "http://www.gamesforwebsites.com/game/gemstone-castle", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/15051/game.swf?1320254437", "enable_internet": false, "serve_from_domain": null}, "flashgames.GhostClimb2Player-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GhostClimb2Player-v0", "human_url": "http://old.2pg.com/game/ghost-climb-2-player/play/", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/G/ghost_climb_2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dLaSupercars2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.3dLaSupercars2-v0", "human_url": "http://www.cartitans.com/game/3d-la-supercars-2/", "regions": null, "height": 330, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/upload/games/1909/8745189532.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Typeasaurus-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Typeasaurus-v0", "human_url": "http://armorgames.com/play/15880", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/typeasaurus-15880.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SantaMan-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SantaMan-v0", "human_url": "http://www.gamesforwebsites.com/game/santa-man", "regions": null, "height": 520, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/135194/game.swf?1449756797", "enable_internet": false, "serve_from_domain": null}, "flashgames.TapRocket-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TapRocket-v0", "human_url": "http://www.kongregate.com/games/jseldon/tap-rocket", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/2017/live/embeddable_192017.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WoollyBearJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WoollyBearJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/woolly-bear-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17180/game.swf?1304178547", "enable_internet": false, "serve_from_domain": null}, "flashgames.BraveHeads-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.BraveHeads-v0", "human_url": "http://www.y8.com/games/brave_heads", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/110579/original/brave_heads.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BoxRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.BoxRacers-v0", "human_url": "http://www.kongregate.com/games/FreeWorldGroup/box-racers", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn4.kongcdn.com/game_icons/0007/2205/box_100x75.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.Gloom-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Gloom-v0", "human_url": "http://www.games68.com/games.php?id=25247", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2666abee4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KingRolla-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.KingRolla-v0", "human_url": "http://www.gamesbutler.com/game/15330/king-rolla/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/kingRolla_GB.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.BikeTrial-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BikeTrial-v0", "human_url": "http://1000webgames.com/play-7775-Bike-Trial.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/bike-trial.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.4x4Monster3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.4x4Monster3-v0", "human_url": "http://1000webgames.com/play-8993-4x4-Monster-3.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/4x4monster3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SantaSituation-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SantaSituation-v0", "human_url": "http://www.gamesforwebsites.com/game/santasituation", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23896/game.swf?1357139442", "enable_internet": false, "serve_from_domain": null}, "flashgames.LilyFighters-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.LilyFighters-v0", "human_url": "http://www.gamesforwebsites.com/game/lily-fighters", "regions": null, "height": 320, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22901/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.XmasChains-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.XmasChains-v0", "human_url": "http://www.gamesbutler.com/game/18379/xmas-chains/", "regions": null, "height": 600, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/32363a60b3d47c8b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DotGrowth-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DotGrowth-v0", "human_url": "http://www.games68.com/games.php?id=5000168", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1bfa69b4c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FreecellDuplex-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FreecellDuplex-v0", "human_url": "http://www.games68.com/games.php?id=25258", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae26ac0f4f4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CoasterRacer3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-3", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/5738/live/embeddable_175738.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DragonVsMonster-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DragonVsMonster-v0", "human_url": "http://www.gamesforwebsites.com/game/dragon-vs-monster", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38294/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SlipSlideSloth-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SlipSlideSloth-v0", "human_url": "http://publishers.spilgames.com/en/game/Slip-Slide-Sloth,576742227280284594", "regions": null, "height": 700, "width": 510, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/s/slip_slide_sloth/slip_slide_sloth.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PicnicPanicTd-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.PicnicPanicTd-v0", "human_url": "http://armorgames.com/play/668", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/picnic-panic-td-668.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IceCreamFromSpace-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IceCreamFromSpace-v0", "human_url": "http://www.games68.com/games.php?id=25474", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae360b962eb.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LuckyBalls-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.LuckyBalls-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/lucky-balls", "regions": null, "height": 420, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/2750/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl6-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MysticIndiaPop-v0": {"readme_header": null, "categories": ["bubble shooter"], "rewarder": false, "autostart": false, "id": "flashgames.MysticIndiaPop-v0", "human_url": "http://publishers.spilgames.com/en/game/Mystic-India%20Pop,576742227280285107", "regions": null, "height": 634, "width": 870, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/m/mystic_india_pop/mystic_india_pop.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GemMania-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.GemMania-v0", "human_url": "http://www.gamesforwebsites.com/game/gem-mania", "regions": null, "height": 420, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/1103/game.swf?1438702680", "enable_internet": false, "serve_from_domain": null}, "flashgames.PiggysCupcakeQuest-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PiggysCupcakeQuest-v0", "human_url": "http://www.gamesforwebsites.com/game/piggys-cupcake-quest", "regions": null, "height": 700, "width": 900, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/87995/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterTruckFever-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.MonsterTruckFever-v0", "human_url": "http://www.kongregate.com/games/fightclub69/monster-truck-fever", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn3.kongcdn.com/game_icons/0048/3603/250x200.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriftRunners-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.DriftRunners-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/drift-runners", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0003/9130/live/DriftRunners.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl6-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BullfrogJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BullfrogJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/bullfrog-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17039/game.swf?1304178248", "enable_internet": false, "serve_from_domain": null}, "flashgames.PlopPlopLite-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PlopPlopLite-v0", "human_url": "http://armorgames.com/play/1641", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/plop-plop-lite-1641.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoffeeClicker-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CoffeeClicker-v0", "human_url": "http://www.games68.com/games.php?id=5000125", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf60f0b569.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DancingWithShadows-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DancingWithShadows-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/evil-minion", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15172/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OfficeTrap-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.OfficeTrap-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/officetrap/", "regions": null, "height": 550, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/officetrap/officetrap.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.TheProfessionals3-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.TheProfessionals3-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/fart-blaster", "regions": null, "height": 480, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15262/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EggzBlast-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": true, "id": "flashgames.EggzBlast-v0", "human_url": "http://www.arkadium.com/games/eggz-blast/", "regions": null, "height": 480, "width": 640, "extra_files": {"relative": {"files": ["data/locale/en-US/strings.xml", "data/leveldata/eggz_as3_leveldata-classic-ex.xml", "data/eggz_as3_config.xml", "data/skins/classic.swf", "data/external_sounds/classic/rollover.mp3", "data/external_sounds/classic/mainloop.mp3", "data/external_sounds/classic/newgamesound.mp3", "data/external_sounds/classic/wallbouncesound.mp3", "data/external_sounds/classic/tictac.mp3", "data/external_sounds/classic/rollersound.mp3", "data/external_sounds/classic/plateclicksound.mp3", "data/external_sounds/classic/nextlinesound.mp3", "data/external_sounds/classic/nextlevelsound.mp3", "data/external_sounds/classic/glass.mp3", "data/external_sounds/classic/gameoversound.mp3", "data/external_sounds/classic/firesound.mp3", "data/external_sounds/classic/bombsound.mp3", "data/external_sounds/classic/barnyardintro.mp3", "data/external_sounds/classic/timercountdown.mp3", "data/external_sounds/classic/startGame.mp3", "data/external_sounds/classic/gulp.mp3", "config.txt"], "base": "http://amsarkadium-a.akamaihd.net/assets/global/game/eggz-blast/c5e2b877-e177-4897-befd-c1d0e22405ff"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://amsarkadium-a.akamaihd.net/assets/global/game/eggz-blast/c5e2b877-e177-4897-befd-c1d0e22405ff/eggz-blast.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SonicBubbles-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SonicBubbles-v0", "human_url": "http://www.gamesforwebsites.com/game/sonicbubbles", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23816/game.swf?1355936182", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dSportRampage-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dSportRampage-v0", "human_url": "http://www.cartitans.com/game/3d-sport-rampage/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-sport-rampage.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFuture-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFuture-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GSwitch-v0": {"readme_header": null, "categories": ["obstacle", "noscore"], "rewarder": false, "autostart": false, "id": "flashgames.GSwitch-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/gswitch/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/gswitch/gswitch.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.SpinSoar-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpinSoar-v0", "human_url": "http://armorgames.com/play/1378", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/spin-soar-1378.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CowboyVsUfos-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CowboyVsUfos-v0", "human_url": "http://www.y8.com/games/cowboy_vs_ufos", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/90822/original/cowboy_vs_ufos.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DragonFortress-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DragonFortress-v0", "human_url": "http://www.games68.com/games.php?id=25618", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeadfc6c1a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TrollingLionJump-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TrollingLionJump-v0", "human_url": "http://www.games68.com/games.php?id=25316", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2ed17e913.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ShootTheCircle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ShootTheCircle-v0", "human_url": "http://www.games68.com/games.php?id=25451", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae34783256c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CatchTheStar-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CatchTheStar-v0", "human_url": "http://www.gamesbutler.com/game/3414/catch-the-star/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/catch_the_star.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FrozenImps-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FrozenImps-v0", "human_url": "http://www.gamesbutler.com/game/4348/frozen-imps/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/c045dc879131d648.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl10-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl10-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChockABox-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ChockABox-v0", "human_url": "http://www.gamesbutler.com/game/3602/chock-a-box/", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/chockABox.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SurfBuggy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SurfBuggy-v0", "human_url": "http://www.gamesbutler.com/game/12131/surf-buggy/", "regions": null, "height": 550, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/f84a91d791158368.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Scribble-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Scribble-v0", "human_url": "http://armorgames.com/play/51", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/scribble-51.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HungerHunter-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.HungerHunter-v0", "human_url": "http://www.gamesforwebsites.com/game/hunger-hunter", "regions": null, "height": 550, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130016/game.swf?1421838711", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpaceMadness-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpaceMadness-v0", "human_url": "http://armorgames.com/play/6156", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/space-madness-6156.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CopperheadJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CopperheadJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/copperhead-jigsaw-puzzle", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17063/game.swf?1304178301", "enable_internet": false, "serve_from_domain": null}, "flashgames.CrystalCurse-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.CrystalCurse-v0", "human_url": "http://publishers.spilgames.com/en/game/Crystal-Curse,576742227280295053", "regions": null, "height": 576, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/CrystalCurseSpilgamesv7.swf?gp=1", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Bimmin2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Bimmin2-v0", "human_url": "http://publishers.spilgames.com/en/game/Bimmin-2,576742227280285141", "regions": null, "height": 640, "width": 352, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/bimmin_2/Bimmin2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MidnightCanine-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.MidnightCanine-v0", "human_url": "http://publishers.spilgames.com/en/game/Midnight-Canine,576742227280284355", "regions": null, "height": 600, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/m/MidnightCanine.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EasterBunnyCollectCarrots-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.EasterBunnyCollectCarrots-v0", "human_url": "http://www.gamesforwebsites.com/game/easter-bunny-collect-carrots", "regions": null, "height": 520, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/136995/game.swf?1458743075", "enable_internet": false, "serve_from_domain": null}, "flashgames.EffingWorms-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EffingWorms-v0", "human_url": "http://www.kongregate.com/games/EffingGames/effing-worms", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0008/5093/live/embeddable_85093.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl5-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Dodge-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Dodge-v0", "human_url": "http://armorgames.com/play/2963", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/dodge-2963.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Filler2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Filler2-v0", "human_url": "http://www.kongregate.com/games/SimianLogic/filler-2", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0004/5215/live/filler2_kong.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Rbots-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Rbots-v0", "human_url": "http://publishers.spilgames.com/en/game/Rbots,576742227280287400", "regions": null, "height": 630, "width": 420, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/r/Rbots/RBots.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlacksmithLab-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlacksmithLab-v0", "human_url": "http://www.gamesbutler.com/game/25789/blacksmith-lab/", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/blacksmithlab.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeroesOfMangaraTheFrostCrown-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HeroesOfMangaraTheFrostCrown-v0", "human_url": "http://www.games68.com/games.php?id=5000122", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf4eb958c7.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Crane-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Crane-v0", "human_url": "http://publishers.spilgames.com/en/game/Crane,576742227280283740", "regions": null, "height": 720, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/c/Crane/Crane.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cloud9-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Cloud9-v0", "human_url": "http://publishers.spilgames.com/en/game/Cloud-9,576742227280284453", "regions": null, "height": 320, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/c/cloud9/Cloud9_Girl.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LaserCannon3LevelsPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LaserCannon3LevelsPack-v0", "human_url": "http://www.games68.com/games.php?id=5000105", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ceffa6050e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DoodleGod2Walkthrough-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DoodleGod2Walkthrough-v0", "human_url": "http://www.kongregate.com/games/Badim/doodle-god-2-walkthrough", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/2258/live/embeddable_112258.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmileyShowdown-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SmileyShowdown-v0", "human_url": "http://www.gamesforwebsites.com/game/smiley-showdown-2", "regions": null, "height": 500, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24529/game.swf?1380293746", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NinjaTrainingWorlds-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.NinjaTrainingWorlds-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/parkour", "regions": null, "height": 600, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/24373/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PoliceHotRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.PoliceHotRacing-v0", "human_url": "http://www.willinggames.com/racing-games/Police-Hot-Racing.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/flash10/Police-Hot-Racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ArmySpeeder-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ArmySpeeder-v0", "human_url": "http://www.cartitans.com/game/army-speeder/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/army-speeder.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LevelEditor3-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.LevelEditor3-v0", "human_url": "http://publishers.spilgames.com/en/game/Level-Editor-3,576742227280290903", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/10/1432719159_level-editor-3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpectrumRunner-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.SpectrumRunner-v0", "human_url": "http://www.kongregate.com/games/DigYourOwnGrave/spectrum-runner", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0011/6439/live/SpectrumRunner_secure.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BulletHeaven-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BulletHeaven-v0", "human_url": "http://www.kongregate.com/games/kupo707/bullet-heaven", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0010/8868/live/embeddable_108868.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BloodyMonstersPack2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BloodyMonstersPack2-v0", "human_url": "http://www.games68.com/games.php?id=5000145", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cfb6966daf.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WhatsInsideTheBox-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WhatsInsideTheBox-v0", "human_url": "http://www.games68.com/games.php?id=25523", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8da97daa.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PumpkinMan-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PumpkinMan-v0", "human_url": "http://www.gamesforwebsites.com/game/pumpkin-man", "regions": null, "height": 520, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/134070/game.swf?1445956649", "enable_internet": false, "serve_from_domain": null}, "flashgames.Dots-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Dots-v0", "human_url": "http://notdoppler.com/dots.php", "regions": null, "height": 400, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/dots.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriveToWreck2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DriveToWreck2-v0", "human_url": "http://1000webgames.com/play-9442-Drive-To-Wreck-2.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/drivetowreck2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CursedTreasureDontTouchMyGems-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CursedTreasureDontTouchMyGems-v0", "human_url": "http://notdoppler.com/cursedtreasure-donttouchmygems.php", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/cursedtreasure-donttouchmygems.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl13-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl13-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CastleRush-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CastleRush-v0", "human_url": "http://www.games68.com/games.php?id=5000174", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c314df29.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Moosters-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.Moosters-v0", "human_url": "http://publishers.spilgames.com/en/game/Moosters,576742227280292396", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/m/Moosters/moosters.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HowDareYou-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HowDareYou-v0", "human_url": "http://www.kongregate.com/games/Geovizz/how-dare-you", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0020/3585/live/embeddable_203585.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GolfRun-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.GolfRun-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/golf-run", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3317/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PearlBreaking-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PearlBreaking-v0", "human_url": "http://www.gamesforwebsites.com/game/pearl-breaking", "regions": null, "height": 500, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23609/game.swf?1354643738", "enable_internet": false, "serve_from_domain": null}, "flashgames.Lazerman-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Lazerman-v0", "human_url": "http://publishers.spilgames.com/en/game/Lazerman,576742227280288022", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/l/Lazerman/lazerman.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.V8MuscleCars-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.V8MuscleCars-v0", "human_url": "http://insanehero.com/mygames/V8MuscleCars/index.html", "regions": null, "height": 270, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/V8MuscleCars/musclecars.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheSilentPlanet-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheSilentPlanet-v0", "human_url": "http://www.games68.com/games.php?id=5000608", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570f58ef42eb7.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AeroDefense-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.AeroDefense-v0", "human_url": "http://www.gamesforwebsites.com/game/aero-defense", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137425/game.swf?1460736899", "enable_internet": false, "serve_from_domain": null}, "flashgames.Resonance-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Resonance-v0", "human_url": "http://publishers.spilgames.com/en/game/Resonance,576742227280287219", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/r/Resonance/Resonance.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleHitPonyParade-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleHitPonyParade-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Hit:-Pony-Parade,576742227280283866", "regions": [{"coordinates": [493, 165, 220, 342], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_hit_pony_parade/Bubble_Hit_Pony_Parade.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DeathCabin-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DeathCabin-v0", "human_url": "http://publishers.spilgames.com/en/game/Death-Cabin,576742227280295342", "regions": null, "height": 0, "width": 0, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/DeathCabin.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DeadHungry2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DeadHungry2-v0", "human_url": "http://www.games68.com/games.php?id=5000164", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1bead8df5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DirkValentine-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DirkValentine-v0", "human_url": "http://www.nitrome.com/games/dirkvalentine/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.nitrome.com/games/dirkvalentine/dirkvalentine.swf?v=10.0.0.0", "enable_internet": false, "serve_from_domain": null}, "flashgames.TurboCrew-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.TurboCrew-v0", "human_url": "http://www.cartitans.com/game/turbo-crew/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/turbo-crew.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GardenRush-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GardenRush-v0", "human_url": "http://www.games68.com/games.php?id=5000170", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c04ad079.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpectrumHeist-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpectrumHeist-v0", "human_url": "http://armorgames.com/play/13386", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/spectrum-heist-13386.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmigoPancho3SheriffSancho-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AmigoPancho3SheriffSancho-v0", "human_url": "http://publishers.spilgames.com/en/game/Amigo-Pancho-3:-Sheriff-Sancho,576742227280287710", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/a/Amigo_pancho_3/AmigoPancho3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlackForce-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlackForce-v0", "human_url": "http://publishers.spilgames.com/en/game/Black-Force,576742227280284173", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/BlackForce/BlackForce.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LineGameLimeEdition-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LineGameLimeEdition-v0", "human_url": "http://notdoppler.com/linegame-limeedition.php", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/linegame-limeedition.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DragonChain-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.DragonChain-v0", "human_url": "http://publishers.spilgames.com/en/game/Dragon-Chain,576742227280289216", "regions": null, "height": 510, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/d/Dragon-Chain/Dragon_Chain_v1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CardinalQuest2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CardinalQuest2-v0", "human_url": "http://www.kongregate.com/games/randomnine/cardinal-quest-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9258/live/embeddable_179258.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriftRunners2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.DriftRunners2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/drift-runners-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0007/6628/live/embeddable_76628.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeySummer-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": true, "autostart": false, "id": "flashgames.HeySummer-v0", "human_url": "http://publishers.spilgames.com/en/game/Hey-Summer,576742227280292511", "regions": [{"coordinates": [601, 54, 84, 47], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/h/Hey%20Summer/hey_summer_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NightDrivin-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NightDrivin-v0", "human_url": "http://www.games68.com/games.php?id=25264", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae26c9186c8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LooneyAndJohny-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.LooneyAndJohny-v0", "human_url": "http://www.gamesforwebsites.com/game/looney-and-johny", "regions": null, "height": 475, "width": 775, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38730/game.swf", "enable_internet": false, "serve_from_domain": null}, "internet.SlitherIO-v0": {"categories": ["mmo"], "height": 300, "width": 502, "server_timestep_limit": null, "tags": {}, "id": "internet.SlitherIO-v0", "rewarder": true, "url": "http://slither.io/", "enable_internet": true, "autostart": true}, "flashgames.VirtualRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.VirtualRacer-v0", "human_url": "http://www.cartitans.com/game/virtual-racer/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/virtual-racer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonkeyManic-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MonkeyManic-v0", "human_url": null, "regions": null, "height": 384, "width": 384, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/monkeymanic/monkeymanic.swf", "enable_internet": false, "serve_from_domain": ["andkon.com"]}, "flashgames.BulletHeaven2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BulletHeaven2-v0", "human_url": "http://www.kongregate.com/games/kupo707/bullet-heaven-2", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0024/7191/live/embeddable_247191.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Helicrane-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Helicrane-v0", "human_url": "http://www.gamesforwebsites.com/game/helicrane", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130677/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CrapImBroke-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CrapImBroke-v0", "human_url": "http://www.gamesbutler.com/game/23248/crap!-i'm-broke/", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/6a5b4c04d29c6efd.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Pointer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Pointer-v0", "human_url": "http://notdoppler.com/pointer.php", "regions": null, "height": 420, "width": 575, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/pointer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DragonFunflap-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DragonFunflap-v0", "human_url": "http://www.gamesforwebsites.com/game/dragon-funflap", "regions": null, "height": 575, "width": 768, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/37961/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Skytrip-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Skytrip-v0", "human_url": "http://www.games68.com/games.php?id=25366", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae30af7d6be.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperAdventurePalsBattleArena-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperAdventurePalsBattleArena-v0", "human_url": "http://www.games68.com/games.php?id=25174", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2509ad6c5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CurveFever-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CurveFever-v0", "human_url": "http://www.games68.com/games.php?id=2126575", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/511136fc20ee0.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperK9-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SuperK9-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/super-k9", "regions": null, "height": 400, "width": 600, "extra_files": {"relative": {"files": ["level1.xml", "level2.xml", "level3.xml", "level4.xml"], "base": "http://www.freegamesforyourwebsite.com/game/super-k9"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/12345/game.swf", "enable_internet": false, "serve_from_domain": "freeonlinegames.com"}, "flashgames.Sieger2LevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Sieger2LevelPack-v0", "human_url": "http://www.games68.com/games.php?id=25528", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8fb7ea9f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IdleFarmer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IdleFarmer-v0", "human_url": "http://www.games68.com/games.php?id=5000140", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf951c5959.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterCars2Contact-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.CoasterCars2Contact-v0", "human_url": "http://www.willinggames.com/racing-games/Coaster-Cars-2-contact.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash4/Coaster-Cars-2-contact.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stalingrad-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.Stalingrad-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/stalingrad", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/14569/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NadiasRage-v0": {"readme_header": null, "categories": ["2 player", "action", "adventure", "our", "1 player", "2 players", "2pg", "action", "blood", "fast", "flash", "free", "highscore", "multiplayer", "pixelated", "runner", "running", "survive", "zombies"], "rewarder": true, "autostart": false, "id": "flashgames.NadiasRage-v0", "human_url": "http://old.2pg.com/game/nadias-rage/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/nadias-rage.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterTruckRally-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.MonsterTruckRally-v0", "human_url": "http://insanehero.com/mygames/MonsterTruckRally/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/MonsterTruckRally/monstertruckrally.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CloseCombat-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.CloseCombat-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/close-combat", "regions": null, "height": 433, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/10195/game.swf", "enable_internet": false, "serve_from_domain": "freeonlinegames.com"}, "flashgames.HeroSimulator-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HeroSimulator-v0", "human_url": "http://www.games68.com/games.php?id=5000033", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cdfe5518e2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IceSlide-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.IceSlide-v0", "human_url": "http://www.gamesforwebsites.com/game/ice-slide", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23644/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VideoGameMonster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.VideoGameMonster-v0", "human_url": "http://publishers.spilgames.com/en/game/Video-Game-Monster,576742227280295519", "regions": null, "height": 720, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/VideoGameMonstersV6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Blosics3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Blosics3-v0", "human_url": "http://notdoppler.com/blosics3.php", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/blosics3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dSpeedFever-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dSpeedFever-v0", "human_url": "http://www.cartitans.com/game/3d-speed-fever/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-speed-fever.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ItsDarkInHell-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ItsDarkInHell-v0", "human_url": "http://publishers.spilgames.com/en/game/It%E2%80%99s-Dark-in-Hell,576742227280284687", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/i/ItsDarkInHell/its_dark_in_hell.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FrozenIslandsNewHorizons-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FrozenIslandsNewHorizons-v0", "human_url": "http://www.games68.com/games.php?id=5000025", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c2227e1961.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmigoPanchoInAfghanistan-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AmigoPanchoInAfghanistan-v0", "human_url": "http://www.games68.com/games.php?id=25424", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae332255b8a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GlobalRallyRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.GlobalRallyRacer-v0", "human_url": "http://turbonuke.com/games.php?game=globalrallyracer", "regions": null, "height": 377, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://turbonuke.com/flashgames/turborally.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Firebug-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Firebug-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/firebug/", "regions": null, "height": 540, "width": 660, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/firebug/firebug.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.Colorfill-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Colorfill-v0", "human_url": "http://armorgames.com/play/1632", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/colorfill-1632.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlockysEscape-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlockysEscape-v0", "human_url": "http://www.games68.com/games.php?id=5000023", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c1f688b844.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MushyMishy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MushyMishy-v0", "human_url": "http://www.games68.com/games.php?id=25642", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb58a34c8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ModelCarRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ModelCarRacing-v0", "human_url": "http://www.y8.com/games/model_car_racing", "regions": null, "height": 343, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/72088/original/model_car_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MadpetSkateboarder2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MadpetSkateboarder2-v0", "human_url": "http://www.gamesbutler.com/game/8197/madpet-skateboarder-2/", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/808254660c6dd73d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Madburger3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Madburger3-v0", "human_url": "http://www.games68.com/games.php?id=5000070", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cea906b079.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchTheFruits-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchTheFruits-v0", "human_url": "http://www.gamesforwebsites.com/game/match-the-fruits", "regions": null, "height": 495, "width": 660, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130460/game.swf?1425384870", "enable_internet": false, "serve_from_domain": null}, "flashgames.MarblesShooter-v0": {"readme_header": null, "categories": ["1 player", "flash", "shooting", "ball", "girl", "matching", "free", "match 3", "idnet", "bubble shooter", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.MarblesShooter-v0", "human_url": "http://www.y8.com/games/marbles_shooter", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/106816/original/marbles_shooter.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NoughtsAndCrossesExtreme-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NoughtsAndCrossesExtreme-v0", "human_url": "http://old.2pg.com/game/noughts-and-crosses-extreme/play/", "regions": null, "height": 480, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/N/noughts-and-crosses-extreme.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl9-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl9-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MarsColonyTd-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MarsColonyTd-v0", "human_url": "http://1000webgames.com/play-9852-Mars-Colony-TD.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/mars-colony-td.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacemanMax-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpacemanMax-v0", "human_url": "http://publishers.spilgames.com/en/game/Spaceman-Max,576742227280285516", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/Spaceman_max/spaceman_max.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KitchenRestaurantCleanUp-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.KitchenRestaurantCleanUp-v0", "human_url": "http://www.games68.com/games.php?id=25412", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae32a818de9.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DoubleEdged-v0": {"readme_header": null, "categories": ["fighting", "noscore"], "rewarder": false, "autostart": false, "id": "flashgames.DoubleEdged-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/doubleedged/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/doubleedged/doubleedged.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.DontPanic-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DontPanic-v0", "human_url": "http://armorgames.com/play/11366", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/dont-panic-11366.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl9-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl9-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MysteriousPirateJewels-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.MysteriousPirateJewels-v0", "human_url": "http://publishers.spilgames.com/en/game/Mysterious-Pirate-Jewels,576742227280291805", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://games.cdn.spilcloud.com/container_df9aa4793b5/1398853638_mysteriouspiratejewels.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DetectiveConrad-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DetectiveConrad-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/pointless", "regions": null, "height": 400, "width": 360, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3145/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AnimeClicker2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AnimeClicker2-v0", "human_url": "http://www.games68.com/games.php?id=5000053", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce7dd8ff69.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Ditloid-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Ditloid-v0", "human_url": "http://www.gamesbutler.com/game/4070/ditloid/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/0b48701f0bcc080a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WishTotems-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WishTotems-v0", "human_url": "http://www.gamesbutler.com/game/11540/wish-totems/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/062b2d8ab1ab511a.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpaceColony-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.SpaceColony-v0", "human_url": "http://www.cartitans.com/game/space-colony/", "regions": null, "height": 458, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/space-colony.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stargrazer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Stargrazer-v0", "human_url": "http://armorgames.com/play/14095", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/stargrazer-14095.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Colordefense-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Colordefense-v0", "human_url": "http://armorgames.com/play/106", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/colordefense-106.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.21Balloons-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.21Balloons-v0", "human_url": "http://www.gamesforwebsites.com/game/21-balloons", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38435/game.swf?1372772447", "enable_internet": false, "serve_from_domain": null}, "flashgames.Clusterobot-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Clusterobot-v0", "human_url": "http://armorgames.com/play/1513", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/clusterobot-1513.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AspenSecret-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AspenSecret-v0", "human_url": "http://www.games68.com/games.php?id=25644", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb5f070a0.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl8-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cooliodj-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Cooliodj-v0", "human_url": "http://armorgames.com/play/103", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/cooliodj-103.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Nook-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Nook-v0", "human_url": "http://armorgames.com/play/6376", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/nook-6376.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Legor9-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Legor9-v0", "human_url": "http://www.games68.com/games.php?id=25591", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea61c62ac.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Commando-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Commando-v0", "human_url": "http://www.miniclip.com/games/commando/en/", "regions": null, "height": 300, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/commando/en/commando.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MotherLoad-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MotherLoad-v0", "human_url": null, "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0004/6879/live/motherloaddemo.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CastleSolitaire-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CastleSolitaire-v0", "human_url": "http://publishers.spilgames.com/en/game/Castle-Solitaire,576742227280283526", "regions": null, "height": 700, "width": 510, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/c/castle_solitaire/castle_solitaire.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WackyStrike-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WackyStrike-v0", "human_url": "http://www.games68.com/games.php?id=25426", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae33511b43d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Gameinit-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Gameinit-v0", "human_url": "http://www.gamesbutler.com/game/24358/game-init/", "regions": null, "height": 480, "width": 704, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/game_init_060415.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl7-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterCars2Megacross-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.CoasterCars2Megacross-v0", "human_url": "http://www.willinggames.com/racing-games/Coaster-Cars-2-megacross.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash4/Coaster-Cars-2-megacross.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlappyPanda-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlappyPanda-v0", "human_url": "http://www.gamesforwebsites.com/game/flappy-panda", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130999/game.swf?1429190148", "enable_internet": false, "serve_from_domain": null}, "flashgames.MissionEscapeTheDojo-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MissionEscapeTheDojo-v0", "human_url": "http://www.games68.com/games.php?id=25540", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee93752380.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClimbOrDrown2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ClimbOrDrown2-v0", "human_url": "http://www.gamesbutler.com/game/18063/climb-or-drown-2/", "regions": null, "height": 500, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/climbordrown2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpaceBounty-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SpaceBounty-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/space-bounty", "regions": null, "height": 580, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15023/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PlaneRace2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PlaneRace2-v0", "human_url": "http://1000webgames.com/play-9947-Plane-Race-2.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/planerace2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JollySwipeLevelPack-v0": {"readme_header": null, "categories": ["1 player", "flash", "shooting", "matching", "fun", "funny", "free", "idnet", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.JollySwipeLevelPack-v0", "human_url": "http://www.y8.com/games/jolly_swipe_level_pack", "regions": null, "height": 640, "width": 360, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/110216/original/jolly_swipe_lp.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.V8MuscleCars2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.V8MuscleCars2-v0", "human_url": "http://insanehero.com/mygames/V8MuscleCars2/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/V8MuscleCars2/v8musclecars2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmericanRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.AmericanRacing-v0", "human_url": "http://www.kongregate.com/games/turboNuke/american-racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0015/6925/live/AmericanRacing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BirdSlice-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BirdSlice-v0", "human_url": "http://www.gamesforwebsites.com/game/bird-slice", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/128707/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TutiFruti-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TutiFruti-v0", "human_url": "http://www.gamesforwebsites.com/game/tuti-fruti", "regions": null, "height": 600, "width": 710, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39604/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AirWar1941-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.AirWar1941-v0", "human_url": "http://www.gamesforwebsites.com/game/air-war-1941", "regions": null, "height": 600, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130205/game.swf?1423063288", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlashRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FlashRacer-v0", "human_url": "http://armorgames.com/play/1321", "regions": null, "height": 600, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/flash-racer-1321.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dFuriousDriver-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dFuriousDriver-v0", "human_url": "http://www.cartitans.com/game/3d-furious-driver/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-furious-driver.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl5-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SkyKnight2-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SkyKnight2-v0", "human_url": "http://www.gamesforwebsites.com/game/sky-knight-2", "regions": null, "height": 570, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/37789/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Pel-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Pel-v0", "human_url": "http://armorgames.com/play/2011", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pel-2011.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmileyJumpFest-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": true, "autostart": false, "id": "flashgames.SmileyJumpFest-v0", "human_url": "http://www.smileygamer.com/ourgames/smileyjumpfest.html", "regions": null, "height": 400, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/SmileyJumpFest.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterLabFeedThemAll-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MonsterLabFeedThemAll-v0", "human_url": "http://www.gamesbutler.com/game/25201/monster-lab:-feed-them-all/", "regions": null, "height": 600, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/MonsterLab_GamesButler2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StreetRace3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.StreetRace3-v0", "human_url": "http://insanehero.com/mygames/StreetRace3/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/StreetRace3/streetrace3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmileyPuzzle2-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": true, "autostart": false, "id": "flashgames.SmileyPuzzle2-v0", "human_url": "http://www.kongregate.com/games/SmileyGamer/smiley-puzzle-2", "regions": null, "height": 540, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0013/0459/live/smileypuzzle2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JungleCrash-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.JungleCrash-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/jungle-crash", "regions": [{"coordinates": [475, 57, 535, 30], "type": "noclick"}], "height": 500, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/2883/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Mrbirdie-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Mrbirdie-v0", "human_url": "http://www.gamesforwebsites.com/game/mrbirdie", "regions": null, "height": 640, "width": 820, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129975/game.swf?1421666755", "enable_internet": false, "serve_from_domain": null}, "flashgames.Gluey2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Gluey2-v0", "human_url": "http://notdoppler.com/gluey2.php", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/gluey2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FoxSnakeJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FoxSnakeJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/fox-snake-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17024/game.swf?1304178365", "enable_internet": false, "serve_from_domain": null}, "flashgames.PaperDefense-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.PaperDefense-v0", "human_url": "http://publishers.spilgames.com/en/game/Paper-Defense,576742227280284997", "regions": null, "height": 420, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/PaperDefense/PaperDefense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dMuscleCarRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dMuscleCarRacer-v0", "human_url": "http://www.cartitans.com/game/3d-muscle-car-racer/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-muscle-car-racer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FrogEatFlies-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FrogEatFlies-v0", "human_url": "http://www.gamesforwebsites.com/game/frog-eat-flies", "regions": null, "height": 568, "width": 528, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/138140/game.swf?1464269993", "enable_internet": false, "serve_from_domain": null}, "flashgames.NewSiberianSupercarsRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.NewSiberianSupercarsRacing-v0", "human_url": "http://www.willinggames.com/racing-games/New-Siberian-SuperCars-Racing.html", "regions": null, "height": 375, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/d/file/20130910/e178ce75aee29e77358ac3f3421b257d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl7-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Gemcraft-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.Gemcraft-v0", "human_url": "http://armorgames.com/play/1716", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/gemcraft-1716.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PirateRunAway-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PirateRunAway-v0", "human_url": "http://www.gamesforwebsites.com/game/pirate-run-away", "regions": null, "height": 480, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133133/game.swf?1441621081", "enable_internet": false, "serve_from_domain": null}, "flashgames.ReleaseTheMooks3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ReleaseTheMooks3-v0", "human_url": "http://www.gamesbutler.com/game/11238/release-the-mooks-3/", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/2cafb3d8a428cec8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CatGodVsSunKing2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CatGodVsSunKing2-v0", "human_url": "http://www.kongregate.com/games/nerdook/cat-god-vs-sun-king-2", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/8603/live/embeddable_168603.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Deliveryman-v0": {"readme_header": null, "categories": ["keyboard only", "old school", "puzzle", "pixel", "platform"], "rewarder": false, "autostart": false, "id": "flashgames.Deliveryman-v0", "human_url": "http://armorgames.com/play/7032", "regions": null, "height": "", "width": "", "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/deliveryman-7032.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Bullets-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Bullets-v0", "human_url": "http://armorgames.com/play/1526", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/bullets-1526.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl4-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl4-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CrystalStoryIi-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CrystalStoryIi-v0", "human_url": "http://www.kongregate.com/games/lan14n/crystal-story-ii", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/0066/live/embeddable_190066.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoastRunners-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.CoastRunners-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coast-runners", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/6635/live/CoastRunners.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DragonChronicles-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DragonChronicles-v0", "human_url": "http://publishers.spilgames.com/en/game/Dragon-Chronicles,576742227280284419", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/d/dragon_chronicles/DragonChronicle.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BackHome-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BackHome-v0", "human_url": "http://www.gamesbutler.com/game/4345/back-home/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/c0b6560e524b2321.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GunpowderAndFeathers-v0": {"readme_header": null, "categories": ["puzzle", "sparsereward"], "rewarder": false, "autostart": false, "id": "flashgames.GunpowderAndFeathers-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/gunpowderandfeathers/", "regions": null, "height": 525, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/gunpowderandfeathers/gunpowderandfeathers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KangoIslands-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.KangoIslands-v0", "human_url": "http://www.games68.com/games.php?id=25374", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae30ea46ab6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Blix-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Blix-v0", "human_url": "http://www.y8.com/games/blix", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/121639/original/blix.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IslandDefense-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.IslandDefense-v0", "human_url": "http://www.y8.com/games/island_defense", "regions": null, "height": 800, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/110358/original/island_defense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Pointless-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Pointless-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/pointless", "regions": null, "height": 400, "width": 360, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3145/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HoleInOne-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HoleInOne-v0", "human_url": "http://www.games68.com/games.php?id=25663", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eebc4281b2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperBomb-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperBomb-v0", "human_url": "http://www.games68.com/games.php?id=25232", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2619f23d9.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.30Seconds-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.30Seconds-v0", "human_url": "http://www.gamesbutler.com/game/2406/30-seconds/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/30seconds.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmileyPuzzleGirlEdition-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": false, "autostart": false, "id": "flashgames.SmileyPuzzleGirlEdition-v0", "human_url": "http://www.smileygamer.com/ourgames/spge.html", "regions": null, "height": 400, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/SmileyPuzzleGirlEdition.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DuskDrive-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.DuskDrive-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/dusk-drive", "regions": null, "height": 512, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0022/3733/live/embeddable_223733.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Ics2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Ics2-v0", "human_url": "http://publishers.spilgames.com/en/game/ICS-2,576742227280298418", "regions": null, "height": 720, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/df9aa4793b5/ICSII-12-01/ICSII.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperBattleCity2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperBattleCity2-v0", "human_url": "http://www.games68.com/games.php?id=25168", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae24e8d8e16.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BugsGotGuns-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BugsGotGuns-v0", "human_url": "http://old.2pg.com/game/bugs-got-guns/play/", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/Bugs-Got-Guns.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DungeonBlocks-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DungeonBlocks-v0", "human_url": "http://www.gamesforwebsites.com/game/dungeon-blocks", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/122005/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ShortCircuit-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ShortCircuit-v0", "human_url": "http://armorgames.com/play/1369", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/short-circuit-1369.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl8-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl16-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl16-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BalloonGods-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BalloonGods-v0", "human_url": "http://old.2pg.com/game/balloon-gods/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/B/balloon-gods.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PinBalls-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.PinBalls-v0", "human_url": "http://publishers.spilgames.com/en/game/Pin-Balls,576742227280283910", "regions": [{"coordinates": [95, 173, 105, 32], "type": "noclick"}], "height": 600, "width": 730, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/pinballs/pin_balls_family.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterCarsCJackTrack-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.CoasterCarsCJackTrack-v0", "human_url": "http://www.willinggames.com/racing-games/Coaster-Cars-C-Jack-track.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash4/Coaster-Cars-C-Jack-track.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleBlubbs-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleBlubbs-v0", "human_url": "http://armorgames.com/play/272", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/bubble-blubbs-272.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FitItQuick-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FitItQuick-v0", "human_url": "http://www.gamesforwebsites.com/game/fit-it-quick", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesforwebsites.com/game/fit-it-quick", "enable_internet": false, "serve_from_domain": null}, "flashgames.ParticleWarsExtreme-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ParticleWarsExtreme-v0", "human_url": "http://armorgames.com/play/10915", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/particle-wars-extrem-10915.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DiamondCrashMania-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DiamondCrashMania-v0", "human_url": "http://www.gamesforwebsites.com/game/diamond-crash-mania", "regions": null, "height": 640, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130174/game.swf?1422975934", "enable_internet": false, "serve_from_domain": null}, "flashgames.Hotspot-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Hotspot-v0", "human_url": "http://notdoppler.com/hotspot.php", "regions": null, "height": 470, "width": 570, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/hotspot.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RiseOfChampions-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RiseOfChampions-v0", "human_url": "http://www.games68.com/games.php?id=5000099", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cef7cbbd9b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FairyDefense-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.FairyDefense-v0", "human_url": "http://publishers.spilgames.com/en/game/Fairy-Defense,576742227280289553", "regions": null, "height": 600, "width": 800, "extra_files": {"absolute": ["http://api.configar.org/cf/pb/1/settings/0/0/bb131084ad822fdda12a4298ff20a460"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/a10/fairydefense/FairyDefense.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stalingrad3-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.Stalingrad3-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/stalingrad-3", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15082/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlashBombs-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.FlashBombs-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/flash-bombs", "regions": null, "height": 440, "width": 728, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/14898/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PigDestroyer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PigDestroyer-v0", "human_url": "http://www.games68.com/games.php?id=25242", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae263c6da8b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl14-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl14-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PrincessBubblesRescuePrince-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.PrincessBubblesRescuePrince-v0", "human_url": "http://publishers.spilgames.com/en/game/Princess-Bubbles:-Rescue-Prince,576742227280288544", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/Princess_Bubbles.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NinjaPandaArena-v0": {"readme_header": null, "categories": ["2 player", "arcade", "our", "1 player", "2 players", "2pg", "arcade", "collecting", "flash", "free", "fruits", "keyboard", "ninja", "ninjas", "pandas", "survive"], "rewarder": false, "autostart": false, "id": "flashgames.NinjaPandaArena-v0", "human_url": "http://old.2pg.com/game/ninja-panda-arena/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/N/ninja-panda-arena.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PingPongSurvival-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PingPongSurvival-v0", "human_url": "http://www.gamesforwebsites.com/game/ping-pong-survival", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133266/game.swf?1442326056", "enable_internet": false, "serve_from_domain": null}, "flashgames.BalloonsPop-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BalloonsPop-v0", "human_url": "http://www.games68.com/games.php?id=25515", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8b819e87.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonkeyBlast-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MonkeyBlast-v0", "human_url": "http://www.gamesforwebsites.com/game/monkey-blast", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/122780/game.swf?1401789576", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombiesMustDie-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ZombiesMustDie-v0", "human_url": "http://publishers.spilgames.com/en/game/Zombies-Must-Die!,576742227280283826", "regions": null, "height": 700, "width": 420, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/z/ZombiesMustDie/zombie_localizing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriftRunners3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.DriftRunners3d-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/drift-runners-3d", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0015/2352/live/DriftRunners3D.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnailBob4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SnailBob4-v0", "human_url": "http://publishers.spilgames.com/en/game/Snail-Bob-4,576742227280288106", "regions": null, "height": 640, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/SnailBob4/SnailBobSpace.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Funkostroll-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Funkostroll-v0", "human_url": "http://armorgames.com/play/6592", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/funk-o-stroll-6592.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Pyro-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Pyro-v0", "human_url": "http://armorgames.com/play/3174", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pyro-3174.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Foosball2Player-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Foosball2Player-v0", "human_url": "http://old.2pg.com/game/foosball-2-player/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/F/foosball-2-player.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GravityBall-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GravityBall-v0", "human_url": "http://www.gamesbutler.com/game/2457/gravity-ball/", "regions": null, "height": 500, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/gravityball.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MotoMadness-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.MotoMadness-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/moto-madness", "regions": null, "height": 367, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15170/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarrotFantasy-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CarrotFantasy-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/carrot-fantasy", "regions": null, "height": 690, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38450/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CandySlider-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": false, "autostart": false, "id": "flashgames.CandySlider-v0", "human_url": "http://www.smileygamer.com/ourgames/candyslider.html", "regions": null, "height": 400, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/CandySlider.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalacticGems2Accelerated-v0": {"readme_header": null, "categories": ["match 3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.GalacticGems2Accelerated-v0", "human_url": "http://www.kongregate.com/games/MikRad/galactic-gems-2-accelerated", "regions": null, "height": 527, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://assets.kongregate.com/gamez/0018/6168/live/Galactic_Gems_2_Accel.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MashaCollectsButterflies-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MashaCollectsButterflies-v0", "human_url": "http://www.gamesforwebsites.com/game/masha-collects-butterflies", "regions": null, "height": 520, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137594/game.swf?1461681034", "enable_internet": false, "serve_from_domain": null}, "flashgames.Thaw-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Thaw-v0", "human_url": "http://publishers.spilgames.com/en/game/Thaw,576742227280287201", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/Thaw/Thaw.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DinoBubble-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DinoBubble-v0", "human_url": "http://www.games68.com/games.php?id=25639", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb4c9ca47.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DoughSnake-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DoughSnake-v0", "human_url": "http://www.gamesforwebsites.com/game/dough-snake", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39935/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dTruckInTheWoods-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dTruckInTheWoods-v0", "human_url": "http://www.cartitans.com/game/3d-truck-in-the-woods/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-truck-in-the-woods.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DrawGems-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DrawGems-v0", "human_url": "http://www.gamesforwebsites.com/game/draw-gems", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24134/game.swf?1359377583", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchCraft-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchCraft-v0", "human_url": "http://www.gamesforwebsites.com/game/match-craft", "regions": null, "height": 500, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133841/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchCrypt-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchCrypt-v0", "human_url": "http://www.gamesforwebsites.com/game/match-crypt", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133603/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OozingForever-v0": {"readme_header": null, "categories": ["platform", "sparsereward"], "rewarder": false, "autostart": false, "id": "flashgames.OozingForever-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/oozingforever/", "regions": null, "height": 400, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/oozingforever/oozingforever.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.GameInit-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GameInit-v0", "human_url": "http://www.gamesbutler.com/game/24358/game-init/", "regions": null, "height": 480, "width": 704, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/game_init_060415.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TerrestrialConflict-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TerrestrialConflict-v0", "human_url": "http://www.games68.com/games.php?id=25651", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb8710e37.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Indefinite-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Indefinite-v0", "human_url": "http://www.games68.com/games.php?id=25363", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae309b07247.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GonAndMon-v0": {"readme_header": null, "categories": ["2 player", "adventure", "featured", "our", "1 player", "2 player", "2pg", "multitask", "obstacles", "platform", "puzzle", "thinking"], "rewarder": false, "autostart": false, "id": "flashgames.GonAndMon-v0", "human_url": "http://old.2pg.com/game/gon-and-mon/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/G/gonandmon.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RedFuryRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.RedFuryRacing-v0", "human_url": "http://www.cartitans.com/game/red-fury-racing/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/red-fury-racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CanyonValleyRally-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CanyonValleyRally-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/canyon-valley-rally", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/22103/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.V8RacingChampion-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.V8RacingChampion-v0", "human_url": "http://www.willinggames.com/racing-games/V8-Racing-Champion.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.willinggames.com/d/file/20160104/52c041834531bec15188ae32fc807486.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperDash-v0": {"readme_header": null, "categories": ["2 player", "kids", "our", "1 player", "2 players", "2pg", "avoid", "bombs", "bonus", "collecting", "flash", "fun", "highscore", "keyboard", "kids", "pixelated", "powerups", "record"], "rewarder": true, "autostart": false, "id": "flashgames.SuperDash-v0", "human_url": "http://old.2pg.com/game/super-dash/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/superdash2pg_1365939264.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stickylinky-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.Stickylinky-v0", "human_url": "http://publishers.spilgames.com/en/game/StickyLinky,576742227280287416", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/Sticky_linky/stickylinky-primary.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl7-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AbductionGrannysVersion-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AbductionGrannysVersion-v0", "human_url": "http://www.games68.com/games.php?id=25219", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25e64a173.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DodgeAndCrash-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.DodgeAndCrash-v0", "human_url": "http://www.cartitans.com/game/dodge-and-crash/", "regions": null, "height": 458, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/upload/games/1945/5527750151.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ShamelessClone2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ShamelessClone2-v0", "human_url": "http://old.2pg.com/game/shameless-clone-2-player/play/", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/sc_1362356708.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlastTheMooks-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlastTheMooks-v0", "human_url": "http://www.gamesbutler.com/game/8072/blast-the-mooks/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/b2fbcb8fd552e685.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.ReverseBoots-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.ReverseBoots-v0", "human_url": "http://publishers.spilgames.com/en/game/Reverse-Boots,576742227280288523", "regions": null, "height": 680, "width": 510, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/r/Reverse_boots/ReverseBoots_final.swf", "enable_internet": false, "serve_from_domain": null}, "internet.SlitherIONoSkins-v0": {"categories": ["mmo"], "height": 300, "width": 502, "server_timestep_limit": null, "tags": {}, "id": "internet.SlitherIONoSkins-v0", "rewarder": true, "url": "http://slither.io/", "enable_internet": true, "autostart": true}, "flashgames.TheCubicMonkeyAdventures2-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TheCubicMonkeyAdventures2-v0", "human_url": "http://www.gamesforwebsites.com/game/the-cubic-monkey-adventures-2", "regions": null, "height": 400, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137905/game.swf?1463066352", "enable_internet": false, "serve_from_domain": null}, "flashgames.Knighttron-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Knighttron-v0", "human_url": "http://armorgames.com/play/17722", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/knighttron-17722.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClickerMonsters-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ClickerMonsters-v0", "human_url": "http://www.gamesbutler.com/game/22147/clicker-monsters/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/5631f1a3aeab59b4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterChains-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MonsterChains-v0", "human_url": "http://www.games68.com/games.php?id=25224", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25fd38c07.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Long_short-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Long_short-v0", "human_url": "http://www.gamesbutler.com/game/7178/long_short/", "regions": null, "height": 570, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/04086cc3dda3ab24.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PufferFish-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PufferFish-v0", "human_url": "http://www.gamesforwebsites.com/game/puffer-fish", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129225/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FishAndDestroy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FishAndDestroy-v0", "human_url": "http://old.2pg.com/game/fish-and-destroy/play/", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/fishanddestroy2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperIdleMaster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperIdleMaster-v0", "human_url": "http://www.games68.com/games.php?id=5000059", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce84414182.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleRubble-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BubbleRubble-v0", "human_url": "http://www.gamesforwebsites.com/game/bubble-rubble-the-island", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/131725/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PoliceInterceptor-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.PoliceInterceptor-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/police-interceptor", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/35111/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl12-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl12-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Zombonarium-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Zombonarium-v0", "human_url": "http://publishers.spilgames.com/en/game/Zombonarium,576742227280294623", "regions": null, "height": 800, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://files.cdn.spilcloud.com/flash/zombonarium.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LetsFall-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LetsFall-v0", "human_url": "http://www.y8.com/games/let_s_fall_", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/88523/original/let_s_fall_.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MinicarHunt-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.MinicarHunt-v0", "human_url": "http://www.cartitans.com/game/minicar-hunt/", "regions": null, "height": 458, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/upload/games/1749/4952196422.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EasterEggSlider-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": false, "autostart": false, "id": "flashgames.EasterEggSlider-v0", "human_url": "http://www.smileygamer.com/ourgames/eastereggslider.html", "regions": null, "height": 540, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/EasterEggSlider-NoAds.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvasiveRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.EvasiveRacers-v0", "human_url": "http://www.cartitans.com/game/evasive-racers/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/evasive-racers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AerobaticMaster2-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.AerobaticMaster2-v0", "human_url": "http://www.gamesforwebsites.com/game/aerobatic-master-2", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22626/game.swf?1363780279", "enable_internet": false, "serve_from_domain": null}, "flashgames.Quick-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Quick-v0", "human_url": "http://notdoppler.com/quick.php", "regions": null, "height": 516, "width": 654, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/quick.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AngryNewsVan-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.AngryNewsVan-v0", "human_url": "http://www.cartitans.com/game/angry-news-van/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/angry-news-van.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RollerRider-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.RollerRider-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/roller-rider", "regions": null, "height": 512, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0021/6804/live/embeddable_216804.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DreamChristmasLink-v0": {"readme_header": null, "categories": ["puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.DreamChristmasLink-v0", "human_url": "http://publishers.spilgames.com/en/game/Dream-Christmas%20Link,576742227280285619", "regions": null, "height": 634, "width": 870, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/d/dream_christmas_link/dream_christmas_link.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChickInduce-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ChickInduce-v0", "human_url": "http://www.gamesforwebsites.com/game/chick-induce", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/140741/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TankStorm2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TankStorm2-v0", "human_url": "http://1000webgames.com/play-8708-Tank-Storm-2.html", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tankstorm2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PickUpTruckRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.PickUpTruckRacing-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/pick-up-truck-racing", "regions": null, "height": 500, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38128/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Kawairun-v0": {"readme_header": null, "categories": ["2 player", "action", "cool games", "featured", "our", "1 player", "2 players", "gameboltz", "jumping", "kawairun", "multiplayer", "obstacles", "run", "runner", "running", "skills", "upgrades"], "rewarder": true, "autostart": false, "id": "flashgames.Kawairun-v0", "human_url": "http://old.2pg.com/game/kawairun/play/", "regions": null, "height": 400, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/Kawairun.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RhythmRockets-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RhythmRockets-v0", "human_url": "http://armorgames.com/play/6449", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/rhythm-rockets-6449.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Snake-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Snake-v0", "human_url": "http://www.gamesforwebsites.com/game/snake", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/111118/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl6-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleTub-v0": {"readme_header": null, "categories": ["bubble"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleTub-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Tub,576742227280285413", "regions": null, "height": 580, "width": 870, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/BubbleTub/BubbleTub.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MedievalShark-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MedievalShark-v0", "human_url": "http://armorgames.com/play/14007", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/medieval-shark-14007.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubblesInSpace-v0": {"readme_header": null, "categories": ["2 player", "our", "shooting", "1 player", "2 players", "2pg", "aim", "bubble", "bubbles", "color", "flash", "free", "match", "matching", "shooter"], "rewarder": false, "autostart": false, "id": "flashgames.BubblesInSpace-v0", "human_url": "http://old.2pg.com/wp-content/games/custom/B/bubbles-in-space.swf", "regions": null, "height": null, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "640", "enable_internet": false, "serve_from_domain": null}, "flashgames.FarmRush-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FarmRush-v0", "human_url": "http://www.games68.com/games.php?id=5000006", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c13d620f79.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ThePretenderPartThree-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.ThePretenderPartThree-v0", "human_url": "http://publishers.spilgames.com/en/game/The-Pretender:-Part-Three,576742227280285438", "regions": null, "height": 755, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/the_pretender_3/PretenderPartThree_v3.0b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AircraftRace-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AircraftRace-v0", "human_url": "http://1000webgames.com/play-10100-Aircraft-Race.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/aircraftrace.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RabbitPlanetEscape-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RabbitPlanetEscape-v0", "human_url": "http://www.games68.com/games.php?id=25250", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae266ff40eb.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlappyBat-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlappyBat-v0", "human_url": "http://www.gamesforwebsites.com/game/flabby-bat", "regions": null, "height": 432, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/121993/game.swf?1399478529", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpellIdle2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpellIdle2-v0", "human_url": "http://www.games68.com/games.php?id=5000002", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c0e9900cee.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Oddball2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Oddball2-v0", "human_url": "http://armorgames.com/play/1220", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/oddball-2-1220.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl3-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmericanRacing2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.AmericanRacing2-v0", "human_url": "http://turbonuke.com/games.php?game=americanracing2", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://turbonuke.com/flashgames/globalrallyracer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PaulVaulting-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PaulVaulting-v0", "human_url": "http://www.gamesbutler.com/game/17214/paul-vaulting/", "regions": null, "height": 480, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/paul_vaulting2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TowerMoon-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.TowerMoon-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/tower-moon", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21680/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnowQueen4-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.SnowQueen4-v0", "human_url": "http://publishers.spilgames.com/en/game/Snow-Queen-4,576742227280290589", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/snowqueen4/snowqueen41.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RainbowDrops-v0": {"readme_header": null, "categories": ["2 player", "adventure", "girl", "our", "1 player", "2 players", "2pg", "avoid", "distance", "flash", "highscore", "love", "multiplayer", "rainbow", "running", "skill"], "rewarder": false, "autostart": false, "id": "flashgames.RainbowDrops-v0", "human_url": "http://old.2pg.com/game/rainbow-drops/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/rainbow_drops_2pg_1374836963.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RollingHills-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.RollingHills-v0", "human_url": "http://publishers.spilgames.com/en/game/Rolling-Hills,576742227280285050", "regions": null, "height": 640, "width": 350, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/r/RollingHills/RollingHills.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Multitask-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Multitask-v0", "human_url": null, "regions": null, "height": 530, "width": 740, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0005/3248/live/MultitaskMOCHIcompleteAZERTY.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TankStorm-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TankStorm-v0", "human_url": "http://1000webgames.com/play-8127-Tank-Storm.html", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tank-storm.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BloodbathBay-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.BloodbathBay-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/bloodbath-bay", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/19272/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KartingSuperGo-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.KartingSuperGo-v0", "human_url": "http://www.kongregate.com/games/fightclub69/karting-super-go", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn4.kongcdn.com/game_icons/0047/2708/250x200.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.DnaLabRush-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DnaLabRush-v0", "human_url": "http://www.gamesforwebsites.com/game/dna-lab-rush", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137217/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RetroRunner-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "1 player", "2 players", "2pg", "avoid", "coins", "collecting", "free", "highscore", "jump", "pixelated", "retro", "runner", "running", "score"], "rewarder": true, "autostart": false, "id": "flashgames.RetroRunner-v0", "human_url": "http://old.2pg.com/game/retro-runner/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/retro_runner_2pg_1377290931.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarrotFantasyExtreme2-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CarrotFantasyExtreme2-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/carrot-fantasy-extreme-2", "regions": null, "height": 690, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/39398/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SavingLittleAlien-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SavingLittleAlien-v0", "human_url": "http://www.games68.com/games.php?id=25517", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee8bd071bb.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CakeQuest-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.CakeQuest-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/cake-quest", "regions": null, "height": 410, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15167/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FreeSouls-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FreeSouls-v0", "human_url": "http://www.games68.com/games.php?id=5000160", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e19c5735b8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JumpOverTheRings-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.JumpOverTheRings-v0", "human_url": "http://www.gamesbutler.com/game/25738/jump-over-the-rings/", "regions": null, "height": 450, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/a2272590c846cffc.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FasterMiterMaster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FasterMiterMaster-v0", "human_url": "http://armorgames.com/play/6375", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/faster-miter-master-6375.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WorldsGuard2-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.WorldsGuard2-v0", "human_url": "http://publishers.spilgames.com/en/game/World's-Guard-2,576742227280289032", "regions": null, "height": 520, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/w/Worlds_guard2/game_worlds_guard_2_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CruiseAdventure-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CruiseAdventure-v0", "human_url": "http://www.games68.com/games.php?id=5000760", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570f8f0feaee2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Underrun-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Underrun-v0", "human_url": "http://www.gamesforwebsites.com/game/underrun", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24520/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Raze3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Raze3-v0", "human_url": "http://www.games68.com/games.php?id=5000066", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce9a763ba2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Match3ChristmasPack-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Match3ChristmasPack-v0", "human_url": "http://www.gamesforwebsites.com/game/match-3-christmas-pack", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129734/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl15-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl15-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AchilliaTheGame-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AchilliaTheGame-v0", "human_url": "http://www.games68.com/games.php?id=25550", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee9776dd00.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TankStorm4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TankStorm4-v0", "human_url": "http://1000webgames.com/play-10136-Tank-Storm-4.html", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tankstorm4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LonelyEscapeAsylum-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LonelyEscapeAsylum-v0", "human_url": "http://www.games68.com/games.php?id=25532", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee91568d8e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ThatRedButton-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ThatRedButton-v0", "human_url": "http://www.games68.com/games.php?id=5000021", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c1f2a17124.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LawnmowerRacing3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.LawnmowerRacing3d-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/lawnmower-racing-3d", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/22142/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OffRoaders3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.OffRoaders3d-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/off-roaders-3d", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21957/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FunnyEaster-v0": {"readme_header": null, "categories": ["match-3", "seasonal", "fun"], "rewarder": false, "autostart": false, "id": "flashgames.FunnyEaster-v0", "human_url": "http://publishers.spilgames.com/en/game/Funny-Easter,576742227280291407", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/f/funny%20easter/funny_easter_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TrafficCollision-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TrafficCollision-v0", "human_url": "http://www.games68.com/games.php?id=25604", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea8dd00c1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarrotFantasy2Undersea-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CarrotFantasy2Undersea-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/carrot-fantasy-2-undersea", "regions": null, "height": 690, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38968/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BumbleTumble-v0": {"readme_header": null, "categories": ["puzzle", "online save", "cute", "bug"], "rewarder": false, "autostart": false, "id": "flashgames.BumbleTumble-v0", "human_url": "http://cache.armorgames.com/files/games/bumble-tumble-6452.swf", "regions": null, "height": null, "width": "", "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "", "enable_internet": false, "serve_from_domain": null}, "flashgames.FreakyRun-v0": {"readme_header": null, "categories": ["2 player", "action", "our", "1 player", "2 player", "2pg", "collect", "comics", "freak", "multiplayer", "our", "run"], "rewarder": false, "autostart": false, "id": "flashgames.FreakyRun-v0", "human_url": "http://old.2pg.com/game/freaky-run/play/", "regions": null, "height": 600, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/freaky-run-2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DaleAndPeakot-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DaleAndPeakot-v0", "human_url": "http://www.miniclip.com/games/dale-and-peakot/en/", "regions": null, "height": 540, "width": 720, "extra_files": {"relative": {"files": ["dalePeakot_JB_LevelsAssets.swf"], "base": "http://www.miniclip.com/games/dale-and-peakot/en"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/dale-and-peakot/en/dalepeakot.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ImitationNationSnakeGame-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ImitationNationSnakeGame-v0", "human_url": "http://www.gamesforwebsites.com/game/imitation-nation---snake-game", "regions": null, "height": 385, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/40008/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CosmicSwitch-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.CosmicSwitch-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/cosmic-switch", "regions": [{"coordinates": [425, 58, 417, 29], "type": "noclick"}, {"coordinates": [426, 57, 417, 29], "type": "noclick"}], "height": 440, "width": 526, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3224/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Go-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Go-v0", "human_url": "http://armorgames.com/play/310", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/go-310.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SupergirlGo-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SupergirlGo-v0", "human_url": "http://publishers.spilgames.com/en/game/SuperGirl-Go!,576742227280289582", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/super_girl_go.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IcyGifts2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IcyGifts2-v0", "human_url": "http://www.kongregate.com/games/SilenGames/icy-gifts-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/0645/live/embeddable_160645.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChuteAcademy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ChuteAcademy-v0", "human_url": "http://armorgames.com/play/3455", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/chute-academy-3455.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BouncyCannon-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BouncyCannon-v0", "human_url": "http://www.games68.com/games.php?id=25205", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25907bad5.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BikeTrial3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BikeTrial3-v0", "human_url": "http://1000webgames.com/play-8614-Bike-Trial-3.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/biketrial3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PuddingPie-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PuddingPie-v0", "human_url": "http://www.gamesforwebsites.com/game/pudding-pie", "regions": null, "height": 500, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/131170/game.swf?1430129008", "enable_internet": false, "serve_from_domain": null}, "flashgames.Kinetikz2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Kinetikz2-v0", "human_url": "http://armorgames.com/play/1953", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/kinetikz-2-1953.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SurvivalLab-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.SurvivalLab-v0", "human_url": null, "regions": null, "height": 518, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/survivallab/survivallab.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.EpicTimePirates-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EpicTimePirates-v0", "human_url": "http://notdoppler.com/epictimepirates.php", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/epictimepirates.swf?2", "enable_internet": false, "serve_from_domain": null}, "flashgames.CrazyDarts-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CrazyDarts-v0", "human_url": "http://www.gamesforwebsites.com/game/crazy-darts", "regions": null, "height": 600, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/20597/game.swf?1381835957", "enable_internet": false, "serve_from_domain": null}, "flashgames.EmpireBusiness2Beta-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EmpireBusiness2Beta-v0", "human_url": "http://www.games68.com/games.php?id=5000064", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce9750a4d0.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TractorTrial2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TractorTrial2-v0", "human_url": "http://1000webgames.com/play-9965-Tractor-Trial-2.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tractortrial2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsa-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsa-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperRallyChallenge-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.SuperRallyChallenge-v0", "human_url": "http://insanehero.com/mygames/SuperRallyChallenge/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/SuperRallyChallenge/superrallychallenge.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RunSoldierRun-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RunSoldierRun-v0", "human_url": "http://armorgames.com/play/1535", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/run-soldier-run-1535.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Kinetikz-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Kinetikz-v0", "human_url": "http://armorgames.com/play/1032", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/kinetikz-1032.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SubmarineFighter-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SubmarineFighter-v0", "human_url": "http://www.gamesforwebsites.com/game/submarine-fighter", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/75923/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PocketRocket-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PocketRocket-v0", "human_url": "http://armorgames.com/play/3368", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pocket-rocket-3368.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TankStorm3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TankStorm3-v0", "human_url": "http://1000webgames.com/play-9668-Tank-Storm-3.html", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tankstorm3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvilSun-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.EvilSun-v0", "human_url": "http://www.gamesbutler.com/game/11623/evil-sun/", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/4b04ae0435b9d93b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BurgerBar-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BurgerBar-v0", "human_url": "http://armorgames.com/play/17670", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/burger-bar-17670.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dClassicRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dClassicRacing-v0", "human_url": "http://www.cartitans.com/game/3d-classic-racing/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-classic-racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WonderRocket-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WonderRocket-v0", "human_url": "http://www.gamesforwebsites.com/game/wonder-rocket", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/37955/game.swf?1366895181", "enable_internet": false, "serve_from_domain": null}, "flashgames.BottleCaps-v0": {"readme_header": null, "categories": [], "rewarder": true, "autostart": false, "id": "flashgames.BottleCaps-v0", "human_url": "http://www.neongames.com/game/Bottle+Caps", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/candymatch.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.QubedMysteriousIsland-v0": {"readme_header": null, "categories": ["platform puzzles"], "rewarder": false, "autostart": false, "id": "flashgames.QubedMysteriousIsland-v0", "human_url": "http://www.platformgames.com/game/Qubed%3A+Mysterious+Island", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.platformgames.com/uploaded/swf/qubed.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalaxyEvo2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GalaxyEvo2-v0", "human_url": "http://www.y8.com/games/galaxy_evo_2", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/119205/original/galaxy_evo.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IndependenceDaySlacking2015-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IndependenceDaySlacking2015-v0", "human_url": "http://www.games68.com/games.php?id=25407", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae32451d0c8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Viridia-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Viridia-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/viridia/", "regions": null, "height": 465, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/viridia/viridia.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl13-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl13-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UltimateEscape-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "1 player", "2 players", "2pg", "flash", "free", "game", "highscore", "jumping", "multiplayer", "pixelated", "run", "runner", "running", "skill"], "rewarder": true, "autostart": false, "id": "flashgames.UltimateEscape-v0", "human_url": "http://old.2pg.com/game/ultimate-escape/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/UltimateEscape_1366737833.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubblePopAdventure-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BubblePopAdventure-v0", "human_url": "http://www.gamesforwebsites.com/game/bubble-pop-adventure", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/133130/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HalloweenAdventureRun-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.HalloweenAdventureRun-v0", "human_url": "http://www.gamesforwebsites.com/game/halloween-adventure-run-", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/140623/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MapTurtleJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MapTurtleJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/map-turtle-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17038/game.swf?1304178443", "enable_internet": false, "serve_from_domain": null}, "flashgames.JellyFriend-v0": {"readme_header": null, "categories": ["1 player", "flash", "jewel", "free", "match 3", "idnet", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": true, "autostart": false, "id": "flashgames.JellyFriend-v0", "human_url": "http://www.y8.com/games/jelly_friend", "regions": null, "height": 700, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/87720/original/jelly_friend.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SwingTriangle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SwingTriangle-v0", "human_url": "http://www.games68.com/games.php?id=25587", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea546d8a7.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ProjectMonochrome-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ProjectMonochrome-v0", "human_url": "http://armorgames.com/play/499", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/project-monochrome-499.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LaxAirbusParking-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LaxAirbusParking-v0", "human_url": "http://www.gamesbutler.com/game/13971/lax-airbus-parking/", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/2c9d563ca4039aca.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MotorWheels-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.MotorWheels-v0", "human_url": "http://www.cartitans.com/game/motor-wheels/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/motor-wheels.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClimberGuy-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ClimberGuy-v0", "human_url": "http://www.gamesforwebsites.com/game/climber-guy", "regions": null, "height": 750, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/136513/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NervousLadybug-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.NervousLadybug-v0", "human_url": "http://www.gamesforwebsites.com/game/nervous-ladybug", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23562/game.swf?1353496444", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchStars-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchStars-v0", "human_url": "http://www.gamesforwebsites.com/game/match-stars", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/132146/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AmigoPancho-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AmigoPancho-v0", "human_url": "http://publishers.spilgames.com/en/game/Amigo-Pancho,576742227280284644", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/a/amigo_pancho/AmigoPancho.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RoadRacing-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RoadRacing-v0", "human_url": "http://1000webgames.com/play-8185-Road-Racing.html", "regions": null, "height": 550, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/roadracing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnowQueen3-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.SnowQueen3-v0", "human_url": "http://publishers.spilgames.com/en/game/Snow-Queen-3,576742227280289219", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/Snow_Queen_3/Snow_Queen_3_v1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SapphireClix-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SapphireClix-v0", "human_url": "http://www.gamesforwebsites.com/game/sapphire-clix", "regions": null, "height": 510, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38247/game.swf?1370271893", "enable_internet": false, "serve_from_domain": null}, "flashgames.Overheat-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Overheat-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/overheat", "regions": null, "height": 530, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/22502/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EpicDefender-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.EpicDefender-v0", "human_url": "http://publishers.spilgames.com/en/game/Epic-Defender,576742227280284318", "regions": null, "height": 500, "width": 700, "extra_files": {"absolute": ["http://api.configar.org/cf/pb/1/settings/0/0/01471020250b96470e93de6345fdfe94", "http://www8.agame.com/sdk/spilapi/localization/e/epic_defender_1305188404.swf"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/e/epic_defender/epic_defender.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.QubeyTheCube-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.QubeyTheCube-v0", "human_url": "http://armorgames.com/play/17778", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/qubey-the-cube-17778.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VengeanceRider-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.VengeanceRider-v0", "human_url": "http://www.cartitans.com/game/vengeance-rider/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/vengeance-rider.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IdlePlanet-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IdlePlanet-v0", "human_url": "http://www.games68.com/games.php?id=5000129", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf6edbdc0e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlastTheMooksLevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlastTheMooksLevelPack-v0", "human_url": "http://www.gamesbutler.com/game/9455/blast-the-mooks-level-pack/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/7b05cd16fdfd045e.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stealthbound-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Stealthbound-v0", "human_url": "http://www.games68.com/games.php?id=5000119", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf4b326707.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl9-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl9-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Primary-v0": {"readme_header": null, "categories": ["noscore"], "rewarder": false, "autostart": false, "id": "flashgames.Primary-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/primary/", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/primary/primary.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.FinalNinjaZero-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.FinalNinjaZero-v0", "human_url": "http://publishers.spilgames.com/en/game/Final_Ninja_Zero,576742227280294836", "regions": null, "height": 800, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://games.cdn.spilcloud.com/f/finalninjazero_final.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl2-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HappyBees-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.HappyBees-v0", "human_url": "http://publishers.spilgames.com/en/game/Happy-Bees,576742227280289328", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/h/Happy_Bees/Happy_Bees_v1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AnywayFish-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AnywayFish-v0", "human_url": "http://armorgames.com/play/12192", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/anyway-fish-12192.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Cruisin-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.Cruisin-v0", "human_url": "http://www.kongregate.com/games/fightclub69/cruisin", "regions": null, "height": 270, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0016/1626/live/cruisin.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Enhanced-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Enhanced-v0", "human_url": "http://www.kongregate.com/games/mrDN/enhanced", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0018/5217/live/embeddable_185217.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IdleLifting-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IdleLifting-v0", "human_url": "http://www.games68.com/games.php?id=25320", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2f0c4714c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PickAndDig2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.PickAndDig2-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/rocketeer", "regions": null, "height": 630, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21832/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dFlashRacer-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dFlashRacer-v0", "human_url": "http://www.cartitans.com/game/3d-flash-racer/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-flash-racer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BobbyNutcaseMotoJumping-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BobbyNutcaseMotoJumping-v0", "human_url": "http://publishers.spilgames.com/en/game/Bobby-Nutcase-Moto-Jumping,576742227280285280", "regions": null, "height": 800, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/Bobby_nutcase_motojumping/MotoJumping.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheCaseOfScaryShadow-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheCaseOfScaryShadow-v0", "human_url": "http://www.games68.com/games.php?id=25580", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eea308b85b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JonnyBackflip-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.JonnyBackflip-v0", "human_url": "http://armorgames.com/play/14723", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/jonny-backflip-14723.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MightyTower-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MightyTower-v0", "human_url": "http://old.2pg.com/game/mighty-tower-2pg/play/", "regions": null, "height": 400, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/mighty-tower_1363308497.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchingSweetHearts-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchingSweetHearts-v0", "human_url": "http://www.gamesforwebsites.com/game/matching-sweet-harts", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137952/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.99BricksTheLegendOfGarry-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.99BricksTheLegendOfGarry-v0", "human_url": "http://armorgames.com/play/4375", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/99-bricks-the-legend-4375.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ReachTheGoal-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ReachTheGoal-v0", "human_url": "http://www.dailygames.com/games/reach-the-goal.html", "regions": null, "height": 300, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.dailygames.com/games/reach-the-goal.html", "enable_internet": false, "serve_from_domain": null}, "flashgames.Connect2-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.Connect2-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/connect-2", "regions": [{"coordinates": [15, 568, 80, 37], "type": "noclick"}, {"coordinates": [14, 570, 471, 39], "type": "noclick"}], "height": 420, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3041/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheTowerman-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheTowerman-v0", "human_url": "http://old.2pg.com/game/the-towerman/play/", "regions": null, "height": 550, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/Tower.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JetpackJackride-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.JetpackJackride-v0", "human_url": "http://www.gamesforwebsites.com/game/jetpack-jackride", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130100/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlyingCookieQuest-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FlyingCookieQuest-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/flying-cookie-quest", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/0758/live/embeddable_160758.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CaptainSteelbounce-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CaptainSteelbounce-v0", "human_url": "http://www.kongregate.com/games/BeardshakerGames/captain-steelbounce", "regions": null, "height": 528, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0015/2141/live/embeddable_152141.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HyperTravel-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HyperTravel-v0", "human_url": "http://armorgames.com/play/16096", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/hyper-travel-16096.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.QuashBoard-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.QuashBoard-v0", "human_url": "http://www.games68.com/games.php?id=25666", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eebcfede05.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RoadblockAttack-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.RoadblockAttack-v0", "human_url": "http://www.cartitans.com/game/roadblock-attack/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/roadblock-attack.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Frogged-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Frogged-v0", "human_url": "http://www.y8.com/games/frogged", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/56088/original/frogged.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WolfSpiderJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WolfSpiderJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/wolf-spider-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/17023/game.swf?1304178524", "enable_internet": false, "serve_from_domain": null}, "flashgames.Harvest-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Harvest-v0", "human_url": "http://notdoppler.com/harvest.php", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/harvest.swf?1", "enable_internet": false, "serve_from_domain": null}, "flashgames.FirefighterCannon-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FirefighterCannon-v0", "human_url": "http://1000webgames.com/play-7512-FireFighter-Cannon.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/firefighter-cannon.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stardrops-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Stardrops-v0", "human_url": "http://www.kongregate.com/games/Seirie/stardrops", "regions": null, "height": 600, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0015/1676/live/embeddable_151676.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StitchlandConflict-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.StitchlandConflict-v0", "human_url": "http://armorgames.com/play/6796", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/stitchland-conflict-6796.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BombIt6-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.BombIt6-v0", "human_url": "http://publishers.spilgames.com/en/game/Bomb-It-6,576742227280288098", "regions": null, "height": 700, "width": 510, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bomb_it_6/bomb_it_6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WinterSlider-v0": {"readme_header": null, "categories": ["puzzle", "christmas", "match 3"], "rewarder": true, "autostart": false, "id": "flashgames.WinterSlider-v0", "human_url": "http://www.kongregate.com/games/SmileyGamer/winter-slider", "regions": null, "height": 540, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0016/1697/live/winterslider.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperBoxotron2000-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperBoxotron2000-v0", "human_url": "http://www.games68.com/games.php?id=25654", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb95af002.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Solarsaurs-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Solarsaurs-v0", "human_url": "http://armorgames.com/play/202", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/solarsaurs-202.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NewSplitterPals-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NewSplitterPals-v0", "human_url": "http://www.kongregate.com/games/EvgenyKarataev/new-splitter-pals", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/2464/live/embeddable_172464.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RussianTruck-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RussianTruck-v0", "human_url": "http://www.gamesbutler.com/game/26371/russian-truck/", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/2c3975a12dd62e5f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PanikInChocoland-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.PanikInChocoland-v0", "human_url": "http://www.miniclip.com/games/panik-in-chocoland/en/", "regions": null, "height": 453, "width": 556, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/panik-in-chocoland/en/Panik.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RunRamRun-v0": {"readme_header": null, "categories": ["2 player", "adventure", "kids", "our", "1 player", "2 players", "2pg", "avoid", "collecting", "free", "obstacles", "running", "sheep"], "rewarder": true, "autostart": false, "id": "flashgames.RunRamRun-v0", "human_url": "http://old.2pg.com/game/run-ram-run/play/", "regions": null, "height": 530, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/R/RunRamRun.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlowerSolitaire-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FlowerSolitaire-v0", "human_url": "http://publishers.spilgames.com/en/game/Flower-Solitaire,576742227280285161", "regions": null, "height": 700, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/f/FlowerSolitaire/FlowerSolitaire-Family.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GamerMemoryTest-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GamerMemoryTest-v0", "human_url": "http://www.games68.com/games.php?id=5000147", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cfb81c5aec.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarrotFantasyExtreme3-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CarrotFantasyExtreme3-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/carrot-fantasy-extreme", "regions": null, "height": 690, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/39283/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SiegeHeroPiratePillage-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SiegeHeroPiratePillage-v0", "human_url": "http://armorgames.com/play/17608", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/siege-hero-pirate-pi-17608.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SmileyPuzzle-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": true, "autostart": false, "id": "flashgames.SmileyPuzzle-v0", "human_url": "http://www.smileygamer.com/ourgames/smileypuzzle.html", "regions": null, "height": 400, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/SmileyPuzzle.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClubNitro-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.ClubNitro-v0", "human_url": "http://www.turbonuke.com/games.php?game=clubnitro", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/clubnitro.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SlingBaby-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SlingBaby-v0", "human_url": "http://armorgames.com/play/12785", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/sling-baby-12785.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DuskRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.DuskRacers-v0", "human_url": "http://www.cartitans.com/game/dusk-racers/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/dusk-racers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FeedOurDoughnutOverlords-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.FeedOurDoughnutOverlords-v0", "human_url": "http://publishers.spilgames.com/en/game/Feed-Our-Doughnut-Overlords!,576742227280285048", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/f/Feed_our_doughnut_overlords/Fodo.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpinClimbGreen-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SpinClimbGreen-v0", "human_url": "http://armorgames.com/play/1370", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/spin-climb-green-1370.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TrickyRick-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TrickyRick-v0", "human_url": "http://www.kongregate.com/games/tAMAS_Games/tricky-rick", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/0342/live/embeddable_160342.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SeaPong-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SeaPong-v0", "human_url": "http://www.gamesforwebsites.com/game/sea-pong", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/6629/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Neopods-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Neopods-v0", "human_url": "http://www.gamesbutler.com/game/6211/neopods/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/ef3a22fc8ff9ef4b.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PowerCopter-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PowerCopter-v0", "human_url": "http://armorgames.com/play/1280", "regions": null, "height": 500, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/power-copter-1280.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HammerBall-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HammerBall-v0", "human_url": "http://armorgames.com/play/12877", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/hammer-ball-12877.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TurboRally-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.TurboRally-v0", "human_url": "http://notdoppler.com/turborally.php", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/turborally.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OkParking-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.OkParking-v0", "human_url": "http://1000webgames.com/play-9732-OK-Parking.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/okparking.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EasterBubbles-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.EasterBubbles-v0", "human_url": "http://publishers.spilgames.com/en/game/Easter-Bubbles,576742227280290430", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/e/easterbubble/easterbubble.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Rose-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Rose-v0", "human_url": "http://armorgames.com/play/1345", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/rose-1345.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MiniSportsChallenge-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MiniSportsChallenge-v0", "human_url": "http://www.gamesforwebsites.com/game/mini-sports-challenge", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23457/game.swf?1352644956", "enable_internet": false, "serve_from_domain": null}, "flashgames.JamesTheDeepSeaZebra-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.JamesTheDeepSeaZebra-v0", "human_url": "http://armorgames.com/play/205", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/james-the-deep-sea-z-205.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl8-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Thundercars-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.Thundercars-v0", "human_url": "http://notdoppler.com/thundercars.php", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/thundercars.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TowerEmpire-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TowerEmpire-v0", "human_url": "http://1000webgames.com/play-8826-Tower-Empire.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/tower-empire.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.InfectonatorSurvivorsAlphaDemo-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.InfectonatorSurvivorsAlphaDemo-v0", "human_url": "http://www.games68.com/games.php?id=5000013", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c16dd9ea61.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FinalSiege-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.FinalSiege-v0", "human_url": "http://publishers.spilgames.com/en/game/Final-Siege,576742227280290332", "regions": null, "height": 600, "width": 800, "extra_files": {"absolute": ["http://api.configar.org/cf/pb/1/settings/0/0/8bc2d9277d443bb23723000e76de5cfe", "http://files.cdn.spilcloud.com/flashapi_1_3_1_147/ServicesConnection.swf", "http://files.cdn.spilcloud.com/flashapi_1_3_1_147/BrandSystem.swf", "http://files.cdn.spilcloud.com/flashapi_1_3_1_147/ServicePack.swf", "http://www8.agame.com/sdk/spilapi/localization/BrandLocalization.swf", "http://files.cdn.spilcloud.com/flashapi_assets/logos/a10.com.swf"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/f/Final_siege/Final_Siege.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Parkour-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Parkour-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/parkour", "regions": null, "height": 600, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/24373/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RobotDuelFight-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RobotDuelFight-v0", "human_url": "http://www.games68.com/games.php?id=25430", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae338cad2c6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TamusMitta-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.TamusMitta-v0", "human_url": "http://publishers.spilgames.com/en/game/Tamus-&-Mitta,576742227280285087", "regions": null, "height": 670, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/Tamus_and_mitta/tamus.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ExtremeSkiing-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ExtremeSkiing-v0", "human_url": "http://www.gamesforwebsites.com/game/extreme-skiing", "regions": null, "height": 480, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23430/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PoliceChaseCrackdown-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.PoliceChaseCrackdown-v0", "human_url": "http://turbonuke.com/games.php?game=policechasecrackdown", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://turbonuke.com/flashgames/policechasecrackdown.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Avalancher-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Avalancher-v0", "human_url": "http://www.gamesforwebsites.com/game/avalancher", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/136643/game.swf?1457021989", "enable_internet": false, "serve_from_domain": null}, "flashgames.InfernalMess-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.InfernalMess-v0", "human_url": "http://www.games68.com/games.php?id=25435", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae33a67db91.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PumpkinsInZombieTown-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.PumpkinsInZombieTown-v0", "human_url": "http://www.gamesforwebsites.com/game/pumpkins-in-zombie-town", "regions": null, "height": 520, "width": 740, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137221/game.swf?1459854860", "enable_internet": false, "serve_from_domain": null}, "flashgames.BikeTrial4-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BikeTrial4-v0", "human_url": "http://1000webgames.com/play-9583-Bike-Trial-4.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/biketrial4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EiffelTowerAtNight-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.EiffelTowerAtNight-v0", "human_url": "http://www.gamesforwebsites.com/game/eiffel-tower-at-night", "regions": null, "height": 650, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/138976/game.swf?1468853758", "enable_internet": false, "serve_from_domain": null}, "flashgames.MysticalAncientTreasure-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MysticalAncientTreasure-v0", "human_url": "http://www.games68.com/games.php?id=25549", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee974e6698.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Devilment-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.Devilment-v0", "human_url": "http://www.y8.com/games/devilment", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/84027/original/devilment.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NightRaceRally-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.NightRaceRally-v0", "human_url": "http://www.y8.com/games/night_race_rally", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/119157/original/night_race_rally.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dUrbanMadness-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dUrbanMadness-v0", "human_url": "http://www.cartitans.com/game/3d-urban-madness/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-urban-madness.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl15-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl15-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AdventuresOfBloo-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.AdventuresOfBloo-v0", "human_url": "http://www.miniclip.com/games/adventures-of-bloo/en/", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/adventures-of-bloo/en/adventuresbloo.swf", "enable_internet": false, "serve_from_domain": "www.miniclip.com"}, "flashgames.XChains-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.XChains-v0", "human_url": "http://armorgames.com/play/778", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/x-chains-778.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HarvestDay-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.HarvestDay-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/harvest-day", "regions": [{"coordinates": [21, 59, 430, 29], "type": "noclick"}], "height": 379, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/6632/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriveToWreck3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DriveToWreck3-v0", "human_url": "http://1000webgames.com/play-10158-Drive-To-Wreck-3.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/drivetowreck3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlashDrive-v0": {"readme_header": null, "categories": ["racing", "sports"], "rewarder": false, "autostart": false, "id": "flashgames.FlashDrive-v0", "human_url": "http://www.kongregate.com/games/MartianGames/flash-drive", "regions": null, "height": "", "width": "", "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0014/1396/live/FlashDrive.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperRallyExtreme-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.SuperRallyExtreme-v0", "human_url": "http://www.kongregate.com/games/fightclub69/super-rally-extreme", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn1.kongcdn.com/game_icons/0049/8355/250x200.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.MineDrop-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MineDrop-v0", "human_url": "http://publishers.spilgames.com/en/game/Mine-Drop,576742227280284693", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/m/MineDrop.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NanoKingdoms2JokersRevenge-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NanoKingdoms2JokersRevenge-v0", "human_url": "http://www.kongregate.com/games/Trutruka/nano-kingdoms-2-jokers-revenge", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/1289/live/embeddable_161289.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PapaLouie3WhenSundaesAttack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PapaLouie3WhenSundaesAttack-v0", "human_url": "http://www.games68.com/games.php?id=5000046", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce62bba443.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpunkyVsAliens-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SpunkyVsAliens-v0", "human_url": "http://www.gamesforwebsites.com/game/spunky-vs-aliens", "regions": null, "height": 530, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22700/game.swf?1435318521", "enable_internet": false, "serve_from_domain": null}, "flashgames.AsphaltMadness-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AsphaltMadness-v0", "human_url": "http://1000webgames.com/play-8776-Asphalt-Madness.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/asphalt-madness.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl6-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Sparks-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.Sparks-v0", "human_url": "http://www.arkadium.com/games/sparks/", "regions": null, "height": 444, "width": 640, "extra_files": {"relative": {"files": ["data/sparks_config.xml", "data/locale/en-US/strings.xml", "data/loggers/logger_config.xml", "data/assetpacks/engine.swf", "data/loggers/gameplay-logger.swf", "data/loggers/comscore-logger.swf", "branding.jpg"], "base": "http://amsarkadium-a.akamaihd.net/assets/global/game/sparks/96daf9b9-e77a-492f-96b0-14a418563083"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://amsarkadium-a.akamaihd.net/assets/global/game/sparks/96daf9b9-e77a-492f-96b0-14a418563083/sparks.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SwampTreck-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SwampTreck-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/swamp-treck", "regions": null, "height": 390, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/3222/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GroundBattles-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GroundBattles-v0", "human_url": "http://old.2pg.com/game/ground-battles/play/", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/G/ground-battles.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AwesomeRun2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.AwesomeRun2-v0", "human_url": "http://www.games68.com/games.php?id=5002000", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/57a9ddf8d26b6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Pathillogical-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Pathillogical-v0", "human_url": "http://www.kongregate.com/games/5minutesoff/pathillogical", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0007/3025/live/embeddable_73025.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AladdinAndTheWonderLamp-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.AladdinAndTheWonderLamp-v0", "human_url": "http://publishers.spilgames.com/en/game/Aladdin-and-the-wonder-lamp,576742227280289233", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/a/Aladin_and_the_Wonder_Lamp/Aladin_and_the_Wonder_Lamp.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlackRacerJigsawPuzzle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.BlackRacerJigsawPuzzle-v0", "human_url": "http://www.gamesforwebsites.com/game/black-racer-jigsaw-puzzle", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/16991/game.swf?1304178196", "enable_internet": false, "serve_from_domain": null}, "flashgames.BunnyAndSquirt-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BunnyAndSquirt-v0", "human_url": "http://www.games68.com/games.php?id=25249", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae266e06c11.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleHitChristmas-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleHitChristmas-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Hit:-Christmas,576742227280283935", "regions": [{"coordinates": [496, 161, 275, 286], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_hit_christmas/Bubble_Hit_Christmas.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BombIt4-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.BombIt4-v0", "human_url": "http://publishers.spilgames.com/en/game/Bomb-It-4,576742227280285511", "regions": null, "height": 700, "width": 510, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/BombIt4/bomb_it_4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RedCode3-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.RedCode3-v0", "human_url": "http://www.miniclip.com/games/red-code-3/en/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/red-code-3/en/redCode3SingleFile.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BeachCrazy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BeachCrazy-v0", "human_url": "http://armorgames.com/play/15569", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/beach-crazy-15569.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GrandPrixGo-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.GrandPrixGo-v0", "human_url": "http://www.kongregate.com/games/turboNuke/grand-prix-go", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0011/5460/live/GrandPrixGo.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Crumbs2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Crumbs2-v0", "human_url": "http://www.gamesbutler.com/game/4687/crumbs-2/", "regions": null, "height": 360, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/581f0c4b4ff804a9.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CarrotFantasy2Desert-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.CarrotFantasy2Desert-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/carrot-fantasy-2-desert", "regions": null, "height": 690, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38907/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SwagMan-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SwagMan-v0", "human_url": "http://www.gamesforwebsites.com/game/swag-man", "regions": null, "height": 600, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129912/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KeeperOfTheGrove3-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": true, "autostart": false, "id": "flashgames.KeeperOfTheGrove3-v0", "human_url": "http://www.y8.com/games/keeper_of_the_grove_3", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/91305/original/keeper_of_the_grove_3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacing-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SkiSim-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SkiSim-v0", "human_url": "http://www.gamesforwebsites.com/game/ski-sim", "regions": null, "height": 600, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/20589/game.swf?1377854676", "enable_internet": false, "serve_from_domain": null}, "flashgames.TouchTheSky-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TouchTheSky-v0", "human_url": "http://www.gamesforwebsites.com/game/touch-the-sky", "regions": null, "height": 600, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23253/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchToEnjoy-v0": {"readme_header": null, "categories": ["puzzle", "match 3"], "rewarder": false, "autostart": false, "id": "flashgames.MatchToEnjoy-v0", "human_url": "http://www.kongregate.com/games/charstudio/match-to-enjoy", "regions": null, "height": 527, "width": 620, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0012/6924/live/game_match_to_enjoy.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchAndCrash-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchAndCrash-v0", "human_url": "http://www.gamesforwebsites.com/game/match-and-crash", "regions": null, "height": 500, "width": 300, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/127650/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WildWestConflict-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WildWestConflict-v0", "human_url": "http://1000webgames.com/play-9395-Wild-West-Conflict.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/wildwestconflict.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ChromaticTowerDefense-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ChromaticTowerDefense-v0", "human_url": "http://www.gamesforwebsites.com/game/chromatic-tower-defense", "regions": null, "height": 550, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23181/game.swf?1353509333", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleGlee-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleGlee-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Glee,576742227280289253", "regions": [{"coordinates": [239, 41, 491, 38], "type": "noclick"}, {"coordinates": [458, 38, 490, 41], "type": "noclick"}], "height": 510, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/b/Bubble_Glee/Bubble_Glee.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SupercarDomination-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.SupercarDomination-v0", "human_url": "http://www.cartitans.com/game/supercar-domination/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/supercar-domination.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HighwayRevenge-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.HighwayRevenge-v0", "human_url": "http://www.cartitans.com/game/highway-revenge/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/highway-revenge.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl14-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl14-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MushboomsLevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MushboomsLevelPack-v0", "human_url": "http://www.gamesbutler.com/game/7684/mushbooms-level-pack/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/12929afb1a5c149f.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.CosmoGravity2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.CosmoGravity2-v0", "human_url": "http://publishers.spilgames.com/en/game/Cosmo-Gravity-2,576742227280291914", "regions": null, "height": 800, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/a10/cosmogravity/CosmoGravity3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WormHappy-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WormHappy-v0", "human_url": "http://www.gamesforwebsites.com/game/worm-happy", "regions": null, "height": 440, "width": 440, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/41269/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleSlasher-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleSlasher-v0", "human_url": "http://old.2pg.com/game/bubble-slasher/play/", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesforyourwebsite.org/games/Bubble-Slasher-2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Mushbooms-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Mushbooms-v0", "human_url": "http://www.gamesbutler.com/game/6823/mushbooms/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/mushbooms_locked_final.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.HexBattles-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HexBattles-v0", "human_url": "http://www.gamesbutler.com/game/4689/hex-battles/", "regions": null, "height": 420, "width": 630, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/117514eebc04e644.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PenguinHeroes-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.PenguinHeroes-v0", "human_url": "http://publishers.spilgames.com/en/game/Penguin-Heroes,576742227280285464", "regions": null, "height": 700, "width": 525, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/p/Penguin_heroes/Penguin_heroes.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleAdventures-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleAdventures-v0", "human_url": "http://www.games68.com/games.php?id=25254", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/game-1462796976.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CandyMatchCrush-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CandyMatchCrush-v0", "human_url": "http://www.gamesforwebsites.com/game/candy-match-crush", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/122671/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Coloruid-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Coloruid-v0", "human_url": "http://www.games68.com/games.php?id=5000010", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570c1619e0936.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TinyRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.TinyRacers-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/tiny-racers", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/24398/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Krome-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Krome-v0", "human_url": "http://www.games68.com/games.php?id=25461", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae352110953.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PolygonalFury-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PolygonalFury-v0", "human_url": "http://notdoppler.com/polygonalfury.php", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/polygonalfury.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PixelPurge-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PixelPurge-v0", "human_url": "http://armorgames.com/play/6804", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pixel-purge-6804.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Michimind-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Michimind-v0", "human_url": "http://www.gamesbutler.com/game/3387/michimind/", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/michimind.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MeerkatMission-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.MeerkatMission-v0", "human_url": "http://publishers.spilgames.com/en/game/Meerkat-Mission,576742227280285168", "regions": null, "height": 510, "width": 700, "extra_files": {"absolute": ["http://api.configar.org/cf/pb/1/settings/0/0/4a082ddc81a83df7a18309b48ed79a9a", "http://www8.agame.com/sdk/spilapi/localization/m/meerkat_mission_1306233434.swf"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/m/meerkat_mission/meerkat_mission.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TaxiInc-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TaxiInc-v0", "human_url": "http://www.games68.com/games.php?id=25631", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeb26a74d0.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HelicopsTerritories-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HelicopsTerritories-v0", "human_url": "http://armorgames.com/play/10251", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/helicops-territories-10251.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OldTv-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.OldTv-v0", "human_url": "http://www.gamesbutler.com/game/25283/old-tv/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/125701ab5119e33e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Conjure-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Conjure-v0", "human_url": "http://armorgames.com/play/11813", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/conjure-11813.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WaveLucha-v0": {"readme_header": null, "categories": ["2 player", "adventure", "our", "1 player", "2 players", "2pg", "chase", "chasing", "escape", "highscore", "pixelated", "runner", "running", "survive", "water", "wave"], "rewarder": true, "autostart": false, "id": "flashgames.WaveLucha-v0", "human_url": "http://old.2pg.com/game/wave-lucha/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/wavelucha2pg_1370988428.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl9-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl9-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SiegerRebuiltToDestroy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SiegerRebuiltToDestroy-v0", "human_url": "http://armorgames.com/play/16137", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/sieger-rebuilt-to-de-16137.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl15-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl15-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl7-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StarCars-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.StarCars-v0", "human_url": "http://www.y8.com/games/star_cars", "regions": null, "height": 450, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/102588/original/star_cars.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeliVsTower-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HeliVsTower-v0", "human_url": "http://armorgames.com/play/15296", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/heli-vs-tower-15296.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombiesAndDonuts-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ZombiesAndDonuts-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/zombies-and-donuts", "regions": null, "height": 500, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21830/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl10-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl10-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvolutionRacingLvl11-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.EvolutionRacingLvl11-v0", "human_url": "http://www.y8.com/games/evolution_racing", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/109879/original/evolution_racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LearnToFlyIdle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LearnToFlyIdle-v0", "human_url": "http://www.kongregate.com/games/light_bringer777/learn-to-fly-idle", "regions": null, "height": 540, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0020/3284/live/embeddable_203284.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WastelandSiege-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WastelandSiege-v0", "human_url": "http://www.games68.com/games.php?id=5000407", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570eccfb653d8.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GrandPrixGo2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.GrandPrixGo2-v0", "human_url": "http://notdoppler.com/grandprixgo2.php", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/grandprixgo2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NinjaPainter-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NinjaPainter-v0", "human_url": "http://publishers.spilgames.com/en/game/Ninja-Painter,576742227280284705", "regions": null, "height": 700, "width": 520, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/n/NinjaPainter/ninja_painter_agame_com.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VolleyBomb-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.VolleyBomb-v0", "human_url": "http://www.games68.com/games.php?id=25244", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae265e64dbd.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FireworksGame-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FireworksGame-v0", "human_url": "http://www.games68.com/games.php?id=25246", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2664b32b6.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stars-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Stars-v0", "human_url": "http://www.gamesforwebsites.com/game/stars", "regions": null, "height": 550, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/40105/game.swf?1398679265", "enable_internet": false, "serve_from_domain": null}, "flashgames.TowerEmpire2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TowerEmpire2-v0", "human_url": "http://1000webgames.com/play-9283-Tower-Empire-2.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/towerempire2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperCarRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.SuperCarRacing-v0", "human_url": "http://www.kongregate.com/games/fightclub69/super-car-racing", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "https://cdn1.kongcdn.com/game_icons/0056/9777/icon1_466x373.jpg?i10c=img.resize(width:171,height:137)", "enable_internet": false, "serve_from_domain": null}, "flashgames.PixelFighta-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PixelFighta-v0", "human_url": "http://armorgames.com/play/1267", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pixel-fighta-1267.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoverOrangeJourneyGangsters-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CoverOrangeJourneyGangsters-v0", "human_url": "http://www.games68.com/games.php?id=25255", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae26882fb9d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterCarsBridgesTrack-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.CoasterCarsBridgesTrack-v0", "human_url": "http://www.willinggames.com/racing-games/Coaster-Cars-Bridges-track.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash4/Coaster-Cars-Bridges-track.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Peakart-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.Peakart-v0", "human_url": "http://www.willinggames.com/racing-games/PeaKart.html", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash5/PeaKart.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MiceVsHammers-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MiceVsHammers-v0", "human_url": "http://old.2pg.com/game/mice-vs-hammers/play/", "regions": null, "height": 480, "width": 760, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/M/mice_vs_hammers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Filler-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Filler-v0", "human_url": "http://www.kongregate.com/games/SimianLogic/filler", "regions": null, "height": 527, "width": 620, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0000/5982/live/filler.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleHitHalloween-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleHitHalloween-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Hit:-Halloween,576742227280283659", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_hit_halloween/bubble_hit_halloween.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonsterTroubles-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MonsterTroubles-v0", "human_url": "http://www.gamesforwebsites.com/game/monster-troubles", "regions": null, "height": 500, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/38973/game.swf?1380622674", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlyingKiwi-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlyingKiwi-v0", "human_url": "http://www.gamesforwebsites.com/game/flying-kiwi", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24332/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MonkeyGoHappyNinjaHunt2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MonkeyGoHappyNinjaHunt2-v0", "human_url": "http://www.games68.com/games.php?id=25619", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575eeaeb7f9a9.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnakeClassic-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SnakeClassic-v0", "human_url": "http://www.gamesbutler.com/game/2257/snake-classic/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/snakeclassic.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PurifyTheLegendOfZ-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PurifyTheLegendOfZ-v0", "human_url": "http://www.games68.com/games.php?id=25371", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae30ca2af2f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GalleonFight-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GalleonFight-v0", "human_url": "http://1000webgames.com/play-7852-Galleon-Fight.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/galleon-fight100x100.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Numz-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Numz-v0", "human_url": "http://notdoppler.com/numz.php", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/numz.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BraveAstronaut-v0": {"readme_header": null, "categories": ["flash", "space", "purchase equipment upgrades", "killing", "collecting", "monsters", "planet", "free", "idnet", "idnet highscore", "idnet save", "idnet achievements"], "rewarder": false, "autostart": false, "id": "flashgames.BraveAstronaut-v0", "human_url": "http://www.y8.com/games/brave_astronaut", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/104502/original/brave_astronaut.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CupidBubbles-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.CupidBubbles-v0", "human_url": "http://publishers.spilgames.com/en/game/Cupid-Bubbles,576742227280290597", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/c/cupidbubbles/cupidbubbles.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UdderChaos-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.UdderChaos-v0", "human_url": "http://www.gamesbutler.com/game/3559/udder-chaos/", "regions": null, "height": 680, "width": 540, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/udderchaos.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DriveToWreck-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DriveToWreck-v0", "human_url": "http://1000webgames.com/play-8537-Drive-To-Wreck.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/drive-to-wreck.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dLaSupercars-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dLaSupercars-v0", "human_url": "http://www.cartitans.com/game/3d-la-supercars/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-la-supercars.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HelmetBombers3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HelmetBombers3-v0", "human_url": "http://publishers.spilgames.com/en/game/Helmet-Bombers-3,576742227280285497", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/h/Helmet_bombers3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Streetrace2Nitro-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.Streetrace2Nitro-v0", "human_url": "http://insanehero.com/mygames/StreetRace2/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/StreetRace2/streetrace2nitro.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Sheepy-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Sheepy-v0", "human_url": "http://armorgames.com/play/865", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/sheepy-865.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RoboPop-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.RoboPop-v0", "human_url": "http://www.miniclip.com/games/robo-pop/en/", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/robo-pop/en/robopop.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SneakyScubaEscape-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SneakyScubaEscape-v0", "human_url": "http://www.games68.com/games.php?id=25411", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae32a2be02d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FormulaRacerLvl8-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.FormulaRacerLvl8-v0", "human_url": "http://www.kongregate.com/games/turboNuke/formula-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://chat.kongregate.com/gamez/0010/9268/live/FormulaRacer.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchAroundTheWorld-v0": {"readme_header": null, "categories": [], "rewarder": true, "autostart": false, "id": "flashgames.MatchAroundTheWorld-v0", "human_url": "http://www.neongames.com/game/Match+around+the+World", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/matcharoundtheworld.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WhistleAndMice-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WhistleAndMice-v0", "human_url": "http://www.gamesforwebsites.com/game/whistle-and-mice", "regions": null, "height": 400, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23590/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Drifters-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.Drifters-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/drifters", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/38136/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DanceBattle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DanceBattle-v0", "human_url": "http://armorgames.com/play/6648", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/dance-battle-6648.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Flagman-v0": {"readme_header": null, "categories": ["noscore", "platform"], "rewarder": false, "autostart": false, "id": "flashgames.Flagman-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/flagman/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/flagman/flagman.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.GalacticCats-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.GalacticCats-v0", "human_url": "http://old.2pg.com/game/galactic-cats/play/", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/GalacticCats_1369856512.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SurvivorMissionD-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SurvivorMissionD-v0", "human_url": "http://www.games68.com/games.php?id=25259", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae26baca855.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl14-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl14-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DisasterWillStrikeDefender-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DisasterWillStrikeDefender-v0", "human_url": "http://www.games68.com/games.php?id=25377", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae3100edfb4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TechnoMania-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TechnoMania-v0", "human_url": "http://publishers.spilgames.com/en/game/Techno-Mania,576742227280285331", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/techno_mania/Techno_mania.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheGreatSiege-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheGreatSiege-v0", "human_url": "http://www.kongregate.com/games/ttback/the-great-siege", "regions": null, "height": 450, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0004/1580/live/embeddable_41580.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JamesThePirateZebra-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.JamesThePirateZebra-v0", "human_url": "http://armorgames.com/play/1606", "regions": null, "height": "", "width": "", "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/james-the-pirate-zeb-1606.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TokyoGuineaPop-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TokyoGuineaPop-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/tokyo-guinea-pop", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0014/7832/live/embeddable_147832.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperXtreme5MinuteShootEmUp-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperXtreme5MinuteShootEmUp-v0", "human_url": "http://armorgames.com/play/4274", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/super-xtreme-5-minut-4274.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SafariTime-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SafariTime-v0", "human_url": "http://publishers.spilgames.com/en/game/Safari-Time,576742227280284998", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/Safari_time/Safari_Time.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Hash-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Hash-v0", "human_url": "http://armorgames.com/play/4518", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/hash-4518.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ToonEscapeMaze-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ToonEscapeMaze-v0", "human_url": "http://www.games68.com/games.php?id=25496", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee84236575.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl11-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl11-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MindImpulse-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MindImpulse-v0", "human_url": "http://armorgames.com/play/1711", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/mind-impulse-1711.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CartoonCandy-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CartoonCandy-v0", "human_url": "http://www.gamesforwebsites.com/game/cartoon-candy", "regions": null, "height": 540, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130173/game.swf?1422975985", "enable_internet": false, "serve_from_domain": null}, "flashgames.FootballHeads201314Ligue1-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FootballHeads201314Ligue1-v0", "human_url": "http://www.games68.com/games.php?id=5000096", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cef2ccf8fe.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ManicRallyGo-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.ManicRallyGo-v0", "human_url": "http://armorgames.com/play/6002", "regions": null, "height": 480, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/manic-rally-go-6002.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TempleRunKnight-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TempleRunKnight-v0", "human_url": "http://www.gamesforwebsites.com/game/temple-run-knight", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/140691/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheBigEscape-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheBigEscape-v0", "human_url": "http://www.games68.com/games.php?id=25459", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae350825d15.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FindTheCandy3Kids-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.FindTheCandy3Kids-v0", "human_url": "http://www.games68.com/games.php?id=25391", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae317ac06a3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dTestDrive-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dTestDrive-v0", "human_url": "http://www.cartitans.com/game/3d-test-drive/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-test-drive.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StickBlender-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.StickBlender-v0", "human_url": "http://www.kongregate.com/games/EffingGames/stick-blender", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/8323/live/embeddable_128323.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CavemanEscape-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CavemanEscape-v0", "human_url": "http://www.gamesforwebsites.com/game/caveman-escape", "regions": null, "height": 600, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130136/game.swf?1422963134", "enable_internet": false, "serve_from_domain": null}, "flashgames.TaxiRacers-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.TaxiRacers-v0", "human_url": "http://www.y8.com/games/taxi_racers", "regions": null, "height": 349, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/92271/original/taxi_racers.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UnfreezeMe3-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.UnfreezeMe3-v0", "human_url": "http://publishers.spilgames.com/en/game/Unfreeze-Me-3,576742227280290645", "regions": null, "height": 640, "width": 440, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/u/Unfreeze_me3/UnfreezeMe3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IncrementalAcceleration-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IncrementalAcceleration-v0", "human_url": "http://www.games68.com/games.php?id=25301", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae2e2957165.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacerLvl5-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacerLvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/3927/live/embeddable_93927.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushFutureLvl10-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": false, "id": "flashgames.HeatRushFutureLvl10-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-future", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0019/7465/live/embeddable_197465.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KartOn-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.KartOn-v0", "human_url": "http://armorgames.com/play/5938", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/kart-on-5938.swf", "enable_internet": false, "serve_from_domain": "armorgames.com"}, "flashgames.Rb2-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Rb2-v0", "human_url": "http://www.gamesforwebsites.com/game/rb2", "regions": null, "height": 600, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/130629/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FishEatFish-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FishEatFish-v0", "human_url": "http://www.gamesforwebsites.com/game/fish-eat-fish", "regions": null, "height": 400, "width": 570, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/3019/game.swf?1438339548", "enable_internet": false, "serve_from_domain": null}, "flashgames.DartsSim-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.DartsSim-v0", "human_url": "http://www.gamesforwebsites.com/game/darts-sim", "regions": null, "height": 550, "width": 670, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/20523/game.swf?1272450300", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl3-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl3-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleRubbleTheIsland-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BubbleRubbleTheIsland-v0", "human_url": "http://www.games68.com/games.php?id=25439", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae33f8d98e4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeavenAndHell-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.HeavenAndHell-v0", "human_url": "http://publishers.spilgames.com/en/game/Heaven-and-Hell,576742227280284165", "regions": null, "height": 700, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/h/heaven_and_hell/Heaven_and_hell_agame.com.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LlamasInDistress-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LlamasInDistress-v0", "human_url": "http://www.games68.com/games.php?id=25256", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae268f48568.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Rocketeer-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Rocketeer-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/rocketeer", "regions": null, "height": 630, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21832/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JelliesFun-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.JelliesFun-v0", "human_url": "http://www.games68.com/games.php?id=25172", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25009b979.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dBuggyRacing-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.3dBuggyRacing-v0", "human_url": "http://www.willinggames.com/racing-games/3D-Buggy-Racing.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.willinggames.com/flash3/3D-Buggy-Racing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PyramidApocalypse-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PyramidApocalypse-v0", "human_url": "http://www.games68.com/games.php?id=25493", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee75a26c43.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WizkidEscape-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.WizkidEscape-v0", "human_url": "http://www.gamesforwebsites.com/game/wizkid-escape", "regions": null, "height": 480, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/127272/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SwimmingRace-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SwimmingRace-v0", "human_url": "http://www.gamesforwebsites.com/game/swimming-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22892/game.swf?1344258791", "enable_internet": false, "serve_from_domain": null}, "flashgames.MushboomsLevelPack2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MushboomsLevelPack2-v0", "human_url": "http://www.gamesbutler.com/game/9564/mushbooms-level-pack-2/", "regions": null, "height": 550, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/43861ff73fc7b692.swf?goto=norm", "enable_internet": false, "serve_from_domain": null}, "flashgames.Spectrum-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Spectrum-v0", "human_url": "http://armorgames.com/play/487", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/spectrum-487.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GravityThruster-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.GravityThruster-v0", "human_url": "http://www.gamesforwebsites.com/game/gravity-thruster", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/35269/game.swf?1366122188", "enable_internet": false, "serve_from_domain": null}, "flashgames.MagicSafari-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MagicSafari-v0", "human_url": "http://notdoppler.com/magicsafari.php", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/magicsafari.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ClimbingSanta-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ClimbingSanta-v0", "human_url": "http://www.gamesforwebsites.com/game/climbing-santa", "regions": null, "height": 500, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/23729/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ReleaseTheMooks2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ReleaseTheMooks2-v0", "human_url": "http://www.gamesbutler.com/game/5905/release-the-mooks-2/", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/1092a7ad22e7af81.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheThreeTowers-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.TheThreeTowers-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/the-three-towers", "regions": null, "height": 530, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/39201/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RushOfTanks-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RushOfTanks-v0", "human_url": "http://www.gamesbutler.com/game/26414/rush-of-tanks/", "regions": null, "height": 600, "width": 450, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/RushOfTanks_unlock.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HandsOff-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.HandsOff-v0", "human_url": "http://publishers.spilgames.com/en/game/Hands-Off,576742227280289760", "regions": null, "height": 525, "width": 700, "extra_files": {"absolute": ["http://api.configar.org/cf/pb/1/settings/0/0/7f8d20a2f3b0563f3c041ca93b5cc45d"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/h/hands_off.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl6-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl6-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WilliamTell-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.WilliamTell-v0", "human_url": "http://publishers.spilgames.com/en/game/William-Tell,576742227280285291", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/w/william_tell/williamtell_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Stratega-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Stratega-v0", "human_url": "http://armorgames.com/play/15993", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/stratega-15993.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BlackInk-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BlackInk-v0", "human_url": "http://www.games68.com/games.php?id=25420", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae32f790e21.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3FootNinjaIi-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.3FootNinjaIi-v0", "human_url": "http://www.miniclip.com/games/3-foot-ninja-ii/en/", "regions": null, "height": 420, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/3-foot-ninja-ii/en/3-foot-ninja-ii.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CitySiege3FubarLevelPack-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.CitySiege3FubarLevelPack-v0", "human_url": "http://www.games68.com/games.php?id=5000057", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570ce82ee5190.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TurtleBreak-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TurtleBreak-v0", "human_url": "http://www.y8.com/games/turtle_break", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/19685/original/turtle_break.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl10-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl10-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Velocity-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Velocity-v0", "human_url": "http://armorgames.com/play/343", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/velocity-343.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.JakeTheSnake-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.JakeTheSnake-v0", "human_url": "http://www.gamesforwebsites.com/game/jake-the-snake", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24031/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnakeFightArena-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SnakeFightArena-v0", "human_url": "http://old.2pg.com/game/snake-fight-arena/play/", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/Snakefight2pg_1363807257.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EctoHarvest-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": true, "autostart": false, "id": "flashgames.EctoHarvest-v0", "human_url": "http://publishers.spilgames.com/en/game/Ecto-Harvest,576742227280290232", "regions": [{"coordinates": [509, 210, 630, 54], "type": "noclick"}], "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/e/Ecto_harvest/EctoHarvest-spil-v1.02.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRace-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HunterForDismantlers-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.HunterForDismantlers-v0", "human_url": "http://www.games68.com/games.php?id=25209", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae25a39e611.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Basement-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Basement-v0", "human_url": "http://1000webgames.com/play-7411-Basement.html", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://1000webgames.com/arcade/basement.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SushiCatTheHoneymoon-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SushiCatTheHoneymoon-v0", "human_url": "http://armorgames.com/play/6301", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/sushi-cat-the-honeym-6301.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EscapeTheRedGiant-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.EscapeTheRedGiant-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/escapetheredgiant/", "regions": null, "height": 500, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/escapetheredgiant/escapetheredgiant.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.EpicDerbyRace-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.EpicDerbyRace-v0", "human_url": "http://www.cartitans.com/game/epic-derby-race/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/epic-derby-race.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Sheepster-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Sheepster-v0", "human_url": "http://www.gamesbutler.com/game/2444/sheepster/", "regions": null, "height": 500, "width": 650, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/Sheepster_final.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DaymareInvaders-v0": {"readme_header": null, "categories": ["1 player", "flash", "shooting", "arkanoid", "space invaders", "free", "default"], "rewarder": false, "autostart": false, "id": "flashgames.DaymareInvaders-v0", "human_url": "http://www.y8.com/games/daymare_invaders", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-storage/contents/39092/original/daymare_invaders.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SandcastleShowdown-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SandcastleShowdown-v0", "human_url": "http://www.gamesforwebsites.com/game/sandcastle-showdown", "regions": null, "height": 650, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/24208/game.swf?1360062134", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperShinyheadHarderThanFlappyBird-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SuperShinyheadHarderThanFlappyBird-v0", "human_url": "http://old.2pg.com/game/super-shinyhead/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/S/super-shiny-head-2pg.swf", "enable_internet": false, "serve_from_domain": null}, "internet.SlitherIOErmiyaEskandaryBot-v0": {"categories": ["mmo"], "height": 300, "width": 502, "server_timestep_limit": null, "tags": {}, "id": "internet.SlitherIOErmiyaEskandaryBot-v0", "rewarder": true, "url": "http://slither.io/", "enable_internet": true, "autostart": true}, "flashgames.DiscoverEurope-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.DiscoverEurope-v0", "human_url": "http://publishers.spilgames.com/en/game/Discover-Europe,576742227280285197", "regions": null, "height": 700, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/d/DiscoverEurope/DiscoverEurope-Family.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PixelQuest-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.PixelQuest-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/pixel-quest", "regions": null, "height": 530, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/21869/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Gemclix-v0": {"readme_header": null, "categories": [], "rewarder": true, "autostart": false, "id": "flashgames.Gemclix-v0", "human_url": "http://www.match3.com/match-3-games/gemclix.html", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://flash.match3.com/game_files/gemclixmatch3_mochirev4.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SwapTheDots-v0": {"readme_header": null, "categories": ["match-3", "fun"], "rewarder": true, "autostart": true, "id": "flashgames.SwapTheDots-v0", "human_url": "http://publishers.spilgames.com/en/game/Swap-The-Dots,576742227280291218", "regions": [{"coordinates": [618, 40, 84, 38], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/swap_the_dots/swapthedots.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Titanic-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Titanic-v0", "human_url": "http://publishers.spilgames.com/en/game/Titanic,576742227280285079", "regions": null, "height": 440, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/t/Titanic/titanic_agame.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Qoosh-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Qoosh-v0", "human_url": "http://publishers.spilgames.com/en/game/Qoosh,576742227280287823", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/q/Qoosh/Qoosh.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PixelBasher-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PixelBasher-v0", "human_url": "http://armorgames.com/play/5832", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/pixel-basher-5832.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TinyCastle-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.TinyCastle-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/tinycastle/", "regions": null, "height": 500, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/tinycastle/tinycastle.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.FlappyCopter-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlappyCopter-v0", "human_url": "http://www.gamesforwebsites.com/game/flappy-copter", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39602/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.LessQuick-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.LessQuick-v0", "human_url": "http://notdoppler.com/lessquick.php", "regions": null, "height": 516, "width": 654, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/lessquick.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HungryPiranha-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.HungryPiranha-v0", "human_url": "http://www.gamesforwebsites.com/game/hungry-piranha", "regions": null, "height": 450, "width": 550, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/12832/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.DeepFreeze-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.DeepFreeze-v0", "human_url": "http://www.miniclip.com/games/deep-freeze/en/", "regions": null, "height": 300, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/deep-freeze/en/deepfreeze.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombiesVsBrains-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ZombiesVsBrains-v0", "human_url": "http://www.games68.com/games.php?id=5002396", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/57bef5c024f0e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.UnderwaterSecrets-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": true, "autostart": true, "id": "flashgames.UnderwaterSecrets-v0", "human_url": "http://publishers.spilgames.com/en/game/Underwater-Secrets,576742227280292352", "regions": [{"coordinates": [616, 40, 84, 36], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/u/underwater_secrets/underwater_secrets_spil.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StickyNinjaMissions-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.StickyNinjaMissions-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/sticky-ninja-missions", "regions": null, "height": 512, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0021/0593/live/embeddable_210593.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SpacePunkRacerLvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.SpacePunkRacerLvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/space-punk-racer", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0012/4902/live/embeddable_124902.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.EvilMinion-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.EvilMinion-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/evil-minion", "regions": null, "height": 400, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/15172/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BoltThrough-v0": {"readme_header": null, "categories": ["2 player", "car", "our", "1 player", "2 players", "2playergames", "addicting", "distance", "featured", "flash game", "free", "fun", "highscore", "keyboard", "kids", "runner", "running"], "rewarder": true, "autostart": false, "id": "flashgames.BoltThrough-v0", "human_url": "http://old.2pg.com/game/bolt-through/play/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/boltthrough2pg.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PuzzleMonsters-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PuzzleMonsters-v0", "human_url": "http://www.kongregate.com/games/Edvent/puzzle-monsters", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/1446/live/embeddable_171446.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RunFaustoRun-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.RunFaustoRun-v0", "human_url": "http://www.gamesforwebsites.com/game/run-fausto-run", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/105078/game.swf?", "enable_internet": false, "serve_from_domain": null}, "flashgames.3FootNinja-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.3FootNinja-v0", "human_url": "http://www.miniclip.com/games/3-foot-ninja/en/", "regions": null, "height": 400, "width": 560, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/3-foot-ninja/en/3-foot-ninja.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.HeatRushUsaLvl12-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": true, "autostart": true, "id": "flashgames.HeatRushUsaLvl12-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/heat-rush-usa", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0017/9179/live/embeddable_179179.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RunRunRan-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.RunRunRan-v0", "human_url": "http://www.kongregate.com/games/artlogicgames/run-run-ran", "regions": null, "height": 600, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0016/8905/live/embeddable_168905.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Trizzle-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": true, "id": "flashgames.Trizzle-v0", "human_url": "http://www.arkadium.com/games/trizzle/", "regions": null, "height": 480, "width": 640, "extra_files": {"relative": {"files": ["config.xml", "data/trizzle_config.xml", "data/loggers/logger_config.xml", "data/loggers/gameplay-logger.swf", "branding.jpg", "data/assetpacks/engine.swf", "data/locale/en-US/strings.xml", "data/sounds/VO/Trizzle_titlecheer.mp3", "data/sounds/VO/doll_cheer_3.mp3", "data/sounds/VO/doll_cheer_4.mp3", "data/sounds/VO/doll_voice_1.mp3", "data/sounds/VO/doll_voice_10.mp3", "data/sounds/VO/doll_voice_2.mp3", "data/sounds/VO/doll_voice_3.mp3", "data/sounds/VO/doll_voice_4.mp3", "data/sounds/VO/doll_voice_5.mp3", "data/sounds/VO/doll_voice_6.mp3", "data/sounds/VO/doll_voice_7.mp3", "data/sounds/VO/doll_voice_8.mp3", "data/sounds/VO/doll_voice_9.mp3", "data/sounds/doll_big_complete.mp3", "data/sounds/doll_spin_out.mp3", "data/sounds/doll_tile_power_up.mp3", "data/sounds/help_panel_close.mp3", "data/sounds/help_panel_open.mp3", "data/sounds/level_up.mp3", "data/sounds/level_up_with_cutecheer.mp3", "data/sounds/moves_dec.mp3", "data/sounds/moves_inc.mp3", "data/sounds/nine_moves_left.mp3", "data/sounds/no_more_moves_panel_close.mp3", "data/sounds/no_more_moves_panel_open.mp3", "data/sounds/option_panel_close.mp3", "data/sounds/option_panel_open.mp3", "data/sounds/out_of_moves_placard.mp3", "data/sounds/quit_panel_close.mp3", "data/sounds/quit_panel_open.mp3", "data/sounds/stars_animation.mp3", "data/sounds/talkbubble_popup.mp3", "data/sounds/VO/doll_cheer_1.mp3", "data/sounds/VO/doll_cheer_2.mp3", "data/sounds/button_press.mp3", "data/sounds/button_rollover.mp3", "data/sounds/doll_close.mp3", "data/sounds/doll_drop.mp3", "data/sounds/doll_level_up_1.mp3", "data/sounds/doll_level_up_2.mp3", "data/sounds/doll_open.mp3", "data/sounds/doll_overmatch.mp3", "data/sounds/doll_pick.mp3", "data/sounds/ingame_music.mp3", "data/sounds/play_button_click.mp3", "data/sounds/titlescreen_music.mp3"], "base": "http://amsarkadium-a.akamaihd.net/assets/global/game/trizzle/9335dc40-4402-4b83-86c1-46b41c49ce64"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://amsarkadium-a.akamaihd.net/assets/global/game/trizzle/9335dc40-4402-4b83-86c1-46b41c49ce64/trizzle.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Xmatch2016-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Xmatch2016-v0", "human_url": "http://www.gamesforwebsites.com/game/xmatch-2016", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/135524/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperRally3d-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.SuperRally3d-v0", "human_url": "http://notdoppler.com/superrally3d.php", "regions": null, "height": 374, "width": 750, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://i.notdoppler.com/files/superrally3d.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleShooterChallenge-v0": {"readme_header": null, "categories": ["arcade", "1 player", "flash", "shooting", "bubbles", "matching", "free", "match 3", "idnet", "bubble shooter", "idnet highscore", "idnet achievements"], "rewarder": true, "autostart": true, "id": "flashgames.BubbleShooterChallenge-v0", "human_url": "http://www.y8.com/games/bubble_shooter_challenge", "regions": [{"coordinates": [670, 146, 222, 396], "type": "noclick"}, {"coordinates": [400, 418, 606, 78], "type": "noclick"}], "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/107990/original/bubble_shooter_challenge.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.PowerSwing-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PowerSwing-v0", "human_url": "http://www.gamesbutler.com/game/2268/power-swing/", "regions": null, "height": 444, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/powerswing.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnowQueen-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.SnowQueen-v0", "human_url": "http://www.neongames.com/game/Snow+Queen", "regions": null, "height": 600, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesonly.net/uploaded/swf/snowqueen.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ShimmyChute-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.ShimmyChute-v0", "human_url": "http://www.gamesforwebsites.com/game/shimmy-chute", "regions": null, "height": 800, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/129434/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Crazycle-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Crazycle-v0", "human_url": "http://www.games68.com/games.php?id=25402", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae31d7bb38c.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CaptainNutty-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.CaptainNutty-v0", "human_url": "http://www.gamesforwebsites.com/game/captain-nutty", "regions": null, "height": 530, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39009/game.swf?1415283677", "enable_internet": false, "serve_from_domain": null}, "flashgames.ViewtifulFightClub2-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ViewtifulFightClub2-v0", "human_url": "http://www.games68.com/games.php?id=25552", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/575ee9840404a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TheOneForkRestaurantDx-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TheOneForkRestaurantDx-v0", "human_url": "http://www.games68.com/games.php?id=25429", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae337ac0981.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TableTennisChallenge-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TableTennisChallenge-v0", "human_url": "http://www.y8.com/games/table_tennis_challenge", "regions": null, "height": 585, "width": 780, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://img-ak.y8.com/cloud/y8-game/contents/117550/original/table_tennis_challenge.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MinedigJourneyToHollowEarth-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MinedigJourneyToHollowEarth-v0", "human_url": "http://www.gamesforwebsites.com/game/minedig-journey-to-hollow-earth", "regions": null, "height": 550, "width": 350, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/137667/game.swf?1461944537", "enable_internet": false, "serve_from_domain": null}, "flashgames.MultiballMadness-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MultiballMadness-v0", "human_url": "http://armorgames.com/play/702", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/multiball-madness-702.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TumbleTiles-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": true, "id": "flashgames.TumbleTiles-v0", "human_url": "http://www.arkadium.com/games/tumble-tiles/", "regions": null, "height": 480, "width": 640, "extra_files": {"relative": {"files": ["assets/sounds/HitDestructibleTile.mp3", "assets/sounds/TilePreDamage.mp3", "assets/sounds/TileUniced.mp3", "assets/sounds/TileBounce_3.mp3", "assets/sounds/levelCompleteAppear.mp3", "assets/sounds/GoalBannerDisappear.mp3", "assets/sounds/PowerupAdjacentOrBombComboDouble.mp3", "assets/config/levels.xml", "assets/sounds/bombStartFly.mp3", "assets/sounds/TileBounce_0.mp3", "assets/data/locale/it-IT/strings.xml", "assets/sounds/blackHoleCatch.mp3", "assets/sounds/GainStar1.mp3", "assets/sounds/TileClick.mp3", "assets/sounds/PowerupWindup.mp3", "assets/sounds/PowerupAdjacent.mp3", "assetsPlatformSpecific/config/assets.xml", "assets/sounds/subgoalCompleted.mp3", "assets/sounds/tileRotationCompleted.mp3", "assets/sounds/PowerupMeterFilled.mp3", "assets/data/locale/de-DE/strings.xml", "assets/sounds/BaddieGrunt1.mp3", "assets/sounds/TileBounce_2.mp3", "assets/sounds/GoalBannerAppear.mp3", "assets/config/assets.xml", "assets/sounds/magicGem.mp3", "assets/sounds/PowerupPartyBombExplosion1.mp3", "assets/sounds/TileBounce_1.mp3", "assets/sounds/TilePop2.mp3", "assets/sounds/PowerupColorClear.mp3", "assets/sounds/GainStar2.mp3", "assets/sounds/TileIced.mp3", "assets/sounds/PowerupAdjacentOrBombComboSingle.mp3", "assets/sounds/backgroundMusic.mp3", "assets/config/sounds.xml", "assets/sounds/TileBounce_0.mp3", "assets/data/locale/en-US/strings.xml", "assets/sounds/PowerupColRow.mp3", "assets/sounds/Success.mp3", "assets/sounds/HitGlassSpace.mp3", "assets/sounds/ButtonClick.mp3", "assets/sounds/PowerupBombExplosion.mp3", "assets/sounds/TilePop1.mp3", "assets/sounds/PowerupBombTrigger.mp3", "assets/sounds/TileUncaged.mp3", "assets/data/locale/fr-FR/strings.xml", "assets/sounds/TilePop3.mp3", "assets/data/locale/es-ES/strings.xml", "assets/sounds/GainStar3.mp3", "assetsPlatformSpecific/textures/1x/background.jpg", "assetsPlatformSpecific/textures/1x/fonts/berlin-sans-demi-small.xml", "assetsPlatformSpecific/textures/1x/fonts/berlin-sans-demi-white.xml", "assetsPlatformSpecific/textures/1x/fonts/berlin-sans-demi-yellow.xml", "assetsPlatformSpecific/textures/1x/fonts/berlin-sans-demi.xml", "assetsPlatformSpecific/textures/1x/fonts/level-numbers-white.xml", "assetsPlatformSpecific/textures/1x/fonts/level-numbers-yellow.xml", "assetsPlatformSpecific/textures/1x/fontsatlas.png", "assetsPlatformSpecific/textures/1x/fontsatlas.xml", "assetsPlatformSpecific/textures/1x/gameplayatlas.png", "assetsPlatformSpecific/textures/1x/gameplayatlas.xml", "assetsPlatformSpecific/textures/1x/gui/ConnectionProposition.xml", "assetsPlatformSpecific/textures/1x/gui/buyCoinsControl.xml", "assetsPlatformSpecific/textures/1x/gui/goal.xml", "assetsPlatformSpecific/textures/1x/gui/intro_screen.xml", "assetsPlatformSpecific/textures/1x/gui/level_completed.xml", "assetsPlatformSpecific/textures/1x/gui/level_failed.xml", "assetsPlatformSpecific/textures/1x/gui/pause_popup.xml", "assetsPlatformSpecific/textures/1x/introscreenatlas.png", "assetsPlatformSpecific/textures/1x/introscreenatlas.xml", "assetsPlatformSpecific/textures/1x/introscreenbackgroundatlas.jpg", "assetsPlatformSpecific/textures/1x/introscreenbackgroundatlas.xml", "assetsPlatformSpecific/textures/1x/levelcompleteatlas.png", "assetsPlatformSpecific/textures/1x/levelcompleteatlas.xml", "assetsPlatformSpecific/textures/1x/levelselectatlas.png", "assetsPlatformSpecific/textures/1x/levelselectatlas.xml", "assetsPlatformSpecific/textures/1x/particles/black-hole-prediction.xml", "assetsPlatformSpecific/textures/1x/particles/chain-break.xml", "assetsPlatformSpecific/textures/1x/particles/gem-break.xml", "assetsPlatformSpecific/textures/1x/particles/glass-break.xml", "assetsPlatformSpecific/textures/1x/particles/intro-screen-idle.xml", "assetsPlatformSpecific/textures/1x/particles/liana-break.xml", "assetsPlatformSpecific/textures/1x/particles/mask-break.xml", "assetsPlatformSpecific/textures/1x/particles/powerup-trail.xml", "assetsPlatformSpecific/textures/1x/particles/stars.xml", "assetsPlatformSpecific/textures/1x/particles/stars_big.xml", "assetsPlatformSpecific/textures/1x/particles/stone-break.xml", "assetsPlatformSpecific/textures/1x/particles/tile-break-blue.xml", "assetsPlatformSpecific/textures/1x/particles/tile-break-green.xml", "assetsPlatformSpecific/textures/1x/particles/tile-break-purple.xml", "assetsPlatformSpecific/textures/1x/particles/tile-break-red.xml", "assetsPlatformSpecific/textures/1x/particles/tile-break-yellow.xml", "assetsPlatformSpecific/textures/1x/powerup-bar-blue.png", "assetsPlatformSpecific/textures/1x/powerup-bar-green.png", "assetsPlatformSpecific/textures/1x/powerup-bar-mask.png", "assetsPlatformSpecific/textures/1x/powerup-bar-purple.png", "assetsPlatformSpecific/textures/1x/powerup-bar-red.png", "assetsPlatformSpecific/textures/1x/powerup-bar-yellow.png", "assetsPlatformSpecific/textures/1x/powerup-sparkle-guide.png"], "base": "http://amsarkadium-a.akamaihd.net/assets/arena-4/game/tumble-tiles/05b8d9cf-8607-4876-a95f-8d48eeee68bf"}}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://amsarkadium-a.akamaihd.net/assets/arena-4/game/tumble-tiles/05b8d9cf-8607-4876-a95f-8d48eeee68bf/tumble-tiles.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SuperRallyChallenge2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.SuperRallyChallenge2-v0", "human_url": "http://insanehero.com/mygames/SuperRallyChallenge2/index.html", "regions": null, "height": 360, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://insanehero.com/mygames/SuperRallyChallenge2/superrallychallenge2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Offroaders2-v0": {"readme_header": null, "categories": ["sports"], "rewarder": false, "autostart": false, "id": "flashgames.Offroaders2-v0", "human_url": "http://notdoppler.com/offroaders2.php", "regions": null, "height": 550, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/offroaders2.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Commando2-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Commando2-v0", "human_url": "http://www.miniclip.com/games/commando-2/en/", "regions": null, "height": 400, "width": 590, "extra_files": {"absolute": ["http://www.miniclip.com/swfcontent/components/gatekeeper_as2.swf", "http://www.miniclip.com/swfcontent/components/game_offsite_as2.swf"]}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.miniclip.com/games/commando-2/en/commando2.swf", "enable_internet": false, "serve_from_domain": "www.miniclip.com"}, "flashgames.Xnake-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Xnake-v0", "human_url": "http://armorgames.com/play/780", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/xnake-780.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BusinessmanSimulator-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BusinessmanSimulator-v0", "human_url": "http://www.gamesbutler.com/game/25668/businessman-simulator/", "regions": null, "height": 540, "width": 960, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/04bd1eaf1fd74969.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.KnightsOfRock-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.KnightsOfRock-v0", "human_url": "http://armorgames.com/play/45", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/knights-of-rock-45.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZodiacMatch-v0": {"readme_header": null, "categories": ["match-3", "puzzle"], "rewarder": false, "autostart": false, "id": "flashgames.ZodiacMatch-v0", "human_url": "http://publishers.spilgames.com/en/game/Zodiac-Match,576742227280286626", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/z/Zodiac_match/ZodiacMatch.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dSuperRide-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": true, "id": "flashgames.3dSuperRide-v0", "human_url": "http://www.cartitans.com/game/3d-super-ride/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-super-ride.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlyPlane-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlyPlane-v0", "human_url": "http://www.gamesforwebsites.com/game/fly-plane", "regions": null, "height": 300, "width": 500, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/222/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.WindowShooter-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.WindowShooter-v0", "human_url": "http://www.games68.com/games.php?id=5000171", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c145137f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.MatchMonsters-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.MatchMonsters-v0", "human_url": "http://www.gamesforwebsites.com/game/match-monsters", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/22318/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.3dRookieCop-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.3dRookieCop-v0", "human_url": "http://www.cartitans.com/game/3d-rookie-cop/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://www.cartitans.com/download/3d-rookie-cop.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ZombieMatch3-v0": {"readme_header": null, "categories": ["match 3"], "rewarder": true, "autostart": false, "id": "flashgames.ZombieMatch3-v0", "human_url": "http://www.smileygamer.com/ourgames/zombiematch3.html", "regions": [{"coordinates": [16, 136, 477, 73], "type": "noclick"}], "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.smileygamer.com/ourgames/zombiematch3.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRaceLvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.NeonRaceLvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0009/7872/live/embeddable_97872.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AdrenalineChaser-v0": {"readme_header": null, "categories": ["racing"], "rewarder": false, "autostart": false, "id": "flashgames.AdrenalineChaser-v0", "human_url": "http://www.cartitans.com/game/adrenaline-chaser/", "regions": null, "height": 400, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "birdseye"}, "title": null, "swf_url": "http://www.cartitans.com/download/adrenaline-chaser.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Jumpz-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.Jumpz-v0", "human_url": "http://publishers.spilgames.com/en/game/Jumpz,576742227280285289", "regions": null, "height": 640, "width": 480, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/j/Jumpz/Jumpz.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl12-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl12-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.GoGreenGo-v0": {"readme_header": null, "categories": ["platform"], "rewarder": false, "autostart": false, "id": "flashgames.GoGreenGo-v0", "human_url": "http://www.freegamesforyourwebsite.com/game/go-green-go", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cdn.freeonlinegames.com/games/4111/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2Lvl7-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2Lvl7-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.StormRage-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.StormRage-v0", "human_url": "http://www.gamesforwebsites.com/game/storm-rage", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/15028/game.swf?1437476679", "enable_internet": false, "serve_from_domain": null}, "flashgames.PouThanksgivingDaySlacking-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.PouThanksgivingDaySlacking-v0", "human_url": "http://www.games68.com/games.php?id=25204", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae257b08b3e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BearInSuperActionAdventure-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.BearInSuperActionAdventure-v0", "human_url": "http://armorgames.com/play/17755", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/bear-in-super-action-17755.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IceBlock-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.IceBlock-v0", "human_url": "http://publishers.spilgames.com/en/game/Ice-Block,576742227280289227", "regions": null, "height": 510, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/i/Ice-Block/Ice_Block_v1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Match2Collapse-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.Match2Collapse-v0", "human_url": "http://www.gamesforwebsites.com/game/match-2-collapse", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/131631/game.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.VolcanoPanicInIsland-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.VolcanoPanicInIsland-v0", "human_url": "http://www.games68.com/games.php?id=25445", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/56ae34446277e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.BubbleHit-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": true, "autostart": false, "id": "flashgames.BubbleHit-v0", "human_url": "http://publishers.spilgames.com/en/game/Bubble-Hit,576742227280283380", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/games/flash/b/bubble_hit/bubble_hit.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.RobotWantsFishy-v0": {"readme_header": null, "categories": ["sparsereward"], "rewarder": false, "autostart": false, "id": "flashgames.RobotWantsFishy-v0", "human_url": "http://www.andkon.com/arcade/adventureaction/robotwantsfishy/", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.andkon.com/arcade/adventureaction/robotwantsfishy/robotwantsfishy.swf", "enable_internet": false, "serve_from_domain": "andkon.com"}, "flashgames.ToyWarAngryRobotDog-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ToyWarAngryRobotDog-v0", "human_url": "http://www.games68.com/games.php?id=5000192", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e73468701f.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NoughtsAndCrosses-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.NoughtsAndCrosses-v0", "human_url": "http://old.2pg.com/game/noughts-and-crosses/play/", "regions": null, "height": 525, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://old.2pg.com/wp-content/games/custom/N/noughtsandcrosses1.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.TrickOrToad-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.TrickOrToad-v0", "human_url": "http://armorgames.com/play/1382", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/trick-or-toad-1382.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.YummyyummyMonsterShooter-v0": {"readme_header": null, "categories": ["jewel"], "rewarder": false, "autostart": false, "id": "flashgames.YummyyummyMonsterShooter-v0", "human_url": "http://publishers.spilgames.com/en/game/Yummy-Yummy-Monster-Shooter,576742227280286880", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/y/Yummy_yummy_monster/yummy_yummy_monster_shooter.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnackOnLittleCreatures-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SnackOnLittleCreatures-v0", "human_url": "http://www.games68.com/games.php?id=5000180", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570e1c5c9550a.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.IntoSpace-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.IntoSpace-v0", "human_url": "http://notdoppler.com/intospace.php", "regions": null, "height": 500, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://i.notdoppler.com/files/intospace.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.Chefday-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.Chefday-v0", "human_url": "http://www.games68.com/games.php?id=5000134", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/570cf7b8e7c94.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.FlyingTest-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.FlyingTest-v0", "human_url": "http://www.gamesforwebsites.com/game/flying-test", "regions": null, "height": 600, "width": 800, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/134912/game.swf?1448885357", "enable_internet": false, "serve_from_domain": null}, "flashgames.MusicZap-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.MusicZap-v0", "human_url": "http://armorgames.com/play/1364", "regions": null, "height": 450, "width": 600, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://cache.armorgames.com/files/games/music-zap-1364.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SnowPrincessMakeup-v0": {"readme_header": null, "categories": ["Tower Defense"], "rewarder": false, "autostart": false, "id": "flashgames.SnowPrincessMakeup-v0", "human_url": "http://publishers.spilgames.com/en/game/Snow-Princess-Make-Up,576742227280284508", "regions": null, "height": 513, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www8.agame.com/mirror/flash/s/SnowPrincessMakeUp/snow_princess_make_up.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.CoasterRacer2-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": true, "id": "flashgames.CoasterRacer2-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/coaster-racer-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0011/8219/live/embeddable_118219.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.NeonRace2Lvl5-v0": {"readme_header": null, "categories": ["racing"], "rewarder": true, "autostart": false, "id": "flashgames.NeonRace2Lvl5-v0", "human_url": "http://www.kongregate.com/games/LongAnimals/neon-race-2", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {"perspective": "driver"}, "title": null, "swf_url": "http://external.kongregate-games.com/gamez/0013/7834/live/embeddable_137834.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.ReleaseTheMooks-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.ReleaseTheMooks-v0", "human_url": "http://www.gamesbutler.com/game/3635/release-the-mooks/", "regions": null, "height": 500, "width": 700, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/relesethemooks.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.OswaldTheAngryDwarf-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.OswaldTheAngryDwarf-v0", "human_url": "http://www.games68.com/games.php?id=25210", "regions": null, "height": 480, "width": 640, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.games68.com/games/game-1462009415.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.SantaClimbHere-v0": {"readme_header": null, "categories": ["Assorted"], "rewarder": false, "autostart": false, "id": "flashgames.SantaClimbHere-v0", "human_url": "http://www.gamesbutler.com/game/27121/santa-climb-here/", "regions": null, "height": 525, "width": 400, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.gamesbutler.com/files/660b25a543ac979e.swf", "enable_internet": false, "serve_from_domain": null}, "flashgames.AntsGlider-v0": {"readme_header": null, "categories": [], "rewarder": false, "autostart": false, "id": "flashgames.AntsGlider-v0", "human_url": "http://www.gamesforwebsites.com/game/ants-glider", "regions": null, "height": 480, "width": 720, "extra_files": {}, "server_timestep_limit": null, "tags": {}, "title": null, "swf_url": "http://www.freeonlinegames.com/games/39883/game.swf", "enable_internet": false, "serve_from_domain": null}}
================================================
FILE: universe/runtimes/registration.py
================================================
import collections
import json
import six
from gym import error
class UnregisteredRuntime(error.Unregistered):
"""Raised when the user requests a runtime from the registry that does
not actually exist.
"""
pass
class DockerRuntime(object):
"""Lightweight struct for our DockerImage configuration"""
def __init__(self, id=id, image=None, command=None, host_config=None, default_params=None, server_registry_file=None):
"""
Args:
id: The short identifier for this runtime
image: The full docker image name including a tag
command: A list of commands to be passed to docker
host_config: A dict that will be fed to docker.Client().create_host_config
default_params: The default parameter values for this environment
server_registry: A file containing a JSON dump of the server registry. The format will be runtime-specific.
"""
self.id = id
self.image = image
self.command = command or []
self.host_config = host_config or {}
self.default_params = default_params or {}
self._server_registry = None
self._server_registry_file = server_registry_file
@property
def server_registry(self):
if self._server_registry is None:
with open(self._server_registry_file) as f:
self._server_registry = json.load(f)
return self._server_registry
@property
def _cli_flags(self):
# Not everything maps in a straightforward way, e.g. cap_add => '--cap-add' but ipc_mode => '--ipc
api_to_cli = {
'ipc_mode': 'ipc'
}
cli_flags = []
for api_key, api_value in self.host_config.items():
if isinstance(api_value, (six.string_types, bool)):
cli_values = [api_value]
else:
cli_values = api_value
for cli_value in cli_values:
if api_key in api_to_cli:
api_key = api_to_cli[api_key]
cli_flag = '--{}'.format(api_key.replace('_', '-'))
if isinstance(cli_value, bool):
# boolean flag, like --privileged
cli_flags += [cli_flag]
else:
cli_flags += [cli_flag, cli_value]
return cli_flags
def cli_command(self, vnc_port, rewarder_port, extra_flags=[]):
return ['docker', 'run',
'-p', '{}:5900'.format(vnc_port),
'-p', '{}:15900'.format(rewarder_port)] + \
extra_flags + \
self._cli_flags + \
[self.image] + self.command
class WindowsRuntime(object):
# TODO: Spawn windows runtimes (right now managed manually)
def __init__(self, id=id, default_params=None):
"""
Args:
id: The short identifier for this runtime
"""
self.id = id
self.default_params = default_params
class Registry(object):
def __init__(self):
self.runtimes = collections.OrderedDict()
def register_runtime(self, id, kind, **kwargs):
if kind == "docker":
self.runtimes[id] = DockerRuntime(id, **kwargs)
elif kind == "windows":
self.runtimes[id] = WindowsRuntime(id, **kwargs)
else:
raise error.Error("No runtime of kind {} . \n Valid options are ['docker']".format(kind))
def runtime_spec(self, id):
"""
id is a string describing the runtime, e.g 'flashgames
Returns a configured DockerRuntime object
"""
try:
return self.runtimes[id]
except KeyError:
raise UnregisteredRuntime('No registered runtime with name: {}'.format(id))
registry = Registry()
register_runtime = registry.register_runtime
runtime_spec = registry.runtime_spec
================================================
FILE: universe/runtimes.yml
================================================
flashgames:
tag: 0.20.28
gym-core:
tag: 0.20.6
world-of-bits:
tag: 0.20.0
================================================
FILE: universe/scoreboard/__init__.py
================================================
from gym.benchmarks import scoring
from gym.benchmarks import register_benchmark
register_benchmark(
id='Atari7VNC-v0',
scorer=scoring.TotalReward(),
name='AtariVNC',
description='7 Atari games, with pixel observations (using universe)',
tasks=[
{
"env_id": "VNCBeamRider-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "VNCBreakout-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "VNCEnduro-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "gym-core.Pong-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "VNCQbert-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "VNCSeaquest-v3",
"trials": 1,
"max_timesteps": 10000000
},
{
"env_id": "VNCSpaceInvaders-v3",
"trials": 1,
"max_timesteps": 10000000
}
])
register_benchmark(
id='FlashRacing-v0',
scorer=scoring.RewardPerTime(),
name='FlashRacing',
description='7 flash racing games, goal is best score per time',
tasks=[
{'env_id': 'flashgames.NeonRace-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 175.0,
'reward_ceiling': 1700.0,
},
{'env_id': 'flashgames.CoasterRacer-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 17.0,
'reward_ceiling': 400.0,
},
{'env_id': 'flashgames.HeatRushUsa-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 150.0,
'reward_ceiling': 700.0,
},
{'env_id': 'flashgames.FormulaRacer-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 0.27,
'reward_ceiling': 1.0,
},
{'env_id': 'flashgames.DuskDrive-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 5000.0,
'reward_ceiling': 15000.0,
},
{'env_id': 'flashgames.SpacePunkRacer-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 0.67,
'reward_ceiling': 2.25,
},
{'env_id': 'flashgames.NeonRace2-v0',
'trials': 1,
'max_timesteps': 5000000,
'reward_floor': 0.0,
'reward_ceiling': 1200.0,
}
])
================================================
FILE: universe/spaces/__init__.py
================================================
from universe.spaces.hardcoded import Hardcoded
from universe.spaces.vnc_action_space import VNCActionSpace
from universe.spaces.vnc_event import VNCEvent, KeyEvent, PointerEvent
from universe.spaces.vnc_observation_space import VNCObservationSpace
from universe.spaces.diagnostics import PeekReward
================================================
FILE: universe/spaces/diagnostics.py
================================================
class DiagnosticEvent(object):
pass
PeekReward = DiagnosticEvent()
================================================
FILE: universe/spaces/hardcoded.py
================================================
from gym.spaces import prng
class Hardcoded(object):
def __init__(self, actions):
self.actions = actions
def contains(self, action):
return action in self.actions
def sample(self):
i = prng.np_random.randint(len(self.actions))
return self.actions[i]
def __getitem__(self, i):
return self.actions[i]
================================================
FILE: universe/spaces/joystick_action_space.py
================================================
import gym
from gym.spaces import Box
from universe.spaces import joystick_event
from gym.spaces import prng
from collections import OrderedDict
class JoystickActionSpace(gym.Space):
"""
Programmable joystick - currently Windows-only => mapped to vJoy
"""
def __init__(self, axis_x=False, axis_y=False, axis_z=False, axis_rx=False, axis_ry=False, axis_rz=False,
slider_0=False, slider_1=False):
self.event_space_map = OrderedDict()
if axis_x:
self.axis_x = box_axis()
self.event_space_map[joystick_event.JoystickAxisXEvent] = self.axis_x
if axis_y:
self.axis_y = box_axis()
self.event_space_map[joystick_event.JoystickAxisYEvent] = self.axis_y
if axis_z:
self.axis_z = box_axis()
self.event_space_map[joystick_event.JoystickAxisZEvent] = self.axis_z
if axis_rx:
self.axis_rx = box_axis()
self.event_space_map[joystick_event.JoystickAxisRxEvent] = self.axis_rx
if axis_ry:
self.axis_ry = box_axis()
self.event_space_map[joystick_event.JoystickAxisRyEvent] = self.axis_ry
if axis_rz:
self.axis_rz = box_axis()
self.event_space_map[joystick_event.JoystickAxisRzEvent] = self.axis_rz
if slider_0:
self.slider_0 = box_axis()
self.event_space_map[joystick_event.JoystickSlider0Event] = self.slider_0
if slider_1:
self.slider_1 = box_axis()
self.event_space_map[joystick_event.JoystickSlider1Event] = self.slider_1
# TODO: Add buttons (similar to a vnc_event.KeyEvent - but 1..32)
# TODO: Add POV hats
def contains(self, action):
if not isinstance(action, list):
return False
for a in action:
if isinstance(a, joystick_event.JoystickAxisEvent):
axis = self.event_space_map[a]
if not axis.contains(a):
return False
return True
def sample(self):
event_type_index = prng.np_random.randint(len(self.event_space_map))
event_type = list(self.event_space_map.keys())[event_type_index]
if event_type.__bases__[0] == joystick_event.JoystickAxisEvent:
event = [event_type(self.event_space_map[event_type].sample()[0])]
else:
raise JoystickActionSpaceException('Unexpected event type')
return event
class JoystickActionSpaceException(Exception):
pass
def box_axis():
return Box(-1.0, 1.0, shape=(1,))
================================================
FILE: universe/spaces/joystick_event.py
================================================
class JoystickEvent(object):
pass
class JoystickAxisEvent(JoystickEvent):
def __init__(self, amount):
self.amount = float(amount)
def __repr__(self):
return str(type(self)) + ''.format(self.amount)
def __str__(self):
return repr(self)
def __hash__(self):
return self.amount.__hash__()
def __eq__(self, other):
return type(other) == type(self) and \
other.amount == self.amount
def compile(self):
return type(self).__name__, self.amount
class JoystickAxisXEvent(JoystickAxisEvent):
pass
class JoystickAxisYEvent(JoystickAxisEvent):
pass
class JoystickAxisZEvent(JoystickAxisEvent):
pass
class JoystickAxisRxEvent(JoystickAxisEvent):
pass
class JoystickAxisRyEvent(JoystickAxisEvent):
pass
class JoystickAxisRzEvent(JoystickAxisEvent):
pass
class JoystickSlider0Event(JoystickAxisEvent):
pass
class JoystickSlider1Event(JoystickAxisEvent):
pass
================================================
FILE: universe/spaces/vnc_action_space.py
================================================
import gym
import string
from gym.spaces import prng
from universe.vncdriver import constants
from universe.spaces import vnc_event
class VNCActionSpace(gym.Space):
"""The space of VNC actions.
You can submit a list of KeyEvents or PointerEvents. KeyEvents
correspond to pressing or releasing a key. PointerEvents correspond
to moving to a specific pixel, and setting the mouse buttons to some state
(buttonmask is a bitmap corresponding to which buttons are down).
Note that key releases work differently from click releases: keys
are stateful and must be explicitly released, while the state of
the mouse buttons is provided at each timestep, so you have to
explicitly keep the mouse down.
Attributes:
keys (list): The allowed key presses
buttonmasks (list): The allowed buttonmasks (i.e. mouse presses)
screen_shape (int, int): The X and Y dimensions of the screen
"""
def __init__(self, keys=None, buttonmasks=None, screen_shape=(1024, 728)):
self.keys = []
if keys is None:
keys = [c for c in string.printable] + list(constants.KEYMAP.keys())
for key in (keys or []):
down = vnc_event.KeyEvent.by_name(key, down=True)
up = vnc_event.KeyEvent.by_name(key, down=False)
self.keys.append(down)
self.keys.append(up)
self._key_set = set(self.keys)
self.screen_shape = screen_shape
if self.screen_shape is not None:
self.buttonmasks = []
if buttonmasks is None:
buttonmasks = range(256)
for buttonmask in buttonmasks:
self.buttonmasks.append(buttonmask)
self._buttonmask_set = set(self.buttonmasks)
def contains(self, action):
if not isinstance(action, list):
return False
for a in action:
if isinstance(a, vnc_event.KeyEvent):
if a not in self._key_set:
return False
elif isinstance(a, vnc_event.PointerEvent):
if self.screen_shape is None:
return False
if a.x < 0 or a.x > self.screen_shape[0]:
return False
elif a.y < 0 or a.y > self.screen_shape[1]:
return False
elif a.buttonmask not in self._buttonmask_set:
return False
return True
def sample(self):
# Both key and pointer allowed
if self.screen_shape is not None:
event_type = prng.np_random.randint(2)
else:
event_type = 0
if event_type == 0:
# Let's press a key
key = prng.np_random.choice(self.keys)
event = [key]
else:
x = prng.np_random.randint(self.screen_shape[0])
y = prng.np_random.randint(self.screen_shape[1])
buttonmask = prng.np_random.choice(self.buttonmasks)
event = [vnc_event.PointerEvent(x, y, buttonmask)]
return event
================================================
FILE: universe/spaces/vnc_event.py
================================================
import string
from universe import error
from universe.vncdriver import constants
class VNCEvent(object):
pass
def keycode(key):
if key in constants.KEYMAP:
return constants.KEYMAP.get(key)
elif len(key) == 1:
return ord(key)
else:
raise error.Error('Not sure how to translate to keycode: {!r}'.format(key))
class KeyEvent(VNCEvent):
_keysym_to_name = {}
for key, value in constants.KEYMAP.items():
_keysym_to_name[value] = key
for c in string.printable:
_keysym_to_name[ord(c)] = c
@classmethod
def build(cls, keys, down=None):
"""Build a key combination, such as:
ctrl-t
"""
codes = []
for key in keys.split('-'):
key = keycode(key)
codes.append(key)
events = []
if down is None or down:
for code in codes:
events.append(cls(code, down=True))
if down is None or not down:
for code in reversed(codes):
events.append(cls(code, down=False))
return events
@classmethod
def by_name(cls, key, down=None):
return cls(keycode(key), down=down)
def __init__(self, key, down=True):
# TODO: validate key
self.key = key
self.down = bool(down)
def compile(self):
return 'KeyEvent', self.key, self.down
def __repr__(self):
if self.down:
direction = 'down'
else:
direction = 'up'
name = self._keysym_to_name.get(self.key)
if not name:
name = '0x{:x}'.format(self.key)
else:
name = '{} (0x{:x})'.format(name, self.key)
return 'KeyEvent'.format(name, direction)
def __str__(self):
return repr(self)
def __hash__(self):
return (self.key, self.down).__hash__()
def __eq__(self, other):
return type(other) == type(self) and \
other.key == self.key and \
other.down == self.down
@property
def key_name(self):
"""Human readable name"""
return self._keysym_to_name.get(self.key)
class PointerEvent(VNCEvent):
def __init__(self, x, y, buttonmask=0):
self.x = x
self.y = y
self.buttonmask = buttonmask
def compile(self):
return 'PointerEvent', self.x, self.y, self.buttonmask
def __repr__(self):
return 'PointerEvent'.format(self.x, self.y, self.buttonmask)
def __str__(self):
return repr(self)
================================================
FILE: universe/spaces/vnc_observation_space.py
================================================
import gym
class VNCObservationSpace(gym.Space):
# For now, we leave the VNC ObservationSpace wide open, since
# there isn't much use-case for this object.
def contains(self, x):
return True
================================================
FILE: universe/twisty.py
================================================
import threading
from twisted.python.runtime import platform
# On OSX, we should use kqueue rather than the default select
# backend. (Proximal issue is that select only can handle a limited
# number of file descriptors.)
#
# Based off twisted.internet.default
def _get_reactor(platform):
try:
if platform.isLinux():
try:
from twisted.internet import epollreactor
cls = epollreactor.EPollReactor
except ImportError:
from twisted.internet import pollreactor
cls = pollreactor.PollReactor
elif platform.isMacOSX():
from twisted.internet import kqreactor
cls = kqreactor.KQueueReactor
elif platform.getType() == 'posix' and not platform.isMacOSX():
from twisted.internet import pollreactor
cls = pollreactor.PollReactor
else:
from twisted.internet import selectreactor
cls = selectreactor.SelectReactor
except ImportError:
from twisted.internet import selectreactor
cls = selectreactor.SelectReactor
return cls()
class TwistedThread(threading.Thread):
started = False
daemon = True
@classmethod
def start_once(cls):
if cls.started:
return
cls.started = True
instance = cls(name='Twisted')
instance.start()
def run(self):
reactor.run(installSignalHandlers=False)
reactor = _get_reactor(platform)
start_once = TwistedThread.start_once
================================================
FILE: universe/utils/__init__.py
================================================
import logging
import six
import sys
if six.PY2:
import Queue as queue
else:
import queue
import threading
import signal
from twisted.internet import defer
from universe.twisty import reactor
logger = logging.getLogger(__name__)
class ErrorBuffer(object):
def __init__(self):
self.queue = queue.Queue()
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
if value is not None:
self.record(value)
def __call__(self, error, wrap=True):
self.record(error, wrap=True)
def record(self, error, wrap=True):
logger.debug('Error in thread %s: %s', threading.current_thread().name, error)
if wrap:
error = format_error(error)
try:
self.queue.put_nowait(error)
except queue.Full:
pass
def check(self, timeout=None):
if timeout is None:
timeout = 0
try:
error = self.queue.get(timeout=timeout)
except queue.Empty:
return
else:
raise error
def blocking_check(self, timeout=None):
# TODO: get rid of this method
if timeout is None:
while True:
self.check(timeout=3600)
else:
self.check(timeout)
from twisted.python import failure
import traceback
import threading
from universe import error
def format_error(e):
# errback automatically wraps everything in a Twisted Failure
if isinstance(e, failure.Failure):
e = e.value
if isinstance(e, str):
err_string = e
elif six.PY2:
err_string = traceback.format_exc(e).rstrip()
else:
err_string = ''.join(traceback.format_exception(type(e), e, e.__traceback__)).rstrip()
if err_string == 'None':
# Reasonable heuristic for exceptions that were created by hand
last = traceback.format_stack()[-2]
err_string = '{}\n {}'.format(e, last)
# Quick and dirty hack for now.
err_string = err_string.replace('Connection to the other side was lost in a non-clean fashion', 'Connection to the other side was lost in a non-clean fashion (HINT: this generally actually means we got a connection refused error. Check that the remote is actually running.)')
return error.Error(err_string)
def queue_get(local_queue):
while True:
try:
result = local_queue.get(timeout=1000)
except queue.Empty:
pass
else:
return result
def blockingCallFromThread(f, *a, **kw):
local_queue = queue.Queue()
def _callFromThread():
result = defer.maybeDeferred(f, *a, **kw)
result.addBoth(local_queue.put)
reactor.callFromThread(_callFromThread)
result = queue_get(local_queue)
if isinstance(result, failure.Failure):
if result.frames:
e = error.Error(str(result))
else:
e = result.value
raise e
return result
from gym import spaces
def repeat_space(space, n):
return spaces.Tuple([space] * n)
import base64
import uuid
def random_alphanumeric(length=14):
buf = []
while len(buf) < length:
entropy = base64.encodestring(uuid.uuid4().bytes).decode('ascii')
bytes = [c for c in entropy if c.isalnum()]
buf += bytes
return ''.join(buf)[:length]
def best_effort(function, *args, **kwargs):
try:
return function(*args, **kwargs)
except:
if six.PY2:
logging.error('Error in %s:', function.__name__)
traceback.print_exc()
else:
logging.error('Error in %s:', function.__name__)
logger.error(traceback.format_exc())
return None
import base64
def basic_auth_encode(username, password=''):
fmt = '{}:{}'.format(username, password)
return 'Basic ' + base64.encodestring(fmt.encode('utf-8')).rstrip().decode('utf-8')
def basic_auth_decode(header):
if header.startswith('Basic '):
header = header[len('Basic '):]
decoded = base64.decodestring(header.encode('utf-8')).decode('utf-8')
username, password = decoded.split(':')
return username, password
else:
return None
import os
def default_password():
if os.path.exists('/usr/local/openai/privileged_state/password'):
with open('/usr/local/openai/privileged_state/password') as f:
return f.read().strip()
return 'openai'
import logging
import time
logger = logging.getLogger(__name__)
class PeriodicLog(object):
def log(self, obj, name, msg, *args, **kwargs):
try:
info = obj._periodic_log_info
except AttributeError:
info = obj._periodic_log_info = {}
# Would be better to use a frequency=... arg after kwargs, but
# that isn't py2 compatible.
frequency = kwargs.pop('frequency', 1)
delay = kwargs.pop('delay', 0)
last_log = info.setdefault(name, time.time()-frequency+delay)
if time.time() - last_log < frequency:
return
info[name] = time.time()
logger.info('[{}] {}'.format(name, msg), *args)
def log_debug(self, obj, name, msg, *args, **kwargs):
try:
info = obj._periodic_log_debug
except AttributeError:
info = obj._periodic_log_debug = {}
frequency = kwargs.pop('frequency', 1)
delay = kwargs.pop('delay', 0)
last_log = info.setdefault(name, time.time()-frequency+delay)
if time.time() - last_log < frequency:
return
info[name] = time.time()
logger.debug('[{}] {}'.format(name, msg), *args)
_periodic = PeriodicLog()
periodic_log = _periodic.log
periodic_log_debug = _periodic.log_debug
import threading
def thread_name():
return threading.current_thread().name
def exit_on_signal():
"""
Install a signal handler for HUP, INT, and TERM to call exit, allowing clean shutdown.
When running a universe environment, it's important to shut down the container when the
agent dies so you should either call this or otherwise arrange to exit on signals.
"""
def shutdown(signal, frame):
logger.warn('Received signal %s: exiting', signal)
sys.exit(128+signal)
signal.signal(signal.SIGHUP, shutdown)
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
================================================
FILE: universe/utils/display.py
================================================
# -*- coding: utf-8 -*-
import logging
import six
import numpy as np
logger = logging.getLogger(__name__)
# We log these with logger, which in py2 chokes on unicode
def fmt_plusminus(mean, dev):
if six.PY3:
return mean + '±' + dev
else:
# Logging unicode in py2 is asking for trouble
return mean + '+-' + dev
def compute_timestamps_pair_max(time_m_2, flat=True):
if flat:
# Ignore empty inputs, which happens when environments are resetting.
time_m_2 = [[x for x in time_m_2 if len(x)]]
if len(time_m_2) == 0:
return None, None
# We concatenate the (min, max) lags from a variety of runs. Those
# runs may have different lengths.
time_m_2 = [np.array(m) for m in time_m_2]
timestamp_m = []
data_m = []
for m in time_m_2:
if len(m) > 0:
timestamp, data = compute_timestamps_sigma(m[:, 1])
timestamp_m.append(timestamp)
data_m.append(data)
else:
timestamp_m.append(None)
data_m.append({})
return timestamp_m, data_m
def display_timestamps_pair_compact(time_m_2):
"""Takes a list of the following form: [(a1, b1), (a2, b2), ...] and
returns a string a_mean-b_mean, flooring out at 0.
"""
if len(time_m_2) == 0:
return '(empty)'
time_m_2 = np.array(time_m_2)
low = time_m_2[:, 0].mean()
high = time_m_2[:, 1].mean()
low = max(low, 0)
# Not sure if this'll always be true, and not worth crashing over
if high < 0:
logger.warn('Harmless warning: upper-bound on clock skew is negative: (%s, %s). Please let Greg know about this.', low, high)
return '{}-{}'.format(display_timestamp(low), display_timestamp(high))
def display_timestamps_pair(time_m_2):
"""Takes a list of the following form: [(a1, b1), (a2, b2), ...] and
returns a string (a_mean+/-a_error, b_mean+/-b_error).
"""
if len(time_m_2) == 0:
return '(empty)'
time_m_2 = np.array(time_m_2)
return '({}, {})'.format(
display_timestamps(time_m_2[:, 0]),
display_timestamps(time_m_2[:, 1]),
)
def compute_timestamps_sigma_n(time_m):
timestamp_m = []
data_m = []
for t in time_m:
timestamp, data = compute_timestamps(t)
timestamp_m.append(timestamp)
data_m.append(data)
return timestamp_m, data_m
def compute_timestamps_sigma(time_m):
if len(time_m) == 0:
return None, {}
mean = np.mean(time_m)
std = standard_error(time_m)
scale, units = pick_time_units(mean)
return fmt_plusminus('{:.2f}{}'.format(mean * scale, units), '{:.2f}{}'.format(std * scale, units)), {'mean': mean}
def display_timestamps(time_m):
res, _ = compute_timestamps(time_m)
if res is None:
return '(empty)'
else:
return res
def compute_timestamps(time_m):
if len(time_m) == 0:
return None, {}
mean = np.mean(time_m)
std = standard_error(time_m)
return fmt_plusminus(display_timestamp(mean), display_timestamp(std)), {'mean': mean}
def display_timestamps_n(time_m):
# concatenate all the n's timesteps together, then display_timestamps on it
return display_timestamps(np.concatenate(time_m))
def standard_error(ary, axis=0):
if len(ary) > 1:
return np.std(ary, axis=axis) / np.sqrt(len(ary) - 1)
else:
return np.std(ary, axis=axis)
def display_timestamp(time):
assert not isinstance(time, np.ndarray), 'Invalid scalar: {}'.format(time)
scale, units = pick_time_units(time)
return '{:.2f}{}'.format(time * scale, units)
def pick_time_units(time):
assert not isinstance(time, np.ndarray), 'Invalid scalar: {}'.format(time)
if abs(time) < 1:
return 1000, 'ms'
else:
return 1, 's'
================================================
FILE: universe/vectorized/__init__.py
================================================
from universe.vectorized.core import Env, Wrapper, ObservationWrapper, ActionWrapper, RewardWrapper
from universe.vectorized.multiprocessing_env import MultiprocessingEnv
from universe.vectorized.vectorize_filter import Filter, VectorizeFilter
================================================
FILE: universe/vectorized/core.py
================================================
import gym
from gym import spaces
from universe import error
class Env(gym.Env):
"""Base class capable of handling vectorized environments.
"""
metadata = {
# This key indicates whether an env is vectorized (or, in the case of
# Wrappers where autovectorize=True, whether they should automatically
# be wrapped by a Vectorize wrapper.)
'runtime.vectorized': True,
}
# Number of remotes. User should set this.
n = None
class Wrapper(Env, gym.Wrapper):
"""Use this instead of gym.Wrapper iff you're wrapping a vectorized env,
(or a vanilla env you wish to be vectorized).
"""
# If True and this is instantiated with a non-vectorized environment,
# automatically wrap it with the Vectorize wrapper.
autovectorize = True
def __init__(self, env):
super(Wrapper, self).__init__(env)
if not env.metadata.get('runtime.vectorized'):
if self.autovectorize:
# Circular dependency :(
from universe import wrappers
env = wrappers.Vectorize(env)
else:
raise error.Error('This wrapper can only wrap vectorized envs (i.e. where env.metadata["runtime.vectorized"] = True), not {}. Set "self.autovectorize = True" to automatically add a Vectorize wrapper.'.format(env))
self.env = env
@property
def n(self):
return self.env.n
def configure(self, **kwargs):
self.env.configure(**kwargs)
class ObservationWrapper(Wrapper, gym.ObservationWrapper):
pass
class RewardWrapper(Wrapper, gym.RewardWrapper):
pass
class ActionWrapper(Wrapper, gym.ActionWrapper):
pass
================================================
FILE: universe/vectorized/multiprocessing_env.py
================================================
import logging
import multiprocessing
import numpy as np
import traceback
import gym
from gym import spaces
from universe.vectorized import core
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class Error(Exception):
pass
def display_name(exception):
prefix = ''
# AttributeError has no __module__; RuntimeError has module of
# exceptions
if hasattr(exception, '__module__') and exception.__module__ != 'exceptions':
prefix = exception.__module__ + '.'
return prefix + type(exception).__name__
def render_dict(error):
return {
'type': display_name(error),
'message': error.message,
'traceback': traceback.format_exc(error)
}
class Worker(object):
def __init__(self, env_m, worker_idx):
# These are instantiated in the *parent* process
# currently. Probably will want to change this. The parent
# does need to obtain the relevant Spaces at some stage, but
# that's doable.
self.worker_idx = worker_idx
self.env_m = env_m
self.m = len(env_m)
self.parent_conn, self.child_conn = multiprocessing.Pipe()
self.joiner = multiprocessing.Process(target=self.run)
self._clear_state()
self.start()
# Parent only!
self.child_conn.close()
def _clear_state(self):
self.mask = [True] * self.m
# Control methods
def start(self):
self.joiner.start()
def _parent_recv(self):
rendered, res = self.parent_conn.recv()
if rendered is not None:
raise Error('[Worker {}] Error: {} ({})\n\n{}'.format(self.worker_idx, rendered['message'], rendered['type'], rendered['traceback']))
return res
def _child_send(self, msg):
self.child_conn.send((None, msg))
def _parent_send(self, msg):
try:
self.parent_conn.send(msg)
except IOError: # the worker is now dead
try:
res = self._parent_recv()
except EOFError:
raise Error('[Worker {}] Child died unexpectedly'.format(self.worker_idx))
else:
raise Error('[Worker {}] Child returned unexpected result: {}'.format(self.worker_idx, res))
def close_start(self):
self._parent_send(('close', None))
def close_finish(self):
self.joiner.join()
def reset_start(self):
self._parent_send(('reset', None))
def reset_finish(self):
return self._parent_recv()
def step_start(self, action_m):
"""action_m: the batch of actions for this worker"""
self._parent_send(('step', action_m))
def step_finish(self):
return self._parent_recv()
def mask_start(self, i):
self._parent_send(('mask', i))
def seed_start(self, seed_m):
self._parent_send(('seed', seed_m))
def render_start(self, mode, close):
self._parent_send(('render', (mode, close)))
def render_finish(self):
return self._parent_recv()
def run(self):
try:
self.do_run()
except Exception as e:
rendered = render_dict(e)
self.child_conn.send((rendered, None))
return
def do_run(self):
# Child only!
self.parent_conn.close()
while True:
method, body = self.child_conn.recv()
logger.debug('[%d] Received: method=%s body=%s', self.worker_idx, method, body)
if method == 'close':
logger.info('Closing envs')
# TODO: close envs?
return
elif method == 'reset':
self._clear_state()
observation_m = [env.reset() for env in self.env_m]
self._child_send(observation_m)
elif method == 'step':
action_m = body
observation_m, reward_m, done_m, info = self.step_m(action_m)
self._child_send((observation_m, reward_m, done_m, info))
elif method == 'mask':
i = body
assert 0 <= i < self.m, 'Bad value for mask: {} (should be >= 0 and < {})'.format(i, self.m)
self.mask[i] = False
logger.debug('[%d] Applying mask: i=%d', self.worker_idx, i)
elif method == 'seed':
seeds = body
[env.seed(seed) for env, seed in zip(self.env_m, seeds)]
elif method == 'render':
mode, close = body
if mode == 'human':
self.env_m[0].render(mode=mode, close=close)
result = [None]
else:
result = [env.render(mode=mode, close=close) for env in self.env_m]
self._child_send(result)
else:
raise Error('Bad method: {}'.format(method))
def step_m(self, action_m):
observation_m = []
reward_m = []
done_m = []
info = {'m': []}
for env, enabled, action in zip(self.env_m, self.mask, action_m):
if enabled:
observation, reward, done, info_i = env.step(action)
if done:
observation = env.reset()
else:
observation = None
reward = 0
done = False
info_i = {}
observation_m.append(observation)
reward_m.append(reward)
done_m.append(done)
info['m'].append(info_i)
return observation_m, reward_m, done_m, info
def step_n(worker_n, action_n):
accumulated = 0
for worker in worker_n:
action_m = action_n[accumulated:accumulated+worker.m]
worker.step_start(action_m)
accumulated += worker.m
observation_n = []
reward_n = []
done_n = []
info = {'n': []}
for worker in worker_n:
observation_m, reward_m, done_m, info_i = worker.step_finish()
observation_n += observation_m
reward_n += reward_m
done_n += done_m
info['n'] += info_i['m']
return observation_n, reward_n, done_n, info
def reset_n(worker_n):
for worker in worker_n:
worker.reset_start()
observation_n = []
for worker in worker_n:
observation_n += worker.reset_finish()
return observation_n
def seed_n(worker_n, seed_n):
accumulated = 0
for worker in worker_n:
action_m = seed_n[accumulated:accumulated+worker.m]
worker.seed_start(seed_n)
accumulated += worker.m
def mask(worker_n, i):
accumulated = 0
for k, worker in enumerate(worker_n):
if accumulated + worker.m <= i:
accumulated += worker.m
else:
worker.mask_start(i - accumulated)
return
def render_n(worker_n, mode, close):
if mode == 'human':
# Only render 1 worker
worker_n = worker_n[0:]
for worker in worker_n:
worker.render_start(mode, close)
res = []
for worker in worker_n:
res += worker.render_finish()
if mode != 'human':
return res
else:
return None
def close_n(worker_n):
if worker_n is None:
return
# TODO: better error handling: workers should die when we go away
# anyway. Also technically should wait for these processes if
# we're not crashing.
for worker in worker_n:
try:
worker.close_start()
except Error:
pass
# for worker in worker_n:
# try:
# worker.close_finish()
# except Error:
# pass
class MultiprocessingEnv(core.Env):
metadata = {
'runtime.vectorized': True,
'configure.required': True,
}
def __init__(self, env_id):
self.worker_n = None
# Pull the relevant info from a transient env instance
self.spec = gym.spec(env_id)
env = self.spec.make()
current_metadata = self.metadata
self.metadata = env.metadata.copy()
self.metadata.update(current_metadata)
self.action_space = env.action_space
self.observation_space = env.observation_space
self.reward_range = env.reward_range
def configure(self, n=1, pool_size=None, episode_limit=None):
self.n = n
self.envs = [self.spec.make() for _ in range(self.n)]
if pool_size is None:
pool_size = min(len(self.envs), multiprocessing.cpu_count() - 1)
pool_size = max(1, pool_size)
self.worker_n = []
m = int((self.n + pool_size - 1) / pool_size)
for i in range(0, self.n, m):
envs = self.envs[i:i+m]
self.worker_n.append(Worker(envs, i))
if episode_limit is not None:
self._episode_id.episode_limit = episode_limit
def _seed(self, seed):
seed_n(self.worker_n, seed)
return [[seed_i] for seed_i in seed]
def _reset(self):
return reset_n(self.worker_n)
def _step(self, action_n):
return step_n(self.worker_n, action_n)
def _render(self, mode='human', close=False):
return render_n(self.worker_n, mode=mode, close=close)
def mask(self, i):
mask(self.worker_n, i)
def _close(self):
close_n(self.worker_n)
if __name__ == '__main__':
env_n = make('Pong-v3')
env_n.configure()
env_n.reset()
print(env_n.step([0] * 10))
================================================
FILE: universe/vectorized/tests/test_monitoring.py
================================================
import glob
import os
import gym.monitoring
from gym.monitoring.tests import helpers
from universe import wrappers
def test_multiprocessing_env_monitoring():
with helpers.tempdir() as temp:
env = wrappers.WrappedMultiprocessingEnv('Pong-v3')
env.configure(n=2)
env = wrappers.Monitor(env, temp)
env.reset()
for i in range(2):
env.step([0, 0])
env.close()
manifests = glob.glob(os.path.join(temp, '*.video.*'))
assert len(manifests) == 2, 'There are {} manifests: {}'.format(len(manifests), manifests)
results = gym.monitoring.load_results(temp)
assert results['env_info']['env_id'] == 'Pong-v3'
def test_vnc_monitoring():
with helpers.tempdir() as temp:
env = gym.make('gym-core.Pong-v3')
env.configure(remotes=2)
env = wrappers.GymCoreAction(env)
env = wrappers.Monitor(env, temp)
env.reset()
for i in range(2):
env.step([0, 0])
env.close()
results = gym.monitoring.load_results(temp)
assert results['env_info']['env_id'] == 'gym-core.Pong-v3'
if __name__ == '__main__':
test_multiprocessing_env_monitoring()
test_vnc_monitoring()
================================================
FILE: universe/vectorized/vectorize_filter.py
================================================
from universe.vectorized import core
class Filter(object):
def _after_reset(self, observation):
return observation
def _after_step(self, observation, reward, done, info):
return observation, reward, done, info
class VectorizeFilter(core.Wrapper):
"""Vectorizes a Filter written for the non-vectorized case."""
autovectorize = False
metadata = {
'configure.required': True
}
def __init__(self, env, filter_factory, *args, **kwargs):
super(VectorizeFilter, self).__init__(env)
self.filter_factory = filter_factory
self.filter_n = None
self._args = args
self._kwargs = kwargs
def _reset(self):
if self.filter_n is None:
self.filter_n = [self.filter_factory(*self._args, **self._kwargs) for _ in range(self.n)]
observation_n = self.env.reset()
observation_n = [filter._after_reset(observation) for filter, observation in zip(self.filter_n, observation_n)]
return observation_n
def _step(self, action_n):
o_n, r_n, d_n, i = self.env.step(action_n)
observation_n = []
reward_n = []
done_n = []
info = i.copy()
info['n'] = []
for filter, observation, reward, done, info_i in zip(self.filter_n, o_n, r_n, d_n, i['n']):
observation, reward, done, info_i = filter._after_step(observation, reward, done, info_i)
observation_n.append(observation)
reward_n.append(reward)
done_n.append(done)
info['n'].append(info_i)
return observation_n, reward_n, done_n, info
def __str__(self):
return '<{}[{}]{}>'.format(type(self).__name__, self.filter_factory, self.env)
================================================
FILE: universe/vncdriver/README.md
================================================
# Python VNC driver implementation
This Python VNC driver is using an older API, and needs a small amount
of work to once again become a good backend. We haven't bothered with
this since the Go driver is much faster. We would take a pull request
to fix it though!
================================================
FILE: universe/vncdriver/__init__.py
================================================
import logging
from universe.vncdriver.vnc_session import VNCSession
from universe.vncdriver.vnc_client import client_factory
from universe.vncdriver.screen import NumpyScreen, PygletScreen
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
================================================
FILE: universe/vncdriver/auth.py
================================================
import six
import uuid
from universe import utils
from universe.vncdriver.vendor import pydes
class RFBDes(pydes.des):
def setKey(self, key):
key = key.encode('ascii')
newkey = []
for ki in range(len(key)):
if six.PY2:
bsrc = ord(key[ki])
else:
bsrc = key[ki]
# Reverse the bits
btgt = 0
for i in range(8):
if bsrc & (1 << i):
btgt = btgt | (1 << 7-i)
if six.PY2:
newkey.append(chr(btgt))
else:
newkey.append(btgt)
super(RFBDes, self).setKey(newkey)
def challenge():
length = 16
buf = b''
while len(buf) < length:
entropy = uuid.uuid4().bytes
buf += entropy
return buf[:length]
def challenge_response(challenge, password=None):
if password is None:
password = utils.default_password()
password += ((8 - len(password)) % 8) * '\0' # pad to multiple of 8 bytes
des = RFBDes(password)
return des.encrypt(challenge)
================================================
FILE: universe/vncdriver/constants.py
================================================
# Encodings
RAW_ENCODING = 0
COPY_RECTANGLE_ENCODING = 1
RRE_ENCODING = 2
CORRE_ENCODING = 4
HEXTILE_ENCODING = 5
ZLIB_ENCODING = 6
TIGHT_ENCODING = 7
ZLIBHEX_ENCODING = 8
ZRLE_ENCODING = 16
#0xffffff00 to 0xffffffff tight options
PSEUDO_CURSOR_ENCODING = -239
# Keycodes
KEY_BackSpace = 0xff08
KEY_Tab = 0xff09
KEY_Return = 0xff0d
KEY_Escape = 0xff1b
KEY_Insert = 0xff63
KEY_Delete = 0xffff
KEY_Home = 0xff50
KEY_End = 0xff57
KEY_PageUp = 0xff55
KEY_PageDown = 0xff56
KEY_Left = 0xff51
KEY_Up = 0xff52
KEY_Right = 0xff53
KEY_Down = 0xff54
KEY_F1 = 0xffbe
KEY_F2 = 0xffbf
KEY_F3 = 0xffc0
KEY_F4 = 0xffc1
KEY_F5 = 0xffc2
KEY_F6 = 0xffc3
KEY_F7 = 0xffc4
KEY_F8 = 0xffc5
KEY_F9 = 0xffc6
KEY_F10 = 0xffc7
KEY_F11 = 0xffc8
KEY_F12 = 0xffc9
KEY_F13 = 0xFFCA
KEY_F14 = 0xFFCB
KEY_F15 = 0xFFCC
KEY_F16 = 0xFFCD
KEY_F17 = 0xFFCE
KEY_F18 = 0xFFCF
KEY_F19 = 0xFFD0
KEY_F20 = 0xFFD1
KEY_ShiftLeft = 0xffe1
KEY_ShiftRight = 0xffe2
KEY_ControlLeft = 0xffe3
KEY_ControlRight = 0xffe4
KEY_MetaLeft = 0xffe7
KEY_MetaRight = 0xffe8
KEY_AltLeft = 0xffe9
KEY_AltRight = 0xffea
KEY_Scroll_Lock = 0xFF14
KEY_Sys_Req = 0xFF15
KEY_Num_Lock = 0xFF7F
KEY_Caps_Lock = 0xFFE5
KEY_Pause = 0xFF13
KEY_Super_L = 0xFFEB
KEY_Super_R = 0xFFEC
KEY_Hyper_L = 0xFFED
KEY_Hyper_R = 0xFFEE
KEY_KP_0 = 0xFFB0
KEY_KP_1 = 0xFFB1
KEY_KP_2 = 0xFFB2
KEY_KP_3 = 0xFFB3
KEY_KP_4 = 0xFFB4
KEY_KP_5 = 0xFFB5
KEY_KP_6 = 0xFFB6
KEY_KP_7 = 0xFFB7
KEY_KP_8 = 0xFFB8
KEY_KP_9 = 0xFFB9
KEY_KP_Enter = 0xFF8D
KEY_ForwardSlash = 0x002F
KEY_BackSlash = 0x005C
KEY_SpaceBar= 0x0020
# TODO: build this programmatically?
KEYMAP = {
'bsp': KEY_BackSpace,
'tab': KEY_Tab,
'return': KEY_Return,
'enter': KEY_Return,
'esc': KEY_Escape,
'ins': KEY_Insert,
'delete': KEY_Delete,
'del': KEY_Delete,
'home': KEY_Home,
'end': KEY_End,
'pgup': KEY_PageUp,
'pgdn': KEY_PageDown,
'ArrowLeft': KEY_Left,
'left': KEY_Left,
'ArrowUp': KEY_Up,
'up': KEY_Up,
'ArrowRight': KEY_Right,
'right': KEY_Right,
'ArrowDown': KEY_Down,
'down': KEY_Down,
'slash': KEY_BackSlash,
'bslash': KEY_BackSlash,
'fslash': KEY_ForwardSlash,
'spacebar': KEY_SpaceBar,
'space': KEY_SpaceBar,
'sb': KEY_SpaceBar,
'f1': KEY_F1,
'f2': KEY_F2,
'f3': KEY_F3,
'f4': KEY_F4,
'f5': KEY_F5,
'f6': KEY_F6,
'f7': KEY_F7,
'f8': KEY_F8,
'f9': KEY_F9,
'f10': KEY_F10,
'f11': KEY_F11,
'f12': KEY_F12,
'f13': KEY_F13,
'f14': KEY_F14,
'f15': KEY_F15,
'f16': KEY_F16,
'f17': KEY_F17,
'f18': KEY_F18,
'f19': KEY_F19,
'f20': KEY_F20,
'lshift': KEY_ShiftLeft,
'shift': KEY_ShiftLeft,
'rshift': KEY_ShiftRight,
'lctrl': KEY_ControlLeft,
'ctrl': KEY_ControlLeft,
'rctrl': KEY_ControlRight,
'lmeta': KEY_MetaLeft,
'meta': KEY_MetaLeft,
'rmeta': KEY_MetaRight,
'lalt': KEY_AltLeft,
'alt': KEY_AltLeft,
'ralt': KEY_AltRight,
'scrlk': KEY_Scroll_Lock,
'sysrq': KEY_Sys_Req,
'numlk': KEY_Num_Lock,
'caplk': KEY_Caps_Lock,
'pause': KEY_Pause,
'lsuper': KEY_Super_L,
'super': KEY_Super_L,
'rsuper': KEY_Super_R,
'lhyper': KEY_Hyper_L,
'hyper': KEY_Hyper_L,
'rhyper': KEY_Hyper_R,
'kp0': KEY_KP_0,
'kp1': KEY_KP_1,
'kp2': KEY_KP_2,
'kp3': KEY_KP_3,
'kp4': KEY_KP_4,
'kp5': KEY_KP_5,
'kp6': KEY_KP_6,
'kp7': KEY_KP_7,
'kp8': KEY_KP_8,
'kp9': KEY_KP_9,
'kpenter': KEY_KP_Enter,
}
================================================
FILE: universe/vncdriver/dual_proxy_server.py
================================================
# a proxy server that handles both reward channel and vnc.
from twisted.python import log
from autobahn.twisted import websocket
import logging
import os
import time
import pexpect
import sys
import threading
from universe.vncdriver.vnc_proxy_server import VNCProxyServer
from universe.rewarder.reward_proxy_server import RewardProxyServer
from universe import utils
logger = logging.getLogger(__name__)
class DualProxyServer(VNCProxyServer):
def __init__(self, action_queue=None, error_buffer=None, enable_logging=True):
self._log_info('DualProxyServer inited')
self.reward_proxy = None
super(DualProxyServer, self).__init__(action_queue, error_buffer, enable_logging)
def _log_info(self, msg, *args, **kwargs):
logger.info('[dual_proxy] ' + msg, *args, **kwargs)
def recv_ClientInit(self, block):
# start reward proxy.
self._log_info('Starting reward proxy server')
self.reward_proxy = pexpect.spawnu(self.factory.reward_proxy_bin,
logfile=sys.stdout,
timeout=None)
# wait on reward proxy to be up.
self._log_info('Waiting for reward proxy server')
self.reward_proxy.expect('\[RewardProxyServer\]')
self.reward_proxy_thread = threading.Thread(target=lambda: self.reward_proxy.expect(pexpect.EOF))
self.reward_proxy_thread.start()
self._log_info('Reward proxy server is up %s', self.reward_proxy.before)
super(DualProxyServer, self).recv_ClientInit(block)
self.logfile_dir = self.log_manager.logfile_dir
def close(self):
# end connections.
super(DualProxyServer, self).close()
# wait for rewarder to close.
if self.reward_proxy:
self.reward_proxy.terminate()
# upload to s3.
# probably hacky right now.
logger.info('log manager = %s', self.log_manager)
if self.log_manager:
os.system('/app/universe/bin/upload_directory.sh demonstrator_%(recorder_id)s %(directory)s %(bucket)s' %
dict(recorder_id=self.factory.recorder_id, directory=self.logfile_dir,
bucket=self.factory.bucket)
)
================================================
FILE: universe/vncdriver/error.py
================================================
class Error(Exception):
pass
================================================
FILE: universe/vncdriver/fbs_reader.py
================================================
import json
import os
import struct
from universe import error
class InvalidFBSFileError(error.Error):
pass
class FBSReader(object):
def __init__(self, path):
self.file = open(path, 'rb')
version = self.file.read(12)
if version != b'FBS 001.002\n':
raise InvalidFBSFileError('Unrecognized FBS version: {}'.format(version))
header = self.file.readline()
pos = self.file.tell()
self.file.seek(pos, os.SEEK_SET)
header = json.loads(header.decode('utf-8'))
self.start = header['start']
def __iter__(self):
return self
def read_safe(self, size=None):
"""
We currently close our fbs files by killing them, so sometimes they end
up with bad data at the end. Close our reader if we expect `size` bytes
and get fewer.
This is a hack and should be removed when we cleanly close our
connections in fbs_writer.
https://github.com/openai/universe-envs/issues/41
"""
bytes = self.file.read(size)
if len(bytes) != size:
# We unexpectedly got to the end of the file
self.close()
raise StopIteration
return bytes
def next(self):
return self.__next__()
def __next__(self):
length_str = self.read_safe(4)
if length_str == '':
# Indicates a file with no trailer
self.close()
raise StopIteration
(length,) = struct.unpack('!I', length_str)
if length == 0:
# Reached the end
self.close()
raise StopIteration()
data = self.read_safe(length)
timestamp_str = self.read_safe(4)
(timestamp,) = struct.unpack('!I', timestamp_str)
return data, self.start + timestamp/1000.
def close(self):
self.file.close()
================================================
FILE: universe/vncdriver/fbs_writer.py
================================================
import json
import struct
import time
from gym.utils import atomic_write, closer
fbs_closer = closer.Closer()
class FBSWriter(object):
def __init__(self, path):
self._closed = False
self.start = None
self.stop = None
self._id = fbs_closer.register(self)
self.file = open(path, 'wb')
# custom format: exactly the same as FBS 001.000 except:
#
# FBS 001.002
# {line-of-json}
# [length-byte, data, timestamp]...
# \0\0\0\0 {line-of-json}
self.file.write(b'FBS 001.002\n')
def write(self, data):
# Format:
#
# length
# data
# timestamp (4 bytes)
if not data:
return
if self.start is not None:
delta = int(1000 * (time.time() - self.start))
else:
delta = 0
self.start = time.time()
# Write metadata header
self.file.write(json.dumps({'start': self.start}).encode('utf-8'))
self.file.write(b'\n')
length = struct.pack('!I', len(data))
self.file.write(length)
self.file.write(data)
delta = struct.pack('!I', delta)
self.file.write(delta)
def _write_metadata(self):
# Write metadata trailer
null = struct.pack('!I', 0)
self.file.write(null)
self.file.write(json.dumps({'stop': self.stop}).encode('utf-8'))
self.file.write(b'\n')
def close(self):
if self._closed:
return
self._closed = True
fbs_closer.unregister(self._id)
self.stop = time.time()
self._write_metadata()
self.file.close()
def __del__(self):
self.close()
================================================
FILE: universe/vncdriver/libvnc_session.py
================================================
import logging
import os
from twisted.internet import defer, endpoints
from universe import error, utils
from universe.twisty import reactor
from universe.vncdriver import screen, vnc_client
PYGAME_INSTALLED = None
def load_pygame():
global PYGAME_INSTALLED, pygame
if PYGAME_INSTALLED is not None:
return
try:
import pygame
PYGAME_INSTALLED = True
except ImportError:
PYGAME_INSTALLED = False
logger = logging.getLogger(__name__)
class LibVNCSession(object):
def __init__(self, remotes, error_buffer, encoding=None, compress_level=None, fine_quality_level=None, subsample_level=None):
"""compress_level: 0-9 [9 is highest compression]
fine_quality_level: 0-100 [100 is best quality]
subsample_level: 0-3 [0 is best quality]
Lots of references for this, but
https://github.com/TurboVNC/turbovnc/blob/master/doc/performance.txt
is decent.
"""
load_pygame()
import libvncdriver
if encoding is None:
encoding = os.environ.get('LIBVNC_ENCODING', 'tight')
if compress_level is None:
compress_level = int(os.environ.get('LIBVNC_COMPRESS_LEVEL', '0'))
if fine_quality_level is None:
fine_quality_level = int(os.environ.get('LIBVNC_FINE_QUALITY_LEVEL', '100'))
if subsample_level is None:
subsample_level = int(os.environ.get('LIBVNC_SUBSAMPLE_LEVEL', '0'))
if not hasattr(libvncdriver, 'VNCSession'):
raise error.Error('''
*=================================================*
|| libvncdriver is not installed ||
|| Try installing with "pip install libvncdriver" ||
|| or use the go or python driver by setting ||
|| UNIVERSE_VNCDRIVER=go ||
|| UNIVERSE_VNCDRIVER=py ||
*=================================================*''')
logger.info("Using libvncdriver's %s encoding" % encoding)
self.driver = libvncdriver.VNCSession(
remotes=remotes,
error_buffer=error_buffer,
encoding=encoding,
compress_level=compress_level,
fine_quality_level=fine_quality_level,
subsample_level=subsample_level,
)
self.screen = None
self.render_called_once = False
if PYGAME_INSTALLED:
pygame.init()
def flip(self):
return self._guard(self.driver.flip)
def step(self, action):
return self.driver.step(action)
def render(self):
self._guard(self._render)
def _guard(self, fn):
try:
return fn()
except (KeyboardInterrupt, SystemExit):
self.close()
def _render(self):
self.before_render()
if not PYGAME_INSTALLED:
return
# For some reason pygame wants X and Y swapped
aray, n = self.driver.flip()
if self.screen is None:
self.screen = pygame.display.set_mode(aray[0].shape[:2][::-1])
surf = pygame.surfarray.make_surface(aray[0].swapaxes(0, 1))
rect = surf.get_rect()
self.screen.blit(surf, rect)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.close()
def before_render(self):
if not self.render_called_once:
self.render_called_once = True
if not PYGAME_INSTALLED:
logger.warn('''
*================================================================*
|| ||
|| Rendering disabled when using libvnc without pygame installed. ||
|| Consider viewing over VNC or running "pip install pygame". ||
|| ||
*================================================================*''')
def close(self):
if PYGAME_INSTALLED:
pygame.quit()
self.driver.close()
================================================
FILE: universe/vncdriver/screen/__init__.py
================================================
from universe.vncdriver.screen.base import Screen
from universe.vncdriver.screen.numpy_screen import NumpyScreen
from universe.vncdriver.screen.pyglet_screen import PygletScreen
from universe.vncdriver.screen.screen_buffer import ScreenBuffer
================================================
FILE: universe/vncdriver/screen/base.py
================================================
import logging
import time
logger = logging.getLogger(__name__)
class Screen(object):
pass
================================================
FILE: universe/vncdriver/screen/numpy_screen.py
================================================
import logging
import numpy as np
from universe import pyprofile
import threading
import time
from universe import error
from universe.twisty import reactor
from universe.vncdriver import server_messages
from universe.spaces import vnc_event
logger = logging.getLogger(__name__)
class NumpyScreen(object):
def __init__(self, width, height):
self.lock = threading.RLock()
shape = (height, width, 3)
self._screens = (np.zeros(shape, dtype=np.uint8), np.zeros(shape, dtype=np.uint8))
self.color_cycle = [0, 1, 2]
self._width = None
self._height = None
self._defer = []
self.paint_cursor = False
self._cursor = {
id(self._screens[0]): {
'behind': None,
'details': None,
'painted': False,
},
id(self._screens[1]): {
'behind': None,
'details': None,
'painted': False,
},
}
self._back_updated = True
self._back_cursor_updated = True
self.cursor_shape = None
self.cursor_position = None
self._has_initial_framebuffer_update = False
def set_paint_cursor(self, paint_cursor):
self.paint_cursor = paint_cursor
def peek(self):
front_screen, _ = self._screens
return front_screen
def flip(self):
pyprofile.push('vncdriver.numpy_screen.flip_bitmap')
with self.lock:
if self._back_updated:
updates = self._defer
# Flip screens
front_screen, back_screen = self._screens
self._screens = back_screen, front_screen
# Mark ourselves as pending application of updates
self._back_updated = False
# This can be called asynchronously if desired, but it means
# less reliably smooth playback.
#
# reactor.callFromThread(self.update_back)
self.update_back()
else:
updates = []
result = self.peek(), {'vnc_session.framebuffer_updates': updates}
pyprofile.pop()
return result
def apply_action(self, action):
if isinstance(action, vnc_event.PointerEvent):
with self.lock:
self.cursor_position = (action.x, action.y)
# If not self._back_updated, we're not actually up to
# date, so any pixels we cached would be wrong. When
# back updates, it'll automatically render the cursor.
if self._back_updated and self.paint_cursor:
self._unpaint_cursor()
self._paint_cursor()
def apply(self, framebuffer_update):
with self.lock:
self._has_initial_framebuffer_update = True
# Pop any pending updates
self._update_back()
self._apply(framebuffer_update)
self._defer.append(framebuffer_update)
def _apply(self, framebuffer_update):
if self.paint_cursor:
self._unpaint_cursor()
for rect in framebuffer_update.rectangles:
if isinstance(rect.encoding,
(server_messages.RAWEncoding, server_messages.ZRLEEncoding, server_messages.ZlibEncoding)):
self._update_rectangle(rect.x, rect.y, rect.width, rect.height, rect.encoding.data)
elif isinstance(rect.encoding, server_messages.PseudoCursorEncoding):
self._update_cursor_shape(rect.x, rect.y, rect.width, rect.height, rect.encoding.image, rect.encoding.mask)
else:
raise error.Error('Unrecognized encoding: {}'.format(rect.encoding))
if self.paint_cursor:
self._paint_cursor()
def update_back(self):
with self.lock:
self._update_back()
def _update_back(self):
if self._back_updated:
return
self._back_updated = True
for framebuffer_update in self._defer:
self._apply(framebuffer_update)
if len(self._defer) == 0 and self.paint_cursor:
self._unpaint_cursor()
self._paint_cursor()
self._defer = []
def _update_rectangle(self, x, y, width, height, data):
_, back_screen = self._screens
back_screen[y:y+height, x:x+width, self.color_cycle] = data
def _update_cursor_shape(self, hotx, hoty, width, height, image, mask):
# hotx, hoty are the hotspot within the cursor
self.cursor_shape = (hotx, hoty, width, height, image, mask)
def _paint_cursor(self):
# use our knowledge of the x, y cursor position plus the
# cursor shape to paint the cursor
if self.cursor_position is None:
return
elif not self._has_initial_framebuffer_update:
return
elif self.cursor_shape is None:
return
self._back_cursor_updated = True
_, back_screen = self._screens
cursor = self._cursor[id(back_screen)]
assert not cursor['painted']
cursor['painted'] = True
hotx, hoty, width, height, image, mask = self.cursor_shape
x, y = self.cursor_position
# Save old data
cursor['details'] = (x, y, width, height)
cursor['behind'] = back_screen[y:y+height, x:x+width, :].copy()
# Paint the cursor
total_h, total_w, _ = back_screen.shape
cutoff_h = min(total_h - y, height)
cutoff_w = min(total_w - x, width)
image = image[:cutoff_h, :cutoff_w]
mask = mask[:cutoff_h, :cutoff_w, np.newaxis]
back_screen[y:y+height, x:x+width, self.color_cycle] = (1 - mask)*back_screen[y:y+height, x:x+width, self.color_cycle] + mask*image
def _unpaint_cursor(self):
_, back_screen = self._screens
cursor = self._cursor[id(back_screen)]
if cursor['behind'] is not None:
assert cursor['painted']
x, y, width, height = cursor['details']
back_screen[y:y+height, x:x+width, :] = cursor['behind']
cursor['painted'] = False
# def _copy_rectangle(self, screen, src_x, src_y, x, y, width, height):
# update = np.frombuffer(data, dtype=np.uint8)
# update = update.reshape([height, width, 4])
# update = update[:, :, self._color_cycle] # Ignore X channel
# screen[y:y+height, x:x+width] = screen[src_y:src_y+height, src_x:src_x+width]
# def _fill_rectangle(self, screen, x, y, width, height, color):
# update = np.frombuffer(color, dtype=np.uint8)
# update = update[self._color_cycle]
# screen[y:y+height, x:x+width] = update
# def begin(self, number_of_rectangles):
# self.lock.acquire()
# # This may already have been called via
# # reactor.callFromThread. It's safe to be called multiple times.
# self._update_back()
# def commit(self):
# self.lock.release()
# def back_bitmap(self):
# _, back_screen = self._screens
# return back_screen
# def screen_synced(self):
# # TODO: lock?
# return self._screens is not None
================================================
FILE: universe/vncdriver/screen/pyglet_screen.py
================================================
import logging
import numpy as np
import os
from universe import pyprofile
import sys
from universe import error
from universe.vncdriver import server_messages
logger = logging.getLogger(__name__)
class PygletScreen(object):
def __init__(self, bitmap=None):
self._window = None
self._is_updated = False
self._height, self._width, _ = bitmap.shape
self._initialize()
self.update_rectangle(0, 0, self._width, self._height, bitmap)
def flip(self):
if not self._is_updated:
return
self._is_updated = False
self._window.clear()
self._window.switch_to()
self._window.dispatch_events()
self.texture.blit(0, 0)
self._window.flip()
def _initialize(self):
if not os.environ.get('DISPLAY') and sys.platform.startswith('linux'):
raise error.Error("Cannot render with mode='human' with no DISPLAY variable set.")
import pyglet
self._window = pyglet.window.Window(width=self._width, height=self._height, visible=True)
self._window.dispatch_events()
self.texture = pyglet.image.Texture.create(width=self._width, height=self._height)
def update_rectangle(self, x, y, width, height, data):
bytes = data.tobytes()
pyprofile.incr('vncdriver.pyglet_screen.blit')
pyprofile.incr('vncdriver.pyglet_screen.blit.bytes', len(bytes), unit=pyprofile.BYTES)
import pyglet
image = pyglet.image.ImageData(width, height, 'RGB', bytes, pitch=width * -3)
self.texture.blit_into(image, x, self._height-height-y, 0)
self._is_updated = True
def apply(self, framebuffer_update):
pyprofile.push('vncdriver.pyglet_screen.apply')
for rect in framebuffer_update.rectangles:
if isinstance(rect.encoding,
(server_messages.RAWEncoding, server_messages.ZRLEEncoding, server_messages.ZlibEncoding)):
self.update_rectangle(rect.x, rect.y, rect.width, rect.height, rect.encoding.data)
else:
raise error.Error('Unrecognized encoding: {}'.format(rect.encoding))
pyprofile.pop()
# # TODO: we don't seem to be able to have multiple independent
# # windows at once
# def update_rectangle(self, x, y, width, height, data):
# self._update_rgbarray(x, y, width, height, update)
# def copy_rectangle(self, src_x, src_y, x, y, width, height):
# assert self._window
# rectangle = self.texture.get_region(src_x, self._height-height-src_y, width, height)
# self.texture.blit_into(rectangle.get_image_data(), x, self._height-height-y, 0)
# def fill_rectangle(self, x, y, width, height, color):
# import pyglet
# # While this technically works, it's super slow
# update = np.frombuffer(color, dtype=np.uint8)
# r, g, b = update[self._color_cycle]
# image_pattern = pyglet.image.SolidColorImagePattern(color=(r, g, b, 0))
# image = image_pattern.create_image(width, height)
# self.texture.blit_into(image, x, self._height-height-y, 0)
# def commit(self):
# self._window.clear()
# self._window.switch_to()
# self.texture.blit(0, 0)
# self._is_updated = True
================================================
FILE: universe/vncdriver/screen/screen_buffer.py
================================================
import logging
import time
import threading
from universe.vncdriver.screen import numpy_screen
logger = logging.getLogger(__name__)
class ScreenBuffer(object):
def __init__(self):
self.lock = threading.Lock()
self.uncommitted = []
self.updates = []
def apply_format(self, attrs):
self._push({
'type': 'apply_format',
'attrs': attrs,
})
def update_rectangle(self, x, y, width, height, data):
self._push({
'type': 'update_rectangle',
'x': x,
'y': y,
'width': width,
'height': height,
'data': data,
})
def copy_rectangle(self, src_x, src_y, x, y, width, height):
self._push({
'type': 'copy_rectangle',
'src_x': src_x,
'src_y': src_y,
'x': x,
'y': y,
'width': width,
'height': height,
})
def fill_rectangle(self, x, y, width, height, color):
self._push({
'type': 'fill_rectangle',
'x': x,
'y': y,
'width': width,
'height': height,
'color': color,
})
def framebuffer_update_finish(self):
with self.lock:
self.updates += self.uncommitted
self.uncommitted = []
def _push(self, update):
"""Always call from single thread."""
self.uncommitted.append(update)
def pop(self):
with self.lock:
if self.updates:
updates = self.updates
self.updates = []
return updates
else:
return None
def peek(self):
with self.lock:
if self.updates:
return self.updates
else:
return None
================================================
FILE: universe/vncdriver/server_messages.py
================================================
try:
# In Py2, use the more efficient cStringIO implementation if it's
# available
from cStringIO import StringIO as BytesIO
except ImportError:
# Fall back to using normal BytesIO, six handles python 2 vs 3 compat
from six import BytesIO
import logging
import numpy as np
from universe import pyprofile
import struct
logger = logging.getLogger(__name__)
class FramebufferUpdate(object):
def __init__(self, rectangles):
self.rectangles = rectangles
class Rectangle(object):
def __init__(self, x, y, width, height, encoding):
self.x = x
self.y = y
self.width = width
self.height = height
self.encoding = encoding
class PseudoCursorEncoding(object):
def __init__(self, image, mask):
self.image = image
self.mask = mask
@classmethod
def parse_rectangle(cls, client, x, y, width, height, data):
split = width * height * client.framebuffer.bypp
image = np.frombuffer(data[:split], np.uint8).reshape((height, width, 4))[:, :, [0, 1, 2]]
# Turn raw bytes into uint8 array
mask = np.frombuffer(data[split:], np.uint8)
# Turn uint8 array into bit array, and go over the scanlines
mask = np.unpackbits(mask).reshape((height, -1 if mask.size else 0))[:, :width]
encoding = cls(image, mask)
return Rectangle(x, y, width, height, encoding)
class RAWEncoding(object):
def __init__(self, data):
self.data = data
@classmethod
def parse_rectangle(cls, client, x, y, width, height, data):
assert client.framebuffer.bpp == 32
data = np.frombuffer(data, np.uint8).reshape((height, width, 4))[:, :, [0, 1, 2]]
encoding = cls(data)
return Rectangle(x, y, width, height, encoding)
class ZlibEncoding(object):
def __init__(self, data):
self.data = data
@classmethod
def parse_rectangle(cls, client, x, y, width, height, data):
decompressed = client.zlib_decompressor.decompress(data)
logger.debug('[zlib] Decompressed from %s bytes -> %s bytes', len(data), len(decompressed))
pyprofile.incr('vncdriver.recv_rectangle.zlib_encoding.decompressed_bytes', len(decompressed), unit=pyprofile.BYTES)
data = np.frombuffer(decompressed, np.uint8).reshape((height, width, 4))[:, :, [0, 1, 2]]
encoding = cls(data)
return Rectangle(x, y, width, height, encoding)
class ZRLEEncoding(object):
def __init__(self, data):
self.data = data
@classmethod
def parse_rectangle(cls, client, x, y, width, height, data):
decompressed = client.zlib_decompressor.decompress(data)
logger.debug('[zrle] Decompressed from %s bytes -> %s bytes', len(data), len(decompressed))
pyprofile.incr('vncdriver.recv_rectangle.zrle_encoding.decompressed_bytes', len(decompressed), unit=pyprofile.BYTES)
if client.framebuffer.bpp > 24:
bytes_per_pixel = 3
else:
bytes_per_pixel = client.framebuffer.bypp
buf = BytesIO(decompressed)
data = cls._read(x, y, width, height, buf, bytes_per_pixel)
encoding = cls(data)
return Rectangle(x, y, width, height, encoding)
@classmethod
def _read(cls, x, y, width, height, buf, bytes_per_pixel):
data = np.zeros([height, width, 3], dtype=np.uint8) + 255
for tile_y in range(0, height, 64):
tile_height = min(64, height-tile_y)
for tile_x in range(0, width, 64):
tile_width = min(64, width-tile_x)
tile = data[tile_y:tile_y+tile_height, tile_x:tile_x+tile_width]
cls._read_tile(tile, buf, tile_width, tile_height, bytes_per_pixel)
return data
@classmethod
def _read_tile(cls, tile, buf, tile_width, tile_height, bytes_per_pixel):
assert bytes_per_pixel == 3
# Each tile begins with a subencoding type byte. The top bit of this
# byte is set if the tile has been run-length encoded, clear otherwise.
# The bottom 7 bits indicate the size of the palette used: zero means
# no palette, 1 means that the tile is of a single color, and 2 to 127
# indicate a palette of that size. The special subencoding values 129
# and 127 indicate that the palette is to be reused from the last tile
# that had a palette, with and without RLE, respectively.
(subencoding,) = struct.unpack('!B', buf.read(1))
run_length_encoded = bool(subencoding & 128)
palette_size = subencoding & 127
bytes = palette_size * bytes_per_pixel
palette_data = buf.read(bytes)
assert len(palette_data) == bytes, "Palette data came up short: {} bytes rather than {}".format(len(palette_data), bytes)
logger.debug('Handling zrle tile: run_length_encoded=%s palette_size=%s', run_length_encoded, palette_size)
if palette_size == 0 and not run_length_encoded:
# 0: Raw pixel data. width*height pixel values follow (where width and
# height are the width and height of the tile):
#
# +-----------------------------+--------------+-------------+
# | No. of bytes | Type [Value] | Description |
# +-----------------------------+--------------+-------------+
# | width*height*BytesPerCPixel | CPIXEL array | pixels |
# +-----------------------------+--------------+-------------+
data = buf.read(bytes_per_pixel * tile_width * tile_height)
data = np.frombuffer(data, dtype=np.uint8).reshape((tile_height, tile_width, 3))
tile[:, :, :] = data
return
elif palette_size == 1 and not run_length_encoded:
# 1: A solid tile consisting of a single color. The pixel value
# follows:
#
# +----------------+--------------+-------------+
# | No. of bytes | Type [Value] | Description |
# +----------------+--------------+-------------+
# | bytesPerCPixel | CPIXEL | pixelValue |
# +----------------+--------------+-------------+
color = np.frombuffer(palette_data, dtype=np.uint8).reshape((3, ))
tile[:, :, :] = color
return
elif not run_length_encoded:
# 2 to 16: Packed palette types. The paletteSize is the value of the
# subencoding, which is followed by the palette, consisting of
# paletteSize pixel values. The packed pixels follow, with each
# pixel represented as a bit field yielding a zero-based index into
# the palette. For paletteSize 2, a 1-bit field is used; for
# paletteSize 3 or 4, a 2-bit field is used; and for paletteSize
# from 5 to 16, a 4-bit field is used. The bit fields are packed
# into bytes, with the most significant bits representing the
# leftmost pixel (i.e., big endian). For tiles not a multiple of 8,
# 4, or 2 pixels wide (as appropriate), padding bits are used to
# align each row to an exact number of bytes.
# +----------------------------+--------------+--------------+
# | No. of bytes | Type [Value] | Description |
# +----------------------------+--------------+--------------+
# | paletteSize*bytesPerCPixel | CPIXEL array | palette |
# | m | U8 array | packedPixels |
# +----------------------------+--------------+--------------+
# where m is the number of bytes representing the packed pixels.
# For paletteSize of 2, this is div(width+7,8)*height; for
# paletteSize of 3 or 4, this is div(width+3,4)*height; or for
# paletteSize of 5 to 16, this is div(width+1,2)*height.
palette = np.frombuffer(palette_data, dtype=np.uint8).reshape((-1, 3))
if palette_size > 16:
# No palette reuse in zrle
assert palette_size < 127
bits_per_packed_pixel = 8
elif palette_size > 4:
bits_per_packed_pixel = 4
elif palette_size > 2:
bits_per_packed_pixel = 2
else:
bits_per_packed_pixel = 1
for j in range(tile_height):
# Discard any leftover bits for each new line
b = 0
nbits = 0
for i in range(tile_width):
if nbits == 0:
(b,) = struct.unpack('!B', buf.read(1))
nbits = 8
nbits -= bits_per_packed_pixel
idx = (b >> nbits) & ((1 << bits_per_packed_pixel) - 1) & 127
tile[j, i, :] = palette[idx]
return
elif run_length_encoded and palette_size == 0:
# 128: Plain RLE. The data consists of a number of runs, repeated
# until the tile is done. Runs may continue from the end of one row
# to the beginning of the next. Each run is represented by a single
# pixel value followed by the length of the run. The length is
# represented as one or more bytes. The length is calculated as one
# more than the sum of all the bytes representing the length. Any
# byte value other than 255 indicates the final byte. So for
# example, length 1 is represented as [0], 255 as [254], 256 as
# [255,0], 257 as [255,1], 510 as [255,254], 511 as [255,255,0], and
# so on.
#
# +-------------------------+--------------+-----------------------+
# | No. of bytes | Type [Value] | Description |
# +-------------------------+--------------+-----------------------+
# | bytesPerCPixel | CPIXEL | pixelValue |
# | div(runLength - 1, 255) | U8 array | 255 |
# | 1 | U8 | (runLength-1) mod 255 |
# +-------------------------+--------------+-----------------------+
i = 0
pixels = tile_width * tile_height
data = np.zeros((pixels, 3))
while i < pixels:
pix = buf.read(bytes_per_pixel)
pix = np.frombuffer(pix, dtype=np.uint8).reshape((3, ))
count = 1
b = None
while b == 255 or b is None:
(b,) = struct.unpack('!B', buf.read(1))
count += b
data[i:i+count, :] = pix
i += count
assert i == pixels
elif run_length_encoded and palette_size > 1:
# 130 to 255: Palette RLE. Followed by the palette, consisting of
# paletteSize = (subencoding - 128) pixel values:
#
# +----------------------------+--------------+-------------+
# | No. of bytes | Type [Value] | Description |
# +----------------------------+--------------+-------------+
# | paletteSize*bytesPerCPixel | CPIXEL array | palette |
# +----------------------------+--------------+-------------+
#
# Following the palette is, as with plain RLE, a number of runs,
# repeated until the tile is done. A run of length one is
# represented simply by a palette index:
#
# +--------------+--------------+--------------+
# | No. of bytes | Type [Value] | Description |
# +--------------+--------------+--------------+
# | 1 | U8 | paletteIndex |
# +--------------+--------------+--------------+
#
# A run of length more than one is represented by a palette index
# with the top bit set, followed by the length of the run as for
# plain RLE.
#
# +-------------------------+--------------+-----------------------+
# | No. of bytes | Type [Value] | Description |
# +-------------------------+--------------+-----------------------+
# | 1 | U8 | paletteIndex + 128 |
# | div(runLength - 1, 255) | U8 array | 255 |
# | 1 | U8 | (runLength-1) mod 255 |
# +-------------------------+--------------+-----------------------+
palette = np.frombuffer(palette_data, dtype=np.uint8).reshape((-1, 3))
i = 0
pixels = tile_width * tile_height
data = np.zeros((pixels, 3))
while i < pixels:
(idx,) = struct.unpack('!B', buf.read(1))
count = 1
if idx & 128:
b = None
while b == 255 or b is None:
(b,) = struct.unpack('!B', buf.read(1))
count += b
idx &= 127
pix = palette[idx]
data[i:i+count, :] = pix
i += count
assert i == pixels
else:
assert False, "Unhandled case: run_length_encoded={} palette_size={}".format(run_length_encoded, palette_size)
tile[:] = data.reshape((tile_height, tile_width, 3))
================================================
FILE: universe/vncdriver/vendor/__init__.py
================================================
================================================
FILE: universe/vncdriver/vendor/pydes.py
================================================
#############################################################################
# Documentation #
#############################################################################
# Author: Todd Whiteman
# Date: 16th March, 2009
# Verion: 2.0.0
# License: Public Domain - free to do as you wish
# Homepage: http://twhiteman.netfirms.com/des.html
#
# This is a pure python implementation of the DES encryption algorithm.
# It's pure python to avoid portability issues, since most DES
# implementations are programmed in C (for performance reasons).
#
# Triple DES class is also implemented, utilising the DES base. Triple DES
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
#
# See the README.txt that should come with this python module for the
# implementation methods used.
#
# Thanks to:
# * David Broadwell for ideas, comments and suggestions.
# * Mario Wolff for pointing out and debugging some triple des CBC errors.
# * Santiago Palladino for providing the PKCS5 padding technique.
# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
#
"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
Class initialization
--------------------
pyDes.des(key, [mode], [IV], [pad], [padmode])
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
for Triple DES
mode -> Optional argument for encryption type, can be either
pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Length must be 8 bytes.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
to use during all encrypt/decrpt operations done with this instance.
I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
padding issues, as the padding can be removed unambiguously upon decrypting
data that was encrypted using PAD_PKCS5 padmode.
Common methods
--------------
encrypt(data, [pad], [padmode])
decrypt(data, [pad], [padmode])
data -> Bytes to be encrypted/decrypted
pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
encryption, adds this characters to the end of the data block when
data is not a multiple of 8 bytes. For decryption, will remove the
trailing characters that match this pad character from the last 8
bytes of the unencrypted data block.
padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
or PAD_PKCS5). Defaults to PAD_NORMAL.
Example
-------
from pyDes import *
data = "Please encrypt my data"
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
# For Python3, you'll need to use bytes, i.e.:
# data = b"Please encrypt my data"
# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = k.encrypt(data)
print "Encrypted: %r" % d
print "Decrypted: %r" % k.decrypt(d)
assert k.decrypt(d, padmode=PAD_PKCS5) == data
See the module source (pyDes.py) for more examples of use.
You can also run the pyDes.py file without and arguments to see a simple test.
Note: This code was not written for high-end systems needing a fast
implementation, but rather a handy portable solution with small usage.
"""
import sys
# _pythonMajorVersion is used to handle Python2 and Python3 differences.
_pythonMajorVersion = sys.version_info[0]
# Modes of crypting / cyphering
ECB = 0
CBC = 1
# Modes of padding
PAD_NORMAL = 1
PAD_PKCS5 = 2
# PAD_PKCS5: is a method that will unambiguously remove all padding
# characters after decryption, when originally encrypted with
# this padding mode.
# For a good description of the PKCS5 padding technique, see:
# http://www.faqs.org/rfcs/rfc1423.html
# The base class shared by des and triple des.
class _baseDes(object):
def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
if IV:
IV = self._guardAgainstUnicode(IV)
if pad:
pad = self._guardAgainstUnicode(pad)
self.block_size = 8
# Sanity checking of arguments.
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if IV and len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
# Set the passed in variables
self._mode = mode
self._iv = IV
self._padding = pad
self._padmode = padmode
def getKey(self):
"""getKey() -> bytes"""
return self.__key
def setKey(self, key):
"""Will set the crypting key for this object."""
key = self._guardAgainstUnicode(key)
self.__key = key
def getMode(self):
"""getMode() -> pyDes.ECB or pyDes.CBC"""
return self._mode
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
self._mode = mode
def getPadding(self):
"""getPadding() -> bytes of length 1. Padding character."""
return self._padding
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
if pad is not None:
pad = self._guardAgainstUnicode(pad)
self._padding = pad
def getPadMode(self):
"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
return self._padmode
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
self._padmode = mode
def getIV(self):
"""getIV() -> bytes"""
return self._iv
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
if not IV or len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
IV = self._guardAgainstUnicode(IV)
self._iv = IV
def _padData(self, data, pad, padmode):
# Pad data depending on the mode
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode == PAD_NORMAL:
if len(data) % self.block_size == 0:
# No padding required.
return data
if not pad:
# Get the default padding.
pad = self.getPadding()
if not pad:
raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
data += (self.block_size - (len(data) % self.block_size)) * pad
elif padmode == PAD_PKCS5:
pad_len = 8 - (len(data) % self.block_size)
if _pythonMajorVersion < 3:
data += pad_len * chr(pad_len)
else:
data += bytes([pad_len] * pad_len)
return data
def _unpadData(self, data, pad, padmode):
# Unpad data depending on the mode.
if not data:
return data
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if padmode == PAD_NORMAL:
if not pad:
# Get the default padding.
pad = self.getPadding()
if pad:
data = data[:-self.block_size] + \
data[-self.block_size:].rstrip(pad)
elif padmode == PAD_PKCS5:
if _pythonMajorVersion < 3:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
data = data[:-pad_len]
return data
def _guardAgainstUnicode(self, data):
# Only accept byte strings or ascii unicode values, otherwise
# there is no way to correctly decode the data into bytes.
if _pythonMajorVersion < 3:
if isinstance(data, unicode):
raise ValueError("pyDes can only work with bytes, not Unicode strings.")
else:
if isinstance(data, str):
# Only accept ascii unicode values.
try:
return data.encode('ascii')
except UnicodeEncodeError:
pass
raise ValueError("pyDes can only work with encoded strings, not Unicode.")
return data
#############################################################################
# DES #
#############################################################################
class des(_baseDes):
"""DES encryption/decrytpion class
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key,[mode], [IV])
key -> Bytes containing the encryption key, must be exactly 8 bytes
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
# Permutation and translation tables for DES
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
# number left rotations of pc1
__left_rotations = [
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
]
# permuted choice key (table 2)
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
# initial permutation IP
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
# Expansion table for turning 32 bit blocks into 48 bits
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
]
# The (in)famous S-boxes
__sbox = [
# S1
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
# S2
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
# S3
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
# S4
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
# S5
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
# S6
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
# S7
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
# S8
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
# 32-bit permutation function P used on the output of the S-boxes
__p = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23,13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]
# final permutation IP^-1
__fp = [
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25,
32, 0, 40, 8, 48, 16, 56, 24
]
# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x01
# Initialisation
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
# Sanity checking of arguments.
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
_baseDes.__init__(self, mode, IV, pad, padmode)
self.key_size = 8
self.L = []
self.R = []
self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
self.final = []
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Must be 8 bytes."""
_baseDes.setKey(self, key)
self.__create_sub_keys()
def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
# Turn the strings into integers. Python 3 uses a bytes
# class, which already has this behaviour.
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
i = 7
while i >= 0:
if ch & (1 << i) != 0:
result[pos] = 1
else:
result[pos] = 0
pos += 1
i -= 1
return result
def __BitList_to_String(self, data):
"""Turn the list of bits -> data, into a string"""
result = []
pos = 0
c = 0
while pos < len(data):
c += data[pos] << (7 - (pos % 8))
if (pos % 8) == 7:
result.append(c)
c = 0
pos += 1
if _pythonMajorVersion < 3:
return ''.join([ chr(c) for c in result ])
else:
return bytes(result)
def __permutate(self, table, block):
"""Permutate this block with the specified table"""
return list(map(lambda x: block[x], table))
# Transform the secret key, so that it is ready for data processing
# Create the 16 subkeys, K[1] - K[16]
def __create_sub_keys(self):
"""Create the 16 subkeys K[1] to K[16] from the given key"""
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
i = 0
# Split into Left and Right sections
self.L = key[:28]
self.R = key[28:]
while i < 16:
j = 0
# Perform circular left shifts
while j < des.__left_rotations[i]:
self.L.append(self.L[0])
del self.L[0]
self.R.append(self.R[0])
del self.R[0]
j += 1
# Create one of the 16 subkeys through pc2 permutation
self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
i += 1
# Main part of the encryption algorithm, the number cruncher :)
def __des_crypt(self, block, crypt_type):
"""Crypt the block of data through DES bit-manipulation"""
block = self.__permutate(des.__ip, block)
self.L = block[:32]
self.R = block[32:]
# Encryption starts from Kn[1] through to Kn[16]
if crypt_type == des.ENCRYPT:
iteration = 0
iteration_adjustment = 1
# Decryption starts from Kn[16] down to Kn[1]
else:
iteration = 15
iteration_adjustment = -1
i = 0
while i < 16:
# Make a copy of R[i-1], this will later become L[i]
tempR = self.R[:]
# Permutate R[i - 1] to start creating R[i]
self.R = self.__permutate(des.__expansion_table, self.R)
# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
# Optimization: Replaced below commented code with above
#j = 0
#B = []
#while j < len(self.R):
# self.R[j] = self.R[j] ^ self.Kn[iteration][j]
# j += 1
# if j % 6 == 0:
# B.append(self.R[j-6:j])
# Permutate B[1] to B[8] using the S-Boxes
j = 0
Bn = [0] * 32
pos = 0
while j < 8:
# Work out the offsets
m = (B[j][0] << 1) + B[j][5]
n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
# Find the permutation value
v = des.__sbox[j][(m << 4) + n]
# Turn value into bits, add it to result: Bn
Bn[pos] = (v & 8) >> 3
Bn[pos + 1] = (v & 4) >> 2
Bn[pos + 2] = (v & 2) >> 1
Bn[pos + 3] = v & 1
pos += 4
j += 1
# Permutate the concatination of B[1] to B[8] (Bn)
self.R = self.__permutate(des.__p, Bn)
# Xor with L[i - 1]
self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
# Optimization: This now replaces the below commented code
#j = 0
#while j < len(self.R):
# self.R[j] = self.R[j] ^ self.L[j]
# j += 1
# L[i] becomes R[i - 1]
self.L = tempR
i += 1
iteration += iteration_adjustment
# Final permutation of R[16]L[16]
self.final = self.__permutate(des.__fp, self.R + self.L)
return self.final
# Data to be encrypted/decrypted
def crypt(self, data, crypt_type):
"""Crypt the data in blocks, running it through des_crypt()"""
# Error check the data
if not data:
return ''
if len(data) % self.block_size != 0:
if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
if not self.getPadding():
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
else:
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
# print "Len of data: %f" % (len(data) / self.block_size)
if self.getMode() == CBC:
if self.getIV():
iv = self.__String_to_BitList(self.getIV())
else:
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
# Split the data into blocks, crypting each one seperately
i = 0
dict = {}
result = []
#cached = 0
#lines = 0
while i < len(data):
# Test code for caching encryption results
#lines += 1
#if dict.has_key(data[i:i+8]):
#print "Cached result for: %s" % data[i:i+8]
# cached += 1
# result.append(dict[data[i:i+8]])
# i += 8
# continue
block = self.__String_to_BitList(data[i:i+8])
# Xor with IV if using CBC mode
if self.getMode() == CBC:
if crypt_type == des.ENCRYPT:
block = list(map(lambda x, y: x ^ y, block, iv))
#j = 0
#while j < len(block):
# block[j] = block[j] ^ iv[j]
# j += 1
processed_block = self.__des_crypt(block, crypt_type)
if crypt_type == des.DECRYPT:
processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
#j = 0
#while j < len(processed_block):
# processed_block[j] = processed_block[j] ^ iv[j]
# j += 1
iv = block
else:
iv = processed_block
else:
processed_block = self.__des_crypt(block, crypt_type)
# Add the resulting crypted block to our list
#d = self.__BitList_to_String(processed_block)
#result.append(d)
result.append(self.__BitList_to_String(processed_block))
#dict[data[i:i+8]] = d
i += 8
# print "Lines: %d, cached: %d" % (lines, cached)
# Return the full crypted string
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self._padData(data, pad, padmode)
return self.crypt(data, des.ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after decrypting.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self.crypt(data, des.DECRYPT)
return self._unpadData(data, pad, padmode)
#############################################################################
# Triple DES #
#############################################################################
class triple_des(_baseDes):
"""Triple DES encryption/decrytpion class
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key, [mode], [IV])
key -> Bytes containing the encryption key, must be either 16 or
24 bytes long
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
_baseDes.__init__(self, mode, IV, pad, padmode)
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
self.key_size = 24 # Use DES-EDE3 mode
if len(key) != self.key_size:
if len(key) == 16: # Use DES-EDE2 mode
self.key_size = 16
else:
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
if self.getMode() == CBC:
if not self.getIV():
# Use the first 8 bytes of the key
self._iv = key[:self.block_size]
if len(self.getIV()) != self.block_size:
raise ValueError("Invalid IV, must be 8 bytes in length")
self.__key1 = des(key[:8], self._mode, self._iv,
self._padding, self._padmode)
self.__key2 = des(key[8:16], self._mode, self._iv,
self._padding, self._padmode)
if self.key_size == 16:
self.__key3 = self.__key1
else:
self.__key3 = des(key[16:], self._mode, self._iv,
self._padding, self._padmode)
_baseDes.setKey(self, key)
# Override setter methods to work on all 3 keys.
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
_baseDes.setMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setMode(mode)
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
_baseDes.setPadding(self, pad)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadding(pad)
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
_baseDes.setPadMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadMode(mode)
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
_baseDes.setIV(self, IV)
for key in (self.__key1, self.__key2, self.__key3):
key.setIV(IV)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
# Pad the data accordingly.
data = self._padData(data, pad, padmode)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
block = self.__key1.crypt(data[i:i+8], ENCRYPT)
block = self.__key2.crypt(block, DECRYPT)
block = self.__key3.crypt(block, ENCRYPT)
self.__key1.setIV(block)
self.__key2.setIV(block)
self.__key3.setIV(block)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
else:
data = self.__key1.crypt(data, ENCRYPT)
data = self.__key2.crypt(data, DECRYPT)
return self.__key3.crypt(data, ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after
decrypting, no pad character is required for PAD_PKCS5.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
iv = data[i:i+8]
block = self.__key3.crypt(iv, DECRYPT)
block = self.__key2.crypt(block, ENCRYPT)
block = self.__key1.crypt(block, DECRYPT)
self.__key1.setIV(iv)
self.__key2.setIV(iv)
self.__key3.setIV(iv)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
data = ''.join(result)
else:
data = bytes.fromhex('').join(result)
else:
data = self.__key3.crypt(data, DECRYPT)
data = self.__key2.crypt(data, ENCRYPT)
data = self.__key1.crypt(data, DECRYPT)
return self._unpadData(data, pad, padmode)
================================================
FILE: universe/vncdriver/vnc_client.py
================================================
import collections
try:
import cStringIO as StringIO
except ImportError:
from six import StringIO
import logging
import math
import numpy as np
import os
from universe import pyprofile
import re
import struct
import zlib
from twisted.internet import defer, protocol, threads
from universe import utils
from universe.twisty import reactor
from universe.vncdriver import auth, constants, error, screen, server_messages
logger = logging.getLogger(__name__)
# pyprofile.profile.print_frequency = 1
# pyprofile.profile.print_filter = lambda event: event.startswith('vncdriver.recv_rectangle')
class UnknownEncoding(Exception):
pass
def peer_address(peer):
return '{}:{}'.format(peer.host, peer.port)
class Framebuffer(object):
def __init__(self, width, height, server_pixel_format, name):
# self.observer = observer
self.width = width
self.height = height
self.name = name
self.numpy_screen = screen.NumpyScreen(width, height)
self.apply_format(server_pixel_format)
def apply_format(self, server_pixel_format):
self.server_pixel_format = server_pixel_format
(self.bpp, self.depth, self.bigendian, self.truecolor,
self.redmax, self.greenmax, self.bluemax,
self.redshift, self.greenshift, self.blueshift) = \
struct.unpack('!BBBBHHHBBBxxx', server_pixel_format)
# x11vnc will set truecolor == 0 by default. We just have to
# hope the client will override it.
#
# assert self.bigendian == 0
# Don't want to deal with colormaps...
# assert self.truecolor == 1
# Derived values
self.bypp = self.bpp // 8 # bytes per pixel
shifts = [self.redshift, self.greenshift, self.blueshift]
assert set(shifts) == set([0, 8, 16]), 'Surprising pixelformat: {}'.format(self.__dict__)
# How to cycle pixels from images to get RGB
self.color_cycle = np.argsort(shifts)
self.server_init = struct.pack('!HH16sI', self.width, self.height, self.server_pixel_format, len(self.name)) + self.name
self.numpy_screen.color_cycle = self.color_cycle
class VNCClient(protocol.Protocol, object):
def __init__(self):
self.numpy_screen = None
self.buf = []
self.buf_len = 0
self.initialized = False
# These could be passed around in
# expected_args/expected_kwargs, but they are needed in so
# many places it'd become confusing.
self._remaining_rectangles = None
self._rectangles = None
self._pause = False
self.expect(self.recv_ProtocolVersion_Handshake, 12)
self._close = False
self.zlib_decompressor = zlib.decompressobj()
self._pointer_x = None
self._pointer_y = None
def connectionLost(self, reason):
if not self._close:
logger.info('Server %s hung up: %s', peer_address(self.transport.getPeer()), reason)
self._error(error.Error('[{}] Lost connection: {}'.format(self.factory.label, reason)))
def sendMessage(self, data):
pyprofile.incr('vnc_client.data.sent.messages')
pyprofile.incr('vnc_client.data.sent.bytes', len(data), unit=pyprofile.BYTES)
if self.transport:
self.transport.write(data)
def dataReceived(self, data):
pyprofile.incr('vnc_client.data.received.messages')
pyprofile.incr('vnc_client.data.received.bytes', len(data), unit=pyprofile.BYTES)
self.buf.append(data)
self.buf_len += len(data)
logger.debug('Received data: %s bytes (brings us to %s total)', len(data), self.buf_len)
self.flush()
def flush(self):
if self.buf_len < self.expected_len:
return
elif self._pause:
# Not strictly needed, but short circuits in case we're
# paused for a while.
return
elif self._close:
return
buffer = b''.join(self.buf)
while len(buffer) >= self.expected_len:
logger.debug('Remaining in buffer: %s bytes', len(buffer))
if self._pause:
logger.debug('Pausing with %s bytes left in the buffer', len(buffer))
break
block, buffer = buffer[:self.expected_len], buffer[self.expected_len:]
if not self.handle(self.expected, block):
logger.debug('Stopping due to error in handle()')
buffer = block + buffer
break
self.buf[:] = [buffer]
self.buf_len = len(buffer)
def handle(self, type, block):
logger.debug('Handling server event: type=%s', type.__name__)
try:
self.expected(block, *self.expected_args, **self.expected_kwargs)
return True
except Exception as e:
self._error(e)
return False
def recv_ProtocolVersion_Handshake(self, block):
match = re.search(b'^RFB (\d{3}).(\d{3})\n$', block)
assert match, 'Expected RFB line, but got: {!r}'.format(block)
major = int(match.group(1))
minor = int(match.group(2))
self.sendMessage(b'RFB 003.003\n')
self.expect(self.recv_Security_Handshake, 4)
def recv_Security_Handshake(self, block):
(auth,) = struct.unpack('!I', block)
if auth == 0:
self.expect(self.recv_SecurityResult_Handshake_failed_length, 4)
elif auth == 1:
self.send_ClientInit()
elif auth == 2:
self.expect(self.recv_VNC_Authentication, 16)
else:
assert False, 'Bad auth: {}'.format(auth)
def recv_SecurityResult_Handshake(self, block):
(result,) = struct.unpack('!xxxB', block)
if result == 0:
logger.debug('VNC Auth succeeded')
self.send_ClientInit()
elif result == 1:
logger.debug('VNC Auth failed.')
# Server optionally can say why
self.expect(self.recv_SecurityResult_Handshake_failed_length, 4)
else:
assert False, 'Bad security result: {}'.format(result)
def recv_SecurityResult_Handshake_failed_length(self, block):
(length,) = struct.unpack('!I', block)
self.expect(self.recv_SecurityResult_Handshake_failed_reason, length)
def recv_SecurityResult_Handshake_failed_reason(self, block):
logger.info('Connection to server failed: %s', block)
def recv_VNC_Authentication(self, block):
response = auth.challenge_response(block)
self.sendMessage(response)
self.expect(self.recv_SecurityResult_Handshake, 4)
def send_ClientInit(self):
shared = True
self.sendMessage(struct.pack('!B', shared))
self.expect(self.recv_ServerInit, 24)
def recv_ServerInit(self, block):
# This can be rewritten, so don't proxy immediately
(width, height, server_pixel_format, namelen) = struct.unpack('!HH16sI', block)
self.expect(self.recv_ServerInit_name, namelen, width, height, server_pixel_format)
def recv_ServerInit_name(self, block, width, height, server_pixel_format):
self.framebuffer = Framebuffer(width, height, server_pixel_format, block)
self.numpy_screen = self.framebuffer.numpy_screen
self.initialized = True
self.send_PixelFormat()
self.send_SetEncodings([
constants.ZRLE_ENCODING,
constants.ZLIB_ENCODING,
constants.RAW_ENCODING,
])
self.send_FramebufferUpdateRequest(incremental=1)
if self.factory.deferred:
self.factory.deferred.callback(self)
# All connected!
self.expect(self.recv_ServerToClient, 1)
def recv_ServerToClient(self, block):
(message_type,) = struct.unpack('!B', block)
if message_type == 0:
# self.observer.server_data_block(block)
self.expect(self.recv_FramebufferUpdate, 3)
elif message_type == 1:
# self.observer.server_data_block(block)
self.expect(self.recv_SetColorMapEntries, 5)
elif message_type == 2:
# self.observer.server_data_block(block)
# self.observer.server_bell()
self.expect(self.recv_ServerToClient, 1)
elif message_type == 3:
# Just drop ServerCutText messages
self.expect(self.recv_ServerCutText, 7)
else:
assert False, 'Unknown server to client message type received: {}'.format(message_type)
def recv_FramebufferUpdate(self, block):
(number_of_rectangles, ) = struct.unpack('!xH', block)
logger.debug('Receiving %d rectangles', number_of_rectangles)
pyprofile.incr('vncdriver.framebuffer_update')
pyprofile.incr('vncdriver.framebuffer_update.number_of_rectangles', number_of_rectangles)
self._remaining_rectangles = number_of_rectangles
self._rectangles = []
self._process_rectangles()
def _process_rectangles(self):
if self._remaining_rectangles > 0:
self.expect(self.recv_Rectangle, 12)
else:
framebuffer_update = server_messages.FramebufferUpdate(self._rectangles)
self.numpy_screen.apply(framebuffer_update)
self._remaining_rectangles = None
self._rectangles = None
# Once you're done, send another framebufferUpdateRequest.
#
# Empirically, after a few framebufferUpdateRequest's without
# any changes, Xvnc will hold off on sending another
# framebuffer update until the display data changes.
self.send_FramebufferUpdateRequest(incremental=1)
self.expect(self.recv_ServerToClient, 1)
def recv_Rectangle(self, block):
self._remaining_rectangles -= 1
(x, y, width, height, encoding) = struct.unpack('!HHHHi', block)
if encoding == constants.RAW_ENCODING:
self.expect(self.recv_DecodeRAW, width*height*self.framebuffer.bypp, x, y, width, height)
elif encoding == constants.ZRLE_ENCODING:
self.expect(self.recv_DecodeZRLE, 4, x, y, width, height)
elif encoding == constants.ZLIB_ENCODING:
self.expect(self.recv_DecodeZlib, 4, x, y, width, height)
elif encoding == constants.PSEUDO_CURSOR_ENCODING:
length = width * height * self.framebuffer.bypp
length += int(math.floor((width + 7.0) / 8)) * height
self.expect(self.recv_DecodePseudoCursor, length, x, y, width, height)
else:
raise UnknownEncoding('Unknown pixel encoding received: {}'.format(encoding))
# Encodings
def recv_DecodePseudoCursor(self, block, x, y, width, height):
# cf https://github.com/sibson/vncdotool/blob/master/vncdotool/rfb.py
rectangle = server_messages.PseudoCursorEncoding.parse_rectangle(self, x, y, width, height, block)
self._rectangles.append(rectangle)
self._process_rectangles()
def recv_DecodeRAW(self, block, x, y, width, height):
pyprofile.incr('vncdriver.recv_rectangle.raw_encoding')
pyprofile.incr('vncdriver.recv_rectangle.raw_encoding.bytes', len(block), unit=pyprofile.BYTES)
rectangle = server_messages.RAWEncoding.parse_rectangle(self, x, y, width, height, block)
self._rectangles.append(rectangle)
self._process_rectangles()
def recv_DecodeZRLE(self, block, x, y, width, height):
pyprofile.incr('vncdriver.recv_rectangle.zrle_encoding')
pyprofile.incr('vncdriver.recv_rectangle.zrle_encoding.bytes', len(block), unit=pyprofile.BYTES)
(length,) = struct.unpack('!I', block)
self.expect(self.recv_DecodeZRLE_value, length, x, y, width, height)
def recv_DecodeZRLE_value(self, block, x, y, width, height):
pyprofile.incr('vncdriver.recv_rectangle.zrle_encoding.bytes', len(block), unit=pyprofile.BYTES)
rectangle = server_messages.ZRLEEncoding.parse_rectangle(self, x, y, width, height, block)
self._rectangles.append(rectangle)
self._process_rectangles()
def recv_DecodeZlib(self, block, x, y, width, height):
pyprofile.incr('vncdriver.recv_rectangle.zlib_encoding')
pyprofile.incr('vncdriver.recv_rectangle.zlib_encoding.bytes', len(block), unit=pyprofile.BYTES)
(length,) = struct.unpack('!I', block)
self.expect(self.recv_DecodeZlib_value, length, x, y, width, height)
def recv_DecodeZlib_value(self, block, x, y, width, height):
pyprofile.incr('vncdriver.recv_rectangle.zlib_encoding.bytes', len(block), unit=pyprofile.BYTES)
rectangle = server_messages.ZlibEncoding.parse_rectangle(self, x, y, width, height, block)
self._rectangles.append(rectangle)
self._process_rectangles()
def recv_SetColorMapEntries(self, block):
# self.observer.server_data_block(block)
(first_color, number_of_colors) = struct.unpack('!xHH', block)
self._handle_SetColorMapEntries(first_color, number_of_colors, [])
def _handle_SetColorMapEntries(self, first_color, number_of_colors, colors):
if number_of_colors > 0:
self.expect(self.recv_SetColorMapEntries_color, 6, first_color, number_of_colors-1, colors)
else:
# self.observer.server_set_color_map_entries(first_color, colors)
self.expect(self.recv_ServerToClient, 1)
def recv_SetColorMapEntries_color(self, block, first_color, number_of_colors, colors):
# self.observer.server_data_block(block)
red, green, blue = struct.unpack('!HHH', block)
colors.append((red, green, blue))
self._handle_SetColorMapEntries(first_color, number_of_colors, colors)
def recv_ServerCutText(self, block):
# We drop these messages
(length,) = struct.unpack('!xxxI', block)
self.expect(self.recv_ServerCutText_value, length)
def recv_ServerCutText_value(self, block):
# We drop these messages
# self.observer.server_cut_text(block)
self.expect(self.recv_ServerToClient, 1)
def send_SetEncodings(self, encodings):
self.sendMessage(struct.pack("!BxH", 2, len(encodings)))
for encoding in encodings:
self.sendMessage(struct.pack("!i", encoding))
def send_PixelFormat(self, bpp=32, depth=24, bigendian=0, truecolor=1, redmax=255, greenmax=255, bluemax=255, redshift=0, greenshift=8, blueshift=16):
if not self.initialized:
# Not too bad to add, but no need right now. (We'd need to
# make sure the framebuffer settings don't get
# overridden.)
raise error.Error('Framebuffer not initialized. We have not yet added support for queuing PixelFormat messages before initialization')
server_pixel_format = struct.pack("!BBBBHHHBBBxxx", bpp, depth, bigendian, truecolor, redmax, greenmax, bluemax, redshift, greenshift, blueshift)
self.sendMessage(struct.pack("!Bxxx16s", 0, server_pixel_format))
self.framebuffer.apply_format(server_pixel_format)
def send_FramebufferUpdateRequest(self, x=0, y=0, width=None, height=None, incremental=0):
if not self.initialized:
# Not too bad to add, but no need right now. (We'd need to
# calculate the message after the framebuffer is
# initialized.)
raise error.Error('Framebuffer not initialized. We have not yet added support for queuing FramebufferUpdateRequest messages before initialization')
if width is None:
width = self.framebuffer.width - x
if height is None:
height = self.framebuffer.height - y
self.sendMessage(struct.pack("!BBHHHH", 3, incremental, x, y, width, height))
def send_KeyEvent(self, key, down):
"""For most ordinary keys, the "keysym" is the same as the
corresponding ASCII value. Other common keys are shown in the
KEY_ constants.
"""
self.sendMessage(struct.pack('!BBxxI', 4, down, key))
def send_PointerEvent(self, x, y, buttonmask=0):
"""Indicates either pointer movement or a pointer button press or
release. The pointer is now at (x-position, y-position),
and the current state of buttons 1 to 8 are represented by
bits 0 to 7 of button-mask respectively, 0 meaning up, 1
meaning down (pressed).
"""
self.sendMessage(struct.pack('!BBHH', 5, buttonmask, x, y))
def send_ClientCutText(self, message):
"""The client has new text in its clipboard.
"""
self.sendMessage(struct.pack("!BxxxI", 6, len(message)))
self.sendMessage(message)
def expect(self, type, length, *args, **kwargs):
logger.debug('Expecting: %s (length=%s)', type.__name__, length)
assert isinstance(length, int), "Bad length (not an int): {}".format(length)
self.expected = type
self.expected_len = length
self.expected_args = args
self.expected_kwargs = kwargs
def close(self):
self._close = True
if self.transport:
self.transport.loseConnection()
def _error(self, e):
self.close()
self.factory.error_buffer.record(e)
if self.factory.deferred:
try:
self.factory.deferred.errback(utils.format_error(e))
except defer.AlreadyCalledError:
pass
def client_factory(deferred, error_buffer):
factory = protocol.ClientFactory()
factory.deferred = deferred
factory.error_buffer = error_buffer
factory.protocol = VNCClient
return factory
================================================
FILE: universe/vncdriver/vnc_proxy_server.py
================================================
import logging
import os
import re
import shutil
import struct
import time
import traceback
from universe import pyprofile
from universe.vncdriver import auth, constants, fbs_writer
from twisted.internet import defer, endpoints, protocol, reactor
logger = logging.getLogger(__name__)
class LogManager(object):
def __init__(self, base_logfile_dir, recorder_id, id):
self.base_logfile_dir = base_logfile_dir
self.recorder_id = recorder_id
self.id = id
self.logfile_dir = os.path.join(self.base_logfile_dir, '{}-{}-{}'.format(int(time.time()), self.recorder_id, str(self.id)))
logger.info('[vnc_proxy] logfile_dir = %s', self.logfile_dir)
# Make the logfile directory that will be written to
if not os.path.isdir(self.logfile_dir):
logger.info('[vnc_proxy] [%s] Creating log directory %s', self.id, self.logfile_dir)
os.makedirs(self.logfile_dir)
@property
def global_rewards_logfile(self):
"""
We assume that there is only a single rewards file that is ever written to.
This is enforced by the reward_recorder that limits connections to one
"""
return '/tmp/demo/rewards.demo'
@property
def global_botaction_logfile(self):
"""
We assume that there is only a single botactions file that is ever written to.
This is enforced by the botaction_recorder that appends everything to one file
"""
return '/tmp/demo/botactions.jsonl'
@property
def global_env_id_file(self):
"""
We assume that there is only a single rewards file that is ever written to.
This is enforced by the reward_recorder that limits connections to one
"""
return '/tmp/demo/env_id.txt'
@property
def server_logfile(self):
return os.path.join(self.logfile_dir, 'server.fbs')
@property
def client_logfile(self):
return os.path.join(self.logfile_dir, 'client.fbs')
def close(self):
logger.info("[%s] Copying rewards.demo into this connection's log dir", self.id)
if os.path.exists(self.global_rewards_logfile):
# Each recording includes all the rewards that we've seen. We count on the player to
# crop them to only contain the ones that occurred during this recording
# TODO: Upload to S3 immediately upon disconnect
shutil.copyfile(self.global_rewards_logfile, os.path.join(self.logfile_dir, 'rewards.demo'))
else:
logger.info("%s does not exist; not copying into recording directory", self.global_rewards_logfile)
if os.path.exists(self.global_botaction_logfile):
shutil.copyfile(self.global_botaction_logfile, os.path.join(self.logfile_dir, 'botactions.jsonl'))
else:
logger.info("%s does not exist; not copying into recording directory", self.global_botaction_logfile)
if os.path.exists(self.global_env_id_file):
shutil.copyfile(self.global_env_id_file, os.path.join(self.logfile_dir, 'env_id.txt'))
else:
logger.info("%s does not exist; not copying into recording directory", self.global_env_id_file)
if os.environ.get('COMPLETED_DEMONSTRATION_DIR'):
dest = os.path.join(os.environ['COMPLETED_DEMONSTRATION_DIR'], os.path.basename(self.logfile_dir))
logger.info('copying to %s', dest)
shutil.copytree(self.logfile_dir, dest)
shutil.copystat(self.logfile_dir, dest)
class VNCProxyServer(protocol.Protocol, object):
"""Bytes received from the end user. (So received data are mostly
actions.)"""
_next_id = 0
SUPPORTED_ENCODINGS = {
# Maybe we can do copy-rect at some point. May not help with
# much though.
# constants.COPY_RECTANGLE_ENCODING,
constants.TIGHT_ENCODING,
constants.RAW_ENCODING,
constants.ZLIB_ENCODING,
# constants.HEXTILE_ENCODING,
# constants.CORRE_ENCODING,
# constants.RRE_ENCODING,
constants.PSEUDO_CURSOR_ENCODING
}
if os.getenv('VNC_ENCODINGS'):
for enc in os.getenv('VNC_ENCODINGS').split():
SUPPORTED_ENCODINGS.add(getattr(constants, enc.upper() + '_ENCODING'))
add_pseudo_cursor_encoding = True
if os.getenv('VNC_ENCODINGS_NO_ADD_PSEUDO_CURSOR_ENCODING'):
add_pseudo_cursor_encoding = False
# ZRLE format is much smaller, but slower to read.
# SUPPORTED_ENCODINGS.add(constants.ZRLE_ENCODING)
@classmethod
def next_id(cls):
id = cls._next_id
cls._next_id += 1
return id
def __init__(self, action_queue=None, error_buffer=None, enable_logging=True):
self.id = self.next_id()
self.server_log = None
self.action_queue = action_queue
self.error_buffer = error_buffer
self.server_log_buffer = []
self.enable_logging = enable_logging
self._broken = False
self.vnc_client = None
self.log_manager = None
self.buf = []
self.buf_len = 0
self.queued_data = []
self.initialized = False
self.challenge = auth.challenge()
self.accept_any_password = True
self.expect(self.recv_ProtocolVersion_Handshake, 12)
def start_logging(self):
self.log_manager = LogManager(self.factory.logfile_dir, self.factory.recorder_id, self.id)
self.server_log = fbs_writer.FBSWriter(self.log_manager.server_logfile)
for data in self.server_log_buffer:
self.server_log.write(data)
# Done with the buffer!
self.server_log_buffer = None
def connectionMade(self):
logger.info('[%s] Connection received from VNC client', self.id)
factory = protocol.ClientFactory()
factory.protocol = VNCProxyClient
factory.vnc_server = self
factory.deferrable = defer.Deferred()
endpoint = endpoints.clientFromString(reactor, self.factory.vnc_address)
def _established_callback(client):
if self._broken:
client.close()
self.vnc_client = client
self.flush()
def _established_errback(reason):
logger.error('[VNCProxyServer] Connection succeeded but could not establish session: %s', reason)
self.close()
factory.deferrable.addCallbacks(_established_callback, _established_errback)
def _connect_errback(reason):
logger.error('[VNCProxyServer] Connection failed: %s', reason)
self.close()
endpoint.connect(factory).addErrback(_connect_errback)
self.send_ProtocolVersion_Handshake()
def connectionLost(self, reason):
logger.info('Losing connection from VNC user')
if self.vnc_client:
self.vnc_client.close()
def dataReceived(self, data):
pyprofile.incr('vnc_proxy_server.data.sent.messages')
pyprofile.incr('vnc_proxy_server.data.sent.bytes', len(data))
self.buf.append(data)
self.buf_len += len(data)
self.flush()
def sendData(self, data):
if self.server_log is None:
self.server_log_buffer.append(data)
else:
self.server_log.write(data)
self.transport.write(data)
def flush(self):
if self.buf_len < self.expected_len:
return
elif self.vnc_client is None and self.action_queue is None:
return
buffer = b''.join(self.buf)
while not self._broken and len(buffer) >= self.expected_len:
block, buffer = buffer[:self.expected_len], buffer[self.expected_len:]
self.handle(self.expected, block)
self.buf[:] = [buffer]
self.buf_len = len(buffer)
def handle(self, type, block):
logger.debug('[%s] Handling: type=%s', self.id, type)
try:
self.expected(block, *self.expected_args, **self.expected_kwargs)
except Exception as e:
self._error(e)
def send_ProtocolVersion_Handshake(self):
self.sendData(b'RFB 003.003\n')
def recv_ProtocolVersion_Handshake(self, block):
# Client chooses RFB version
match = re.search(b'^RFB (\d{3}).(\d{3})\n$', block)
assert match, 'Block does not match: {!r}'.format(block)
major = int(match.group(1))
minor = int(match.group(2))
self.protocol_version = (major, minor)
assert major == 3 and minor in (3, 8), 'Unexpected version: {}'.format((major, minor))
if minor == 3:
self.send_VNC_Authentication()
elif minor == 8:
self.send_SecurityTypes()
def send_SecurityTypes(self):
self.sendData(struct.pack('!BB', 1, 2))
self.expect(self.recv_SecurityTypesResponse, 1)
def recv_SecurityTypesResponse(self, block):
(type,) = struct.unpack('!B', block)
assert type == 2
self.send_VNC_Authentication()
def send_VNC_Authentication(self):
# Now we tell the client to auth with password. (Only do this
# because the built-in Mac viewer prompts for a password no
# matter what.)
self.sendData(struct.pack('!I', 2))
self.sendData(self.challenge)
self.expect(self.recv_VNC_Authentication_response, 16)
def recv_VNC_Authentication_response(self, block):
expected = auth.challenge_response(self.challenge)
if block == expected or self.accept_any_password:
logger.debug('Client authenticated successfully')
self.send_SecurityResult_Handshake_success()
else:
logger.debug('VNC client supplied incorrect password')
self.send_SecurityResult_Handshake_failed('Your password was incorrect')
def send_SecurityResult_Handshake_success(self):
logger.debug('[%s] Send SecurityResult_Handshake_success', self.id)
self.sendData(struct.pack('!I', 0))
self.expect(self.recv_ClientInit, 1)
def send_SecurityResult_Handshake_failed(self, reason):
self.sendData(struct.pack('!I', 1))
self.sendData(struct.pack('!I', len(reason)))
self.sendData(reason)
self.close()
def recv_ClientInit(self, block):
(shared,) = struct.unpack('!B', block)
### Now that the server is up and running, we flush
### everything.
# We don't even create log directory until we've successfully
# established the connection.
if self.enable_logging and self.factory.logfile_dir:
# Log in vnc_recorder; don't log in playback
self.start_logging()
self.vnc_client.start_logging()
for data in self.queued_data:
self.sendData(data)
self.queued_data = None
self.initialized = True
# Listen for messages
self.expect(self.recv_ClientToServer, 1)
def recv_ClientToServer(self, block):
(message_type,) = struct.unpack('!B', block)
if message_type == 0:
self.proxyData(block)
self.expect(self.recv_SetPixelFormat, 19)
elif message_type == 2:
# Do not proxy since we want to transform it
self.expect(self.recv_SetEncodings, 3)
elif message_type == 3:
self.proxyData(block)
self.expect(self.recv_FramebufferUpdateRequest, 9)
elif message_type == 4:
self.proxyData(block)
self.expect(self.recv_KeyEvent, 7)
elif message_type == 5:
self.proxyData(block)
self.expect(self.recv_PointerEvent, 5)
elif message_type == 6:
# Do not proxy since we don't support it
self.expect(self.recv_ClientCutText, 7)
else:
assert False, 'Unknown client to server message type received: {}'.format(message_type)
def recv_SetPixelFormat(self, block):
self.proxyData(block)
(server_pixel_format,) = struct.unpack('!xxx16s', block)
if self.action_queue:
self.action_queue.set_pixel_format(server_pixel_format)
# self.vnc_client.framebuffer.apply_format(server_pixel_format)
self.expect(self.recv_ClientToServer, 1)
def recv_SetEncodings(self, block):
# Do not proxy, as we will write our own transformed version
# of this shortly.
(number_of_encodings,) = struct.unpack('!xH', block)
self._handle_SetEncodings(number_of_encodings, [])
def _handle_SetEncodings(self, number_of_encodings, encodings):
if number_of_encodings > 0:
self.expect(self.recv_SetEncodings_encoding_type, 4, number_of_encodings-1, encodings)
else:
supported = []
unsupported = []
for encoding in encodings:
if encoding in self.SUPPORTED_ENCODINGS:
supported.append(encoding)
else:
unsupported.append(encoding)
if unsupported:
logger.info('[%s] Requested %s unsupported encodings: unsupported=%s supported=%s', self.id, len(unsupported), unsupported, supported)
if self.add_pseudo_cursor_encoding and constants.PSEUDO_CURSOR_ENCODING not in supported:
logger.info('[%s] Add PSEUDO_CURSOR_ENCODING', self.id)
supported.append(constants.PSEUDO_CURSOR_ENCODING)
logger.debug('[%s] Encodings: %s', self.id, supported)
if self.vnc_client:
self.vnc_client.send_SetEncodings(supported)
self.expect(self.recv_ClientToServer, 1)
def recv_SetEncodings_encoding_type(self, block, number_of_encodings, encodings):
# Do not proxy, as we will write our own transformed version
# of this shortly.
(encoding,) = struct.unpack('!i', block)
encodings.append(encoding)
self._handle_SetEncodings(number_of_encodings, encodings)
def recv_FramebufferUpdateRequest(self, block):
self.proxyData(block)
incremental, x, y, width, height = struct.unpack('!BHHHH', block)
self.expect(self.recv_ClientToServer, 1)
def recv_KeyEvent(self, block):
self.proxyData(block)
down, key = struct.unpack('!BxxI', block)
if self.action_queue is not None:
self.action_queue.key_event(key, down)
self.expect(self.recv_ClientToServer, 1)
def recv_PointerEvent(self, block):
self.proxyData(block)
buttonmask, x, y = struct.unpack('!BHH', block)
if self.action_queue is not None:
self.action_queue.pointer_event(x, y, buttonmask)
self.expect(self.recv_ClientToServer, 1)
def recv_ClientCutText(self, block):
# Drop ClientCutText
(length,) = struct.unpack('!xxxI', block)
self.expect(self.recv_ClientCutText_value, length)
def recv_ClientCutText_value(self, block):
# Drop ClientCutText
self.expect(self.recv_ClientToServer, 1)
def expect(self, type, length, *args, **kwargs):
self.expected = type
self.expected_len = length
self.expected_args = args
self.expected_kwargs = kwargs
def proxyData(self, data):
if self.vnc_client:
self.vnc_client.recvProxyData(data)
def recvProxyData(self, data):
"""Write data to server"""
if self.initialized:
self.sendData(data)
else:
self.queued_data.append(data)
def close(self):
logger.info('[%s] Closing', self.id)
self._broken = True
if self.transport:
self.transport.loseConnection()
if self.log_manager:
self.log_manager.close()
if self.server_log:
self.server_log.close()
def _error(self, e):
logger.error('[%s] Connection from client aborting with error: %s', self.id, e)
traceback.print_exc()
if self.error_buffer:
self.error_buffer.record(e)
self.close()
class VNCProxyClient(protocol.Protocol, object):
def __init__(self):
self.id = None
self.vnc_server = None
self.client_log = None
# Messages heading for the server, which haven't been written
# to disk.
self.client_log_buffer = []
self.buf = []
self.buf_len = 0
self._broken = False
self.queued_data = []
self.initialized = False
self.expect(self.recv_ProtocolVersion_Handshake, 12)
def start_logging(self):
self.client_log = fbs_writer.FBSWriter(self.vnc_server.log_manager.client_logfile)
for data in self.client_log_buffer:
self.client_log.write(data)
# Done with the buffer!
self.client_log_buffer = None
def connectionMade(self):
logger.debug('Connection made to VNC server')
self.vnc_server = self.factory.vnc_server
self.id = '{}-client'.format(self.vnc_server.id)
def connectionLost(self, reason):
logger.info('Losing connection to VNC server')
if self.vnc_server:
self.vnc_server.close()
def proxyData(self, data):
"""Write data to client"""
assert self.initialized
self.vnc_server.recvProxyData(data)
def recvProxyData(self, data):
"""Write data to client"""
self.sendData(data)
def sendData(self, data):
"""Write data to server"""
# Not set up yet
if self.client_log is None:
self.client_log_buffer.append(data)
else:
self.client_log.write(data)
self.transport.write(data)
def dataReceived(self, data):
if self.expected is None:
# We're in direct proxy mode
self.proxyData(data)
return
pyprofile.incr('vnc_proxy_server.data.sent.messages')
pyprofile.incr('vnc_proxy_server.data.sent.bytes', len(data))
self.buf.append(data)
self.buf_len += len(data)
self.flush()
def flush(self):
if self.buf_len < self.expected_len:
return
buffer = b''.join(self.buf)
while self.expected is not None and not self._broken and len(buffer) >= self.expected_len:
block, buffer = buffer[:self.expected_len], buffer[self.expected_len:]
self.handle(self.expected, block)
self.buf[:] = [buffer]
self.buf_len = len(buffer)
if self.expected is None:
data = b''.join(self.buf)
if data != '':
self.proxyData(data)
self.buf = []
def handle(self, type, block):
logger.debug('[%s] Handling: type=%s', self.id, type)
try:
self.expected(block, *self.expected_args, **self.expected_kwargs)
except Exception as e:
self._error(e)
def recv_ProtocolVersion_Handshake(self, block):
match = re.search(b'^RFB (\d{3}).(\d{3})\n$', block)
assert match, 'Expected RFB line, but got: {!r}'.format(block)
major = int(match.group(1))
minor = int(match.group(2))
self.sendData(b'RFB 003.003\n')
self.expect(self.recv_Security_Handshake, 4)
def recv_Security_Handshake(self, block):
(auth,) = struct.unpack('!I', block)
if auth == 0:
self.expect(self.recv_SecurityResult_Handshake_failed_length, 4)
elif auth == 1:
self.send_ClientInit()
elif auth == 2:
self.expect(self.recv_VNC_Authentication, 16)
else:
assert False, 'Bad auth: {}'.format(auth)
def recv_VNC_Authentication(self, block):
response = auth.challenge_response(block)
self.sendData(response)
self.expect(self.recv_SecurityResult_Handshake, 4)
def recv_SecurityResult_Handshake(self, block):
(result,) = struct.unpack('!xxxB', block)
if result == 0:
logger.debug('VNC Auth succeeded')
self.send_ClientInit()
elif result == 1:
logger.debug('VNC Auth failed.')
# Server optionally can say why
self.expect(self.recv_SecurityResult_Handshake_failed_length, 4)
else:
assert False, 'Bad security result: {}'.format(result)
def recv_SecurityResult_Handshake_failed_length(self, block):
(length,) = struct.unpack('!I', block)
self.expect(self.recv_SecurityResult_Handshake_failed_reason, length)
def recv_SecurityResult_Handshake_failed_reason(self, block):
logger.info('Connection to server failed: %s', block)
def send_ClientInit(self):
shared = True
self.sendData(struct.pack('!B', shared))
### Now that the session is up and running, we flush
### everything and stop parsing.
# We're up and running!
logger.info('[%s] Marking server as connected', self.id)
self.factory.deferrable.callback(self)
self.initialized = True
# Flush queue
for data in self.queued_data:
self.sendData(data)
self.queued_data = None
# And from now on just do a straight proxy
self.expect(None, None)
def expect(self, type, length, *args, **kwargs):
if type is not None:
logger.debug('Expecting: %s (length=%s)', type.__name__, length)
assert isinstance(length, int), "Bad length: {}".format(length)
self.expected = type
self.expected_len = length
self.expected_args = args
self.expected_kwargs = kwargs
def close(self):
logger.info('[%s] Closing', self.id)
self._broken = True
if self.transport:
self.transport.loseConnection()
if self.client_log:
self.client_log.close()
def _error(self, e):
logger.error('[%s] Connection to server aborting with error: %s', self.id, e)
traceback.print_exc()
self.close()
def send_SetEncodings(self, encodings):
self.sendData(struct.pack("!BxH", 2, len(encodings)))
for encoding in encodings:
self.sendData(struct.pack("!i", encoding))
================================================
FILE: universe/vncdriver/vnc_session.py
================================================
import logging
from twisted.internet import defer, endpoints
from universe import error, utils
from universe.twisty import reactor
from universe.vncdriver import screen, vnc_client
logger = logging.getLogger(__name__)
class VNCSession(object):
def __init__(self, remotes, error_buffer):
self.remotes = remotes
self.error_buffer = error_buffer
self._pyglet_screen = None
self.connect()
def connect(self):
utils.blockingCallFromThread(self._connect)
def _connect(self):
deferreds = []
for i, remote in enumerate(self.remotes):
d = defer.Deferred()
deferreds.append(d)
factory = vnc_client.client_factory(d, self.error_buffer)
factory.rewarder_session = self
factory.label = 'vnc:{}:{}'.format(i, remote)
endpoint = endpoints.clientFromString(reactor, 'tcp:'+remote)
def success(i):
logger.info('[%s] VNC connection established', factory.label)
def fail(reason):
reason = error.Error('[{}] Connection failed: {}'.format(factory.label, reason.value))
try:
d.errback(utils.format_error(reason))
except defer.AlreadyCalledError:
pass
endpoint.connect(factory).addCallback(success).addErrback(fail)
d = defer.DeferredList(deferreds, fireOnOneErrback=True)
def success(results):
# Store the _clients list when connected
self._clients = [client for success, client in results]
d.addCallback(success)
return d
def flip(self):
observation_n = []
info_n = []
for i, client in enumerate(self._clients):
observation, info = client.numpy_screen.flip()
updates = info['vnc_session.framebuffer_updates']
# Keep the pyglet screen fed, but don't flip it until the user calls render
if i == 0 and self._pyglet_screen:
for update in updates:
self._pyglet_screen.apply(update)
observation_n.append(observation)
info_n.append({'vnc.updates.n': len(updates)})
return observation_n, info_n
def peek(self):
observations = [client.numpy_screen.peek() for client in self._clients]
return observations
def step(self, action):
reactor.callFromThread(self._step, action)
return self.flip()
def _step(self, action):
try:
for a, client in zip(action, self._clients):
for event in a:
if event[0] == 'KeyEvent':
key, down = event[1:]
client.send_KeyEvent(key, down)
elif event[0] == 'PointerEvent':
x, y, buttomask = event[1:]
client.send_PointerEvent(x, y, buttomask)
else:
raise error.Error('Bad event type: {}'.format(type))
except Exception as e:
self.error_buffer.record(e)
def render(self):
if not self._pyglet_screen:
start = self.peek()[0]
self._pyglet_screen = screen.PygletScreen(start)
self._pyglet_screen.flip()
def close(self):
utils.blockingCallFromThread(self._close)
def _close(self):
if getattr(self, '_clients', None) is not None:
for client in self._clients:
client.close()
self._clients = None
================================================
FILE: universe/wrappers/__init__.py
================================================
import gym
import universe.wrappers.experimental
from universe import envs, spaces
from universe.wrappers import gym_core_sync
from universe.wrappers.blocking_reset import BlockingReset
from universe.wrappers.diagnostics import Diagnostics
from universe.wrappers.gym_core import GymCoreAction, GymCoreObservation, CropAtari
from universe.wrappers.joint import Joint
from universe.wrappers.logger import Logger
from universe.wrappers.monitoring import Monitor
from universe.wrappers.multiprocessing_env import WrappedMultiprocessingEnv, EpisodeID
from universe.wrappers.recording import Recording
from universe.wrappers.render import Render
from universe.wrappers.throttle import Throttle
from universe.wrappers.time_limit import TimeLimit
from universe.wrappers.timer import Timer
from universe.wrappers.vectorize import Vectorize, Unvectorize, WeakUnvectorize
from universe.wrappers.vision import Vision
def wrap(env):
return Timer(Render(Throttle(env)))
def WrappedVNCEnv():
return wrap(envs.VNCEnv())
def WrappedGymCoreEnv(gym_core_id, fps=None, rewarder_observation=False):
# Don't need to store the ID on the instance; it'll be retrieved
# directly from the spec
env = wrap(envs.VNCEnv(fps=fps))
if rewarder_observation:
env = GymCoreObservation(env, gym_core_id=gym_core_id)
return env
def WrappedGymCoreSyncEnv(gym_core_id, fps=60, rewarder_observation=False):
spec = gym.spec(gym_core_id)
env = gym_core_sync.GymCoreSync(BlockingReset(wrap(envs.VNCEnv(fps=fps))))
if rewarder_observation:
env = GymCoreObservation(env, gym_core_id=gym_core_id)
elif spec._entry_point.startswith('gym.envs.atari:'):
env = CropAtari(env)
return env
def WrappedFlashgamesEnv():
keysym = spaces.KeyEvent.by_name('`').key
return wrap(envs.VNCEnv(probe_key=keysym))
def WrappedInternetEnv(*args, **kwargs):
return wrap(envs.InternetEnv(*args, **kwargs))
def WrappedStarCraftEnv(*args, **kwargs):
return wrap(envs.StarCraftEnv(*args, **kwargs))
def WrappedGTAVEnv(*args, **kwargs):
return wrap(envs.GTAVEnv(*args, **kwargs))
def WrappedWorldOfGooEnv(*args, **kwargs):
return wrap(envs.WorldOfGooEnv(*args, **kwargs))
================================================
FILE: universe/wrappers/action_space.py
================================================
class SoftmaxClickMouse():
def init(self):
raise DeprecationWarning('DEPRECATION WARNING: wrappers.SoftmaxClickMouse has been moved to wrappers.experimental.action_space.SoftmaxClickMouse as of 2017-02-08.')
class SafeActionSpace():
def init(self):
raise DeprecationWarning('DEPRECATION WARNING: wrappers.SafeActionSpace has been moved to '
'wrappers.experimental.action_space.SafeActionSpace as of 2017-01-07. '
'Using legacy wrappers.SafeActionSpace will soon be removed')
================================================
FILE: universe/wrappers/blocking_reset.py
================================================
from universe import rewarder, spaces, vectorized
class BlockingReset(vectorized.Wrapper):
"""
By default, a reset in universe is not a blocking operation. This
wrapper changes it.
"""
def __init__(self, *args, **kwargs):
super(BlockingReset, self).__init__(*args, **kwargs)
self.reward_n = None
self.done_n = None
self.info = None
def _reset(self):
observation_n = self.env.reset()
self.reward_n = [0] * self.n
self.done_n = [False] * self.n
self.info = {'n': [{} for _ in range(self.n)]}
while any(ob is None for ob in observation_n):
action_n = []
for done in self.done_n:
if done:
# No popping of reward/done. Don't want to merge across episode boundaries.
action_n.append([spaces.PeekReward])
else:
action_n.append([])
new_observation_n, new_reward_n, new_done_n, new_info = self.env.step(action_n)
rewarder.merge_n(
observation_n, self.reward_n, self.done_n, self.info,
new_observation_n, new_reward_n, new_done_n, new_info
)
return observation_n
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
if self.reward_n is not None:
rewarder.merge_n(
observation_n, reward_n, done_n, info,
[None] * self.n, self.reward_n, self.done_n, self.info
)
self.reward_n = self.done_n = self.info = None
while any(ob is None for ob in observation_n):
action_n = []
for done in done_n:
if done:
# No popping of reward/done. Don't want to merge across episode boundaries.
action_n.append([spaces.PeekReward])
else:
action_n.append([])
new_observation_n, new_reward_n, new_done_n, new_info = self.env.step(action_n)
rewarder.merge_n(
observation_n, reward_n, done_n, info,
new_observation_n, new_reward_n, new_done_n, new_info
)
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/diagnostics.py
================================================
import logging
import six
from universe import pyprofile, vectorized
logger = logging.getLogger(__name__)
# Not used in core; but used in play_flashgames
class Diagnostics(vectorized.Wrapper):
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
# We want this to be above Mask, so we know whether or not a
# particular index is resetting.
if self.unwrapped.diagnostics:
with pyprofile.push('vnc_env.diagnostics.add_metadata'):
self.unwrapped.diagnostics.add_metadata(observation_n, info['n'])
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/experimental/__init__.py
================================================
from universe.wrappers.experimental.action_space import SafeActionSpace, SoftmaxClickMouse
from universe.wrappers.experimental.observation import CropObservations
from universe.wrappers.experimental.random_env import RandomEnv
================================================
FILE: universe/wrappers/experimental/action_space.py
================================================
import logging
import gym
import numpy as np
from universe import spaces
from universe import vectorized
from universe.wrappers.gym_core import gym_core_action_space
logger = logging.getLogger(__name__)
def slither_vnc(space=False, left=False, right=False):
return [spaces.KeyEvent.by_name('space', down=space),
spaces.KeyEvent.by_name('left', down=left),
spaces.KeyEvent.by_name('right', down=right)]
def racing_vnc(up=False, left=False, right=False):
return [spaces.KeyEvent.by_name('up', down=up),
spaces.KeyEvent.by_name('left', down=left),
spaces.KeyEvent.by_name('right', down=right)]
def platform_vnc(up=False, left=False, right=False, space=False):
return [spaces.KeyEvent.by_name('up', down=up),
spaces.KeyEvent.by_name('left', down=left),
spaces.KeyEvent.by_name('right', down=right),
spaces.KeyEvent.by_name('space', down=space)]
class SafeActionSpace(vectorized.Wrapper):
"""
Recall that every universe environment receives a list of VNC events as action.
There exist many environments for which the set of relevant action is much smaller
and is known. For example, Atari environments have a modest number of keys,
so this wrapper, when applied to an Atari environment will reduce its action space.
Doing so is very convenient for research, since today's RL algorithms rely on random
exploration, which is hurt by small action spaces. As our algorithms get better
and we switch to using the raw VNC commands, this wrapper will become less important.
"""
def __init__(self, env):
super(SafeActionSpace, self).__init__(env)
if self.spec.tags.get('runtime') == 'gym-core':
self.action_space = gym_core_action_space(self.spec._kwargs['gym_core_id'])
elif self.spec is None:
pass
elif self.spec.id in ['internet.SlitherIO-v0',
'internet.SlitherIOErmiyaEskandaryBot-v0',
'internet.SlitherIOEasy-v0']:
self.action_space = spaces.Hardcoded([slither_vnc(left=True),
slither_vnc(right=True),
slither_vnc(space=True),
slither_vnc(left=True, space=True),
slither_vnc(right=True, space=True)])
elif self.spec.id in ['flashgames.DuskDrive-v0']:
# TODO: be more systematic
self.action_space = spaces.Hardcoded([racing_vnc(up=True),
racing_vnc(left=True),
racing_vnc(right=True)])
elif self.spec.id in ['flashgames.RedBeard-v0']:
self.action_space = spaces.Hardcoded([platform_vnc(up=True),
platform_vnc(left=True),
platform_vnc(right=True),
platform_vnc(space=True)])
class SoftmaxClickMouse(vectorized.ActionWrapper):
"""
Creates a Discrete action space of mouse clicks.
This wrapper divides the active region into cells and creates an action for
each which clicks in the middle of the cell.
"""
def __init__(self, env, active_region=(10, 75 + 50, 10 + 160, 75 + 210), discrete_mouse_step=10, noclick_regions=[]):
super(SoftmaxClickMouse, self).__init__(env)
logger.info('Using SoftmaxClickMouse with action_region={}, noclick_regions={}'.format(active_region, noclick_regions))
xlow, ylow, xhigh, yhigh = active_region
xs = range(xlow, xhigh, discrete_mouse_step)
ys = range(ylow, yhigh, discrete_mouse_step)
self.active_region = active_region
self.discrete_mouse_step = discrete_mouse_step
self.noclick_regions = noclick_regions
self._points = []
removed = 0
for x in xs:
for y in ys:
xc = min(x+int(discrete_mouse_step/2), xhigh-1) # click to center of a cell
yc = min(y+int(discrete_mouse_step/2), yhigh-1)
if any(self.is_contained((xc, yc), r) for r in noclick_regions):
removed += 1
continue
self._points.append((xc, yc))
logger.info('SoftmaxClickMouse noclick regions removed {} of {} actions'.format(removed, removed + len(self._points)))
self.action_space = gym.spaces.Discrete(len(self._points))
def _action(self, action_n):
return [self._discrete_to_action(int(i)) for i in action_n]
def _discrete_to_action(self, i):
xc, yc = self._points[i]
return [
spaces.PointerEvent(xc, yc, buttonmask=0), # release
spaces.PointerEvent(xc, yc, buttonmask=1), # click
spaces.PointerEvent(xc, yc, buttonmask=0), # release
]
def _reverse_action(self, action):
xlow, ylow, xhigh, yhigh = self.active_region
try:
# find first valid mousedown, ignore everything else
click_event = next(e for e in action if isinstance(e, spaces.PointerEvent) and e.buttonmask == 1)
index = self._action_to_discrete(click_event)
if index is None:
return np.zeros(len(self._points))
else:
# return one-hot vector, expected by demo training code
# FIXME(jgray): move one-hot translation to separate layer
return np.eye(len(self._points))[index]
except StopIteration:
# no valid mousedowns
return np.zeros(len(self._points))
def _action_to_discrete(self, event):
assert isinstance(event, spaces.PointerEvent)
x, y = event.x, event.y
step = self.discrete_mouse_step
xlow, ylow, xhigh, yhigh = self.active_region
xc = min((int((x - xlow) / step) * step) + xlow + step / 2, xhigh - 1)
yc = min((int((y - ylow) / step) * step) + ylow + step / 2, yhigh - 1)
try:
return self._points.index((xc, yc))
except ValueError:
# ignore clicks outside of active region or in noclick regions
return None
@classmethod
def is_contained(cls, point, coords):
px, py = point
x, width, y, height = coords
return x <= px <= x + width and y <= py <= y + height
================================================
FILE: universe/wrappers/experimental/observation.py
================================================
import logging
from universe import vectorized, runtime_spec
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
def CropObservations(env):
""""
Crops the visual observations of an environment so that they only contain the game screen.
Removes anything outside the game that usually belongs to universe (browser borders and so on).
"""
if env.spec.tags.get('flashgames', False):
spec = runtime_spec('flashgames').server_registry[env.spec.id]
return _CropObservations(env, x=18, y=84, height=spec["height"], width=spec["width"])
elif (env.spec.tags.get('atari', False) and env.spec.tags.get('vnc', False)):
return _CropObservations(env, height=194, width=160)
else:
# if unknown environment (or local atari), do nothing
return env
class _CropObservations(vectorized.ObservationWrapper):
def __init__(self, env, height, width, x=0, y=0):
super(_CropObservations, self).__init__(env)
self.x = x
self.y = y
self.height = height
self.width = width
# modify observation_space? (if so, how to know depth and channels before we have seen the first frame?)
# self.observation_space = Box(0, 255, shape=(height, width, 3))
def _observation(self, observation_n):
return [self._crop_frame(observation) for observation in observation_n]
def _crop_frame(self, frame):
if frame is not None:
if isinstance(frame, dict):
frame['vision'] = frame['vision'][self.y:self.y + self.height, self.x:self.x + self.width]
else:
frame = frame[self.y:self.y + self.height, self.x:self.x + self.width]
return frame
================================================
FILE: universe/wrappers/experimental/random_env.py
================================================
import logging
from universe import vectorized
logger = logging.getLogger(__name__)
class RandomEnv(vectorized.Wrapper):
'''
Randomly sample from a list of env_ids between episodes.
Passes a list of env_ids to configure. When done=True, calls env.reset()
to sample from the list.
'''
def __init__(self, env, env_ids):
super(RandomEnv, self).__init__(env)
self.env_ids = env_ids
def configure(self, **kwargs):
super(RandomEnv, self).configure(sample_env_ids=self.env_ids, **kwargs)
def _reset(self):
observation_n = self.env.reset()
return [ob['vision'] if ob is not None else ob for ob in observation_n]
def _step(self, action_n):
assert self.n == 1
observation, reward, done, info = self.env.step(action_n)
if any(done):
self.env.reset()
return observation, reward, done, info
================================================
FILE: universe/wrappers/gym_core.py
================================================
import logging
import gym
import time
import numpy as np
from universe import error
from gym import spaces as gym_spaces
from universe import spaces
from universe import rewarder, vectorized
from universe.envs.vnc_core_env import translator
logger = logging.getLogger(__name__)
ATARI_HEIGHT = 210
ATARI_WIDTH = 160
def atari_vnc(up=False, down=False, left=False, right=False, z=False):
return [spaces.KeyEvent.by_name('up', down=up),
spaces.KeyEvent.by_name('left', down=left),
spaces.KeyEvent.by_name('right', down=right),
spaces.KeyEvent.by_name('down', down=down),
spaces.KeyEvent.by_name('z', down=z)]
def gym_core_action_space(gym_core_id):
spec = gym.spec(gym_core_id)
if spec.id == 'CartPole-v0':
return spaces.Hardcoded([[spaces.KeyEvent.by_name('left', down=True)],
[spaces.KeyEvent.by_name('left', down=False)]])
elif spec._entry_point.startswith('gym.envs.atari:'):
actions = []
env = spec.make()
for action in env.unwrapped.get_action_meanings():
z = 'FIRE' in action
left = 'LEFT' in action
right = 'RIGHT' in action
up = 'UP' in action
down = 'DOWN' in action
translated = atari_vnc(up=up, down=down, left=left, right=right, z=z)
actions.append(translated)
return spaces.Hardcoded(actions)
else:
raise error.Error('Unsupported env type: {}'.format(spec.id))
class CropAtari(vectorized.ObservationWrapper):
"""
Crop the relevant portion of the monitor where an Atari enviroment resides.
"""
def __init__(self, env):
super(CropAtari, self).__init__(env)
self.observation_space = gym_spaces.Box(0, 255, shape=(ATARI_HEIGHT, ATARI_WIDTH, 3))
def _observation(self, observation_n):
return [{'vision': ob['vision'][:ATARI_HEIGHT, :ATARI_WIDTH, :]} for ob in observation_n]
def one_hot(indices, depth):
return np.eye(depth)[indices]
class GymCoreAction(vectorized.ActionWrapper):
def __init__(self, env, gym_core_id=None):
super(GymCoreAction, self).__init__(env)
if gym_core_id is None:
# self.spec is None while inside of the make, so we need
# to pass gym_core_id in explicitly there. This case will
# be hit when instantiating by hand.
gym_core_id = self.spec._kwargs['gym_core_id']
spec = gym.spec(gym_core_id)
raw_action_space = gym_core_action_space(gym_core_id)
self._actions = raw_action_space.actions
self.action_space = gym_spaces.Discrete(len(self._actions))
if spec._entry_point.startswith('gym.envs.atari:'):
self.key_state = translator.AtariKeyState(gym.make(gym_core_id))
else:
self.key_state = None
def _action(self, action_n):
# Each action might be a length-1 np.array. Cast to int to
# avoid warnings.
return [self._actions[int(action)] for action in action_n]
def _reverse_action(self, action_n):
# Only works for core envs currently
self.key_state.apply_vnc_actions(action_n)
return one_hot(self.key_state.to_index(), self.action_space.n)
class GymCoreObservation(vectorized.Wrapper):
def __init__(self, env, gym_core_id=None):
super(GymCoreObservation, self).__init__(env)
if gym_core_id is None:
# self.spec is None while inside of the make, so we need
# to pass gym_core_id in explicitly there. This case will
# be hit when instantiating by hand.
gym_core_id = self.spec._kwargs['gym_core_id']
self._reward_n = None
self._done_n = None
self._info_n = None
self._gym_core_env = gym.spec(gym_core_id).make()
def _reset(self):
observation_n = self.env.reset()
self.reward_n = [0] * self.n
self.done_n = [False] * self.n
self.info = {'n': [{} for _ in range(self.n)]}
new_observation_n, new_reward_n, new_done_n, new_info = self.env.step([[] for i in range(self.n)])
rewarder.merge_n(
observation_n, self.reward_n, self.done_n, self.info,
new_observation_n, new_reward_n, new_done_n, new_info
)
return self._observation(self.done_n, self.info)
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
if self.reward_n is not None:
rewarder.merge_n(
observation_n, reward_n, done_n, info,
[None] * self.n, self.reward_n, self.done_n, self.info,
)
self.reward_n = self.done_n = self.info = None
return self._observation(done_n, info), reward_n, done_n, info
def _observation(self, done_n, info):
missing = set()
observation_n = [None] * self.n
for i, (done, info_i) in enumerate(zip(done_n, info['n'])):
rewarder_observation = info_i.pop('rewarder.observation', None)
if rewarder_observation is not None:
observation, episode_id = rewarder_observation
observation_n[i] = self._gym_core_env.observation_space.from_jsonable(observation)
if done:
# Check whether we should mask
completed = info_i['env_status.completed_episode_id']
# Observation from old!
if episode_id == completed:
logger.debug('[%d] Masking rewarder_observation on episode boundary', i)
observation_n[i] = None
else:
missing.add(i)
if len(missing) > 0:
logger.debug('Missing rewarder observations: %s', missing)
return observation_n
================================================
FILE: universe/wrappers/gym_core_sync.py
================================================
import gym
import logging
from universe import rewarder, spaces, vectorized
logger = logging.getLogger(__name__)
class GymCoreSync(vectorized.Wrapper):
"""A synchronized version of the core envs. Its semantics should match
that of the core envs. (By default, observations are pixels from
the VNC session, but it also supports receiving the normal Gym
observations over the rewarder protocol.)
Provided primarily for testing and debugging.
"""
def __init__(self, env):
super(GymCoreSync, self).__init__(env)
self.reward_n = None
self.done_n = None
self.info = None
# Metadata has already been cloned
self.metadata['semantics.async'] = False
def _reset(self):
observation_n = self.env.reset()
new_observation_n, self.reward_n, self.done_n, self.info = self.env.step([[] for i in range(self.n)])
rewarder.merge_observation_n(observation_n, new_observation_n)
# Fast forward until the observation is caught up with the rewarder
self._flip_past(observation_n, self.reward_n, self.done_n, self.info)
assert all(r == 0 for r in self.reward_n), "Unexpectedly received rewards during reset phase: {}".format(self.reward_n)
return observation_n
def _step(self, action_n):
# Add C keypress in order to "commit" the action, as
# interpreted by the remote.
action_n = [action + [
spaces.KeyEvent.by_name('c', down=True),
spaces.KeyEvent.by_name('c', down=False)
] for action in action_n]
observation_n, reward_n, done_n, info = self.env.step(action_n)
if self.reward_n is not None:
rewarder.merge_n(
observation_n, reward_n, done_n, info,
[None] * self.n, self.reward_n, self.done_n, self.info,
)
self.reward_n = self.done_n = self.info = None
while True:
count = len([True for info_i in info['n'] if info_i['stats.reward.count'] == 0])
if count > 0:
logger.debug('[GymCoreSync] Still waiting on %d envs to receive their post-commit reward', count)
else:
break
new_observation_n, new_reward_n, new_done_n, new_info = self.env.step([[] for i in range(self.n)])
rewarder.merge_n(
observation_n, reward_n, done_n, info,
new_observation_n, new_reward_n, new_done_n, new_info
)
assert all(info_i['stats.reward.count'] == 1 for info_i in info['n']), "Expected all stats.reward.counts to be 1: {}".format(info)
# Fast forward until the observation is caught up with the rewarder
self._flip_past(observation_n, reward_n, done_n, info)
return observation_n, reward_n, done_n, info
def _flip_past(self, observation_n, reward_n, done_n, info):
# Wait until all observations are past the corresponding reset times
remote_target_time = [info_i['reward_buffer.remote_time'] for info_i in info['n']]
while True:
new_observation_n, new_reward_n, new_done_n, new_info = self.env.step([[] for i in range(self.n)])
# info_i.get['diagnostics.image_remote_time'] may not exist, for example when an env
# is resetting. target is a timestamp, thus > 0, so these will count as "need to catch up"
deltas = [target - info_i.get('diagnostics.image_remote_time', 0) for target, info_i in zip(remote_target_time, new_info['n'])]
count = len([d for d in deltas if d > 0])
rewarder.merge_n(
observation_n, reward_n, done_n, info,
new_observation_n, new_reward_n, new_done_n, new_info
)
if count == 0:
return
else:
logger.debug('[GymCoreSync] Still waiting on %d envs to catch up to their targets: %s', count, deltas)
================================================
FILE: universe/wrappers/joint.py
================================================
from multiprocessing import pool
from universe import error, rewarder, vectorized
class Joint(vectorized.Wrapper):
def __init__(self, env_m):
self.env_m = env_m
# TODO: generalize this. Doing so requires adding a vectorized
# space mode.
self.action_space = env_m[0].action_space
self.observation_space = env_m[0].observation_space
self.pool = pool.ThreadPool(min(len(env_m), 5))
self._n = sum(env.n for env in self.env_m)
self.metadata = self.metadata.copy()
self.metadata['render.modes'] = self.env_m[0].metadata['render.modes']
@property
def n(self):
return self._n
def _close(self):
if hasattr(self, 'pool'):
self.pool.close()
def _render(self, mode='human', close=False):
return self.env_m[0]._render(mode=mode, close=close)
def _reset(self):
# Keep all env[0] action on the main thread, in case we ever
# need to render. Otherwise we get segfaults from the
# go-vncdriver.
reset_m_async = self.pool.map_async(lambda env: env.reset(), self.env_m[1:])
reset = self.env_m[0].reset()
reset_m = [reset] + reset_m_async.get()
observation_n = []
for observation_m in reset_m:
observation_n += observation_m
return observation_n
def _step(self, action_n):
observation_n = []
reward_n = []
done_n = []
info_n = []
info = {}
action_m = []
for env in self.env_m:
action_m.append(action_n[len(action_m):len(action_m)+env.n])
# Keep all env[0] action on the main thread, in case we ever
# need to render. Otherwise we get segfaults from the
# go-vncdriver.
step_m_async = self.pool.map_async(lambda arg: arg[0].step(arg[1]), zip(self.env_m[1:], action_m[1:]))
step = self.env_m[0].step(action_m[0])
step_m = [step] + step_m_async.get()
for observation_m, reward_m, done_m, _info in step_m:
observation_n += observation_m
reward_n += reward_m
done_n += done_m
# copy in any info keys
rewarder.merge_infos(info, _info)
info_n += _info['n']
info['n'] = info_n
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/logger.py
================================================
# -*- coding: utf-8 -*-
import logging
import numpy as np
import six
import time
from universe import vectorized
from universe.utils import display
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
def stats(count):
flat = [e for vals in count for e in vals]
if len(flat) == 0:
return '(empty)'
s = '%0.1f±%0.1f' % (np.mean(flat), np.std(flat))
if six.PY2:
# There is not a great way to backport Unicode support to Python 2.
# We don't use it much, anyway. Easier just not to try.
s = s.replace('±', '+-')
return s
class Logger(vectorized.Wrapper):
metadata = {
'configure.required': True
}
def __init__(self, env, print_frequency=5):
super(Logger, self).__init__(env)
self.print_frequency = print_frequency
extra_logger.info('Running VNC environments with Logger set to print_frequency=%s. To change this, pass "print_frequency=k" or "print_frequency=None" to "env.configure".', self.print_frequency)
if self.n is not None:
self._clear_step_state()
self._last_step_time = None
def configure(self, **kwargs):
self.env.configure(**kwargs)
self._clear_step_state()
def _clear_step_state(self):
self.frames = 0
self.last_print = time.time()
# time between action being sent and processed
self.action_lag_n = [[] for _ in range(self.n)]
# time between observation being generated on the server and being passed to add_metadata
self.observation_lag_n = [[] for _ in range(self.n)]
# time between observation being passed to add_metadata and being returned to Logger
self.processing_lag = []
# time between observation being returned by Logger and then action being passed to Throttle
self.thinking_lag = []
self.vnc_updates_n = [[] for _ in range(self.n)]
self.vnc_bytes_n = [[] for _ in range(self.n)]
self.vnc_pixels_n = [[] for _ in range(self.n)]
self.reward_count_n = [[] for _ in range(self.n)]
self.reward_total_n = [[] for _ in range(self.n)]
self.reward_lag_n = [[] for _ in range(self.n)]
self.rewarder_message_lag_n = [[] for _ in range(self.n)]
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
if self.print_frequency is None:
return observation_n, reward_n, done_n, info
last_step_time = self._last_step_time
self._last_step_time = time.time()
# Printing
self.frames += 1
delta = time.time() - self.last_print
if delta > self.print_frequency:
fps = self.frames/delta
# Displayed independently
# action_lag = ','.join([diagnostics.display_timestamps_pair_max(action_lag) for action_lag in self.action_lag_n])
# observation_lag = ','.join([diagnostics.display_timestamps_pair_max(observation_lag) for observation_lag in self.observation_lag_n])
flat = False
# Smooshed together
action_lag, action_data = display.compute_timestamps_pair_max(self.action_lag_n, flat=flat)
observation_lag, observation_data = display.compute_timestamps_pair_max(self.observation_lag_n, flat=flat)
processing_lag, processing_data = display.compute_timestamps_sigma(self.processing_lag)
thinking_lag, thinking_data = display.compute_timestamps_sigma(self.thinking_lag)
reward_count = [sum(r) / delta for r in self.reward_count_n]
if flat and len(reward_count) > 0:
reward_count = np.mean(reward_count)
reward_total = [sum(r) / delta for r in self.reward_total_n]
if flat and len(reward_total) > 0:
reward_total = np.mean(reward_total)
reward_lag, reward_data = display.compute_timestamps_pair_max(self.reward_lag_n, flat=flat)
rewarder_message_lag, rewarder_message_data = display.compute_timestamps_pair_max(self.rewarder_message_lag_n, flat=flat)
vnc_updates_count = [sum(v) / delta for v in self.vnc_updates_n]
if flat and len(vnc_updates_count) > 0:
vnc_updates_count = np.mean(vnc_updates_count)
# Always aggregate these ones
if len(self.vnc_bytes_n) > 0:
vnc_bytes_count = np.sum(e for vnc_bytes in self.vnc_bytes_n for e in vnc_bytes) / delta
else:
vnc_bytes_count = None
if len(self.vnc_pixels_n) > 0:
vnc_pixels_count = np.sum(e for vnc_pixels in self.vnc_pixels_n for e in vnc_pixels) / delta
else:
vnc_pixels_count = None
reward_stats = stats(self.reward_count_n)
vnc_updates_stats = stats(self.vnc_updates_n)
vnc_bytes_stats = stats(self.vnc_bytes_n)
vnc_pixels_stats = stats(self.vnc_pixels_n)
reaction_time = []
for a, o in zip(action_data, observation_data):
try:
value = thinking_data['mean'] + processing_data['mean'] + a['mean'] + o['mean']
except KeyError:
reaction_time.append(None)
else:
reaction_time.append(display.display_timestamp(value))
log = []
for key, spec, value in [
('vnc_updates_ps', '%0.1f', vnc_updates_count),
('n', '%s', self.n),
('reaction_time', '%s', reaction_time),
('observation_lag', '%s', observation_lag),
('action_lag', '%s', action_lag),
('processing_lag', '%s', processing_lag),
('thinking_lag', '%s', thinking_lag),
('reward_ps', '%0.1f', reward_count),
('reward_total', '%0.1f', reward_total),
('vnc_bytes_ps[total]', '%0.1f', vnc_bytes_count),
('vnc_pixels_ps[total]', '%0.1f', vnc_pixels_count),
('reward_lag', '%s', reward_lag),
('rewarder_message_lag', '%s', rewarder_message_lag),
('fps', '%0.2f', fps),
]:
if value == None:
continue
if isinstance(value, list):
value = ','.join(spec % v for v in value)
else:
value = spec % value
log.append('%s=%s' % (key, value))
if not log:
log.append('(empty)')
if self.frames != 0:
logger.info('Stats for the past %.2fs: %s', delta, ' '.join(log))
self._clear_step_state()
# These are properties of the step rather than any one index
observation_available_at = info.get('throttle.observation.available_at')
if observation_available_at is not None:
# (approximate time that we're going to return -- i.e. now, assuming Logger is fast)
# - (time that the observation was passed to add_metadata)
self.processing_lag.append(self._last_step_time - observation_available_at)
action_available_at = info.get('throttle.action.available_at')
if action_available_at is not None and last_step_time is not None:
# (time that the action was generated) - (approximate time that we last returned)
self.thinking_lag.append(action_available_at - last_step_time)
# Saving of lags
for i, info_i in enumerate(info['n']):
observation_lag = info_i.get('stats.gauges.diagnostics.lag.observation')
if observation_lag is not None:
self.observation_lag_n[i].append(observation_lag)
action_lag = info_i.get('stats.gauges.diagnostics.lag.action')
if action_lag is not None:
self.action_lag_n[i].append(action_lag)
reward_count = info_i.get('reward.count')
if reward_count is not None:
self.reward_count_n[i].append(reward_count)
reward_total = reward_n[i]
if reward_total is not None:
self.reward_total_n[i].append(reward_total)
assert 'vnc.updates.n' not in info, 'Looks like you are using an old go-vncdriver. Please update to >=0.4.0: pip install --ignore-installed --no-cache-dir go-vncdriver'
vnc_updates = info_i.get('stats.vnc.updates.n')
if vnc_updates is not None:
self.vnc_updates_n[i].append(vnc_updates)
vnc_bytes = info_i.get('stats.vnc.updates.bytes')
if vnc_bytes is not None:
self.vnc_bytes_n[i].append(vnc_bytes)
vnc_pixels = info_i.get('stats.vnc.updates.pixels')
if vnc_pixels is not None:
self.vnc_pixels_n[i].append(vnc_pixels)
reward_lag = info_i.get('stats.gauges.diagnostics.lag.reward')
if reward_lag is not None:
self.reward_lag_n[i].append(reward_lag)
rewarder_message_lag = info_i.get('stats.gauges.diagnostics.lag.rewarder_message')
if rewarder_message_lag is not None:
self.rewarder_message_lag_n[i].append(rewarder_message_lag)
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/monitoring.py
================================================
import logging
import gym
from universe.vectorized import core # Cannot import vectorized directly without inducing a cycle
from universe.wrappers.time_limit import TimeLimit
logger = logging.getLogger(__name__)
class _UniverseMonitor(core.Wrapper):
def __init__(self, env, directory, video_callable=None, force=False,
resume=False, write_upon_reset=False, uid=None, mode=None):
super(_UniverseMonitor, self).__init__(env)
self.directory = directory
self.video_callable = video_callable
self.force = force
self.resume = resume
self.write_upon_reset = write_upon_reset
self.uid = uid
self.mode = mode
# TODO if we want to monitor more than one instance in a vectorized
# env we'll have to do this after configure()
self._start_monitor()
def _start_monitor(self):
logger.info("Starting Monitor. Writing monitor logs to {}".format(self.directory))
# Circular dependencies :(
from universe import wrappers
# We need to maintain pointers to these to avoid them being
# GC'd. They have a weak reference to us to avoid cycles.
# TODO if we want to monitor more than one instance in a vectorized
# env we'll need to actually fix WeakUnvectorize
self._unvectorized_envs = [wrappers.WeakUnvectorize(self.env, i) for i in range(1)]
# For now we only monitor the first env
if hasattr(gym, 'wrappers'):
self._monitor = gym.wrappers.Monitor(self._unvectorized_envs[0],
directory=self.directory,
video_callable=self.video_callable,
force=self.force,
resume=self.resume,
write_upon_reset=self.write_upon_reset,
uid=self.uid,
mode=self.mode
)
else:
logger.warn("DEPRECATION WARNING: You are using an older version of gym that has a deprecated Monitor, please update to gym:v0.8.0. This change was made 2017/02/01 and is included in universe version 0.21.3")
from gym import monitoring
self._monitor = monitoring.MonitorManager(self._unvectorized_envs[0])
self._monitor.start(
self.directory,
self.video_callable,
self.force,
self.resume,
self.write_upon_reset,
self.uid,
self.mode
)
def _step(self, action_n):
self._monitor._before_step(action_n[0])
observation_n, reward_n, done_n, info = self.env.step(action_n)
done_n[0] = self._monitor._after_step(observation_n[0], reward_n[0], done_n[0], info)
return observation_n, reward_n, done_n, info
def _reset(self):
self._monitor._before_reset()
observation_n = self.env.reset()
self._monitor._after_reset(observation_n[0])
return observation_n
def _close(self):
super(_UniverseMonitor, self)._close()
self._monitor.close()
def set_monitor_mode(self, mode):
logger.info("Setting the monitor mode is deprecated and will be removed soon")
self._monitor._set_mode(mode)
def Monitor(env, directory, video_callable=None, force=False, resume=False,
write_upon_reset=False, uid=None, mode=None):
return _UniverseMonitor(TimeLimit(env), directory, video_callable, force, resume,
write_upon_reset, uid, mode)
================================================
FILE: universe/wrappers/multiprocessing_env.py
================================================
import logging
import numpy as np
from universe import vectorized
from universe.wrappers import render
logger = logging.getLogger(__name__)
def WrappedMultiprocessingEnv(env_id):
return render.Render(EpisodeID(vectorized.MultiprocessingEnv(env_id)))
class RemoveNones(vectorized.Wrapper):
"""The vectorized environment will return None for any indexes that
have already exceeded their episode count (not to be confused with
the Nones returned by resetting environments in the real-time
case). For convenience, we instead return a plausible observation
in each such slot.
"""
def __init__(self, env):
super(RemoveNones, self).__init__(env)
self.plausible_observation = None
def _reset(self):
observation_n = self.env.reset()
self.plausible_observation = observation_n[0]
return observation_n
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
observation_n = [ob if ob is not None else self.plausible_observation for ob in observation_n]
return observation_n, reward_n, done_n, info
class EpisodeID(vectorized.Wrapper):
metadata = {
'configure.required': True
}
"""
For each episode, return its id, and also return the total number of contiguous
episodes that are now done.
"""
def configure(self, episode_limit=None, **kwargs):
self.env.configure(**kwargs)
self.episode_limit = episode_limit
self._clear_state()
def _clear_state(self):
self.done_to = -1
self.extra_done = set()
self.episode_ids = list(range(self.n))
def _set_done_to(self):
while True:
next_done_to = self.done_to + 1
if next_done_to in self.extra_done:
self.done_to = next_done_to
self.extra_done.remove(next_done_to)
else:
break
def _reset(self):
self._clear_state()
return self.env.reset()
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
# Pass along ID of potentially-done episode
for i, info_i in enumerate(info['n']):
info_i['vectorized.episode_id'] = self.episode_ids[i]
done_i = np.argwhere(done_n).reshape(-1)
if len(done_i):
for i in done_i:
self.extra_done.add(self.episode_ids[i])
# Episode completed, so we bump its value
self.episode_ids[i] += self.n
if self.episode_limit is not None and self.episode_ids[i] > self.episode_limit:
logger.debug('Masking: index=%s episode_id=%s', i, self.episode_ids[i])
self.env.mask(i)
self._set_done_to()
# Pass along the number of contiguous episodes that are now done
info['vectorized.done_to'] = self.done_to
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/recording.py
================================================
import logging
import time
import os
import json
import numpy as np
import threading
from six.moves import queue
from universe import rewarder, spaces, vectorized, pyprofile
from universe.utils import random_alphanumeric
logger = logging.getLogger(__name__)
extra_logger = logging.getLogger('universe.extra.'+__name__)
class Recording(vectorized.Wrapper):
"""
Record all action/observation/reward/info to a log file.
It will do nothing, unless given a (recording_dir='/path/to/results') argument.
recording_policy can be one of:
'capped_cubic' will record a subset of episodes (those that are a perfect cube: 0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000, and every multiple of 1000 thereafter).
'always' records all
'never' records none
recording_notes can be used to record hyperparameters in the log file
The format is line-separated json, with large observations stored separately in binary.
The universe-viewer project (http://github.com/openai/universe-viewer) provides a browser-based UI
for examining logs.
"""
def __init__(self, env, recording_dir=None, recording_policy=None, recording_notes=None):
super(Recording, self).__init__(env)
self._log_n = None
self._episode_ids = None
self._step_ids = None
self._episode_id_counter = 0
self._env_semantics_autoreset = env.metadata.get('semantics.autoreset', False)
self._env_semantics_async = env.metadata.get('semantics.async', False)
self._async_write = self._env_semantics_async
self._recording_dir = recording_dir
if self._recording_dir is not None:
if recording_policy == 'never' or recording_policy is False:
self._recording_policy = lambda episode_id: False
elif recording_policy == 'always' or recording_policy is True:
self._recording_policy = lambda episode_id: True
elif recording_policy == 'capped_cubic' or recording_policy is None:
self._recording_policy = lambda episode_id: (int(round(episode_id ** (1. / 3))) ** 3 == episode_id) if episode_id < 1000 else episode_id % 1000 < 2
else:
self._recording_policy = recording_policy
else:
self._recording_policy = lambda episode_id: False
logger.info('Running Recording wrapper with recording_dir=%s policy=%s. To change this, pass recording_dir="..." to env.configure.', self._recording_dir, recording_policy)
self._recording_notes = {
'env_id': env.spec.id,
'env_metadata': env.metadata,
'env_spec_tags': env.spec.tags,
'env_semantics_async': self._env_semantics_async,
'env_semantics_autoreset': self._env_semantics_autoreset,
}
if recording_notes is not None:
self._recording_notes.update(recording_notes)
if self._recording_dir is not None:
os.makedirs(self._recording_dir, exist_ok=True)
self._instance_id = random_alphanumeric(6)
def _get_episode_id(self):
ret = self._episode_id_counter
self._episode_id_counter += 1
return ret
def _get_writer(self, i):
"""
Returns a tuple of (log_fn, log_f, bin_fn, bin_f) to be written to by vectorized env channel i
Or all Nones if recording is inactive on that channel
"""
if self._recording_dir is None:
return None
if self._log_n is None:
self._log_n = [None] * self.n
if self._log_n[i] is None:
self._log_n[i] = RecordingWriter(self._recording_dir, self._instance_id, i, async_write=self._async_write)
return self._log_n[i]
def _reset(self):
if self._episode_ids is None:
self._episode_ids = [None] * self.n
if self._step_ids is None:
self._step_ids = [None] * self.n
for i in range(self.n):
writer = self._get_writer(i)
if writer is not None:
if self._recording_notes is not None:
writer(type='notes', notes=self._recording_notes)
self._recording_notes = None
writer(type='reset', timestamp=time.time())
self._episode_ids[i] = self._get_episode_id()
self._step_ids[i] = 0
return self.env.reset()
def _step(self, action_n):
observation_n, reward_n, done_n, info = self.env.step(action_n)
info_n = info["n"]
for i in range(self.n):
if self._recording_policy(self._episode_ids[i]):
writer = self._get_writer(i)
if writer is not None:
writer(type='step',
timestamp=time.time(),
episode_id=self._episode_ids[i],
step_id=self._step_ids[i],
action=action_n[i],
observation=observation_n[i],
reward=reward_n[i],
done=done_n[i],
info=info_n[i])
# Agents can later call info_n[i]['annotate'](...) to add more things to be visualized
info_n[i]['annotate'] = RecordingAnnotator(writer, self._episode_ids[i], self._step_ids[i])
self._step_ids[i] += 1
if done_n[i] and self._env_semantics_autoreset:
self._episode_ids[i] = self._get_episode_id()
self._step_ids[i] = 0
return observation_n, reward_n, done_n, info
def _close(self):
super(Recording, self)._close()
if self._log_n is not None:
for i in range(self.n):
if self._log_n[i] is not None:
self._log_n[i].close()
self._log_n[i] = None
class RecordingWriter(object):
"""
Safe to use from multiple threads, in case your agent action generator & learning are running in parallel.
"""
def __init__(self, recording_dir, instance_id, channel_id, async_write=True):
self.log_fn = 'universe.recording.{}.{}.{}.jsonl'.format(os.getpid(), instance_id, channel_id)
log_path = os.path.join(recording_dir, self.log_fn)
self.bin_fn = 'universe.recording.{}.{}.{}.bin'.format(os.getpid(), instance_id, channel_id)
bin_path = os.path.join(recording_dir, self.bin_fn)
extra_logger.info('Logging to %s and %s', log_path, self.bin_fn)
self.log_f = open(log_path, 'w')
self.bin_f = open(bin_path, 'wb')
# It would be better to measure memory use and block the writer when the queue is sitting on too much memory
self.q = queue.Queue(1000)
self.t = threading.Thread(target=self.writer_main)
self.t.start()
def close(self):
self.q.put(None)
def close_files(self):
if self.bin_f is not None:
self.bin_f.close()
self.bin_f = None
if self.log_f is not None:
self.log_f.close()
self.log_f = None
def json_encode(self, obj):
if isinstance(obj, np.ndarray):
offset = self.bin_f.tell()
while offset%8 != 0:
self.bin_f.write(b'\x00')
offset += 1
obj.tofile(self.bin_f)
size = self.bin_f.tell() - offset
return {'__type': 'ndarray', 'shape': obj.shape, 'order': 'C', 'dtype': str(obj.dtype), 'npyfile': self.bin_fn, 'npyoff': offset, 'size': size}
elif isinstance(obj, np.float32):
return float(obj)
elif isinstance(obj, np.float64):
return float(obj)
elif isinstance(obj, np.int32):
return int(obj)
elif isinstance(obj, np.int64):
return int(obj)
elif isinstance(obj, RecordingAnnotator):
return 'RecordingAnnotator'
else:
return obj
def writer_main(self):
while True:
item = self.q.get()
if item is None: break
self.write_item(item)
self.q.task_done()
self.close_files()
def __call__(self, **kwargs):
pyprofile.gauge('recording.qsize', self.q.qsize())
self.q.put(kwargs)
def write_item(self, item):
with pyprofile.push('recording.write'):
l = json.dumps(item, skipkeys=True, default=self.json_encode)
self.log_f.write(l + '\n')
self.log_f.flush()
class RecordingAnnotator(object):
def __init__(self, writer, episode_id, step_id):
self.writer = writer
self.episode_id = episode_id
self.step_id = step_id
def __call__(self, **kwargs):
self.writer.__call__(type='annotate', episode_id=self.episode_id, step_id=self.step_id, **kwargs)
================================================
FILE: universe/wrappers/render.py
================================================
import logging
import os
from twisted.python.runtime import platform
from universe import vectorized
logger = logging.getLogger(__name__)
class Render(vectorized.Wrapper):
metadata = {
'configure.required': True
}
def __init__(self, *args, **kwargs):
if platform.isLinux() and not os.environ.get('DISPLAY'):
self.renderable = False
else:
self.renderable = True
self._observation = None
super(Render, self).__init__(*args, **kwargs)
def configure(self, **kwargs):
self.env.configure(**kwargs)
self.metadata = self.metadata.copy()
modes = self.metadata.setdefault('render.modes', [])
if 'rgb_array' not in modes:
modes.append('rgb_array')
def _reset(self):
observation_n = self.env.reset()
self._observation = observation_n[0]
return observation_n
def _step(self, action_n):
observation_n, reward_n, done_n, info_n = self.env.step(action_n)
self._observation = observation_n[0]
return observation_n, reward_n, done_n, info_n
def _render(self, mode='human', *args, **kwargs):
if not self.renderable and mode == 'human':
return
elif self.env is None:
# Only when this breaks
return
elif mode == 'rgb_array':
if self._observation is not None:
observation = self._observation
if isinstance(self._observation, dict):
observation = observation['vision']
return observation
else:
return None
# Could log, but no need.
return self.env.render(mode=mode, *args, **kwargs)
================================================
FILE: universe/wrappers/tests/test_joint.py
================================================
import gym
import universe
from universe import wrappers
def test_joint():
env1 = gym.make('test.DummyVNCEnv-v0')
env2 = gym.make('test.DummyVNCEnv-v0')
env1.configure(_n=3)
env2.configure(_n=3)
for reward_buffer in [env1._reward_buffers[0], env2._reward_buffers[0]]:
reward_buffer.set_env_info('running', 'test.DummyVNCEnv-v0', '1', 60)
reward_buffer.reset('1')
reward_buffer.push('1', 10, False, {})
env = wrappers.Joint([env1, env2])
assert env.n == 6
observation_n = env.reset()
assert observation_n == [None] * 6
observation_n, reward_n, done_n, info = env.step([[] for _ in range(env.n)])
assert reward_n == [10.0, 0.0, 0.0, 10.0, 0.0, 0.0]
assert done_n == [False] * 6
================================================
FILE: universe/wrappers/tests/test_time_limit.py
================================================
import gym
import time
import universe
from gym.envs import register
from universe import wrappers
register(
id='test.SecondsLimitDummyVNCEnv-v0',
entry_point='universe.envs:DummyVNCEnv',
max_episode_seconds=0.1,
tags={
'vnc': True,
}
)
register(
id='test.StepsLimitDummyVNCEnv-v0',
entry_point='universe.envs:DummyVNCEnv',
max_episode_steps=2,
tags={
'vnc': True,
}
)
def test_steps_limit_restart():
env = gym.make('test.StepsLimitDummyVNCEnv-v0')
env.configure(_n=1)
env = wrappers.TimeLimit(env)
env.reset()
assert env._max_episode_seconds == None
assert env._max_episode_steps == 2
# Episode has started
_, _, done, info = env.step([[]])
assert done == [False]
# Limit reached, now we get a done signal and the env resets itself
_, _, done, info = env.step([[]])
assert done == [True]
assert env._elapsed_steps == 0
def test_steps_limit_restart_unused_when_not_wrapped():
env = gym.make('test.StepsLimitDummyVNCEnv-v0')
env.configure(_n=1)
env.reset()
for i in range(10):
_, _, done, info = env.step([[]])
assert done == [False]
def test_seconds_limit_restart():
env = gym.make('test.SecondsLimitDummyVNCEnv-v0')
env.configure(_n=1)
env = wrappers.TimeLimit(env)
env.reset()
assert env._max_episode_seconds == 0.1
assert env._max_episode_steps == None
# Episode has started
_, _, done, info = env.step([[]])
assert done == [False]
# Not enough time has passed
_, _, done, info = env.step([[]])
assert done == [False]
time.sleep(0.2)
# Limit reached, now we get a done signal and the env resets itself
_, _, done, info = env.step([[]])
assert done == [True]
def test_default_time_limit():
# We need an env without a default limit
register(
id='test.NoLimitDummyVNCEnv-v0',
entry_point='universe.envs:DummyVNCEnv',
tags={
'vnc': True,
},
)
env = gym.make('test.NoLimitDummyVNCEnv-v0')
env.configure(_n=1)
env = wrappers.TimeLimit(env)
env.reset()
assert env._max_episode_seconds == wrappers.time_limit.DEFAULT_MAX_EPISODE_SECONDS
assert env._max_episode_steps == None
================================================
FILE: universe/wrappers/throttle.py
================================================
import logging
import time
from universe import pyprofile, rewarder, spaces, vectorized
logger = logging.getLogger(__name__)
class Throttle(vectorized.Wrapper):
"""
A env wrapper that makes sending the action ASAP.
Previous implementation would sleep first and then call env._step.
This implementation calls env._step twice:
1. first call submits given action
2. after sleeping based on fps, second call submits empty action to
receive observation.
visual observation from first call is discarded.
metadata and rewards from the two calls are merged.
text observations are merged as well.
"""
def __init__(self, env):
super(Throttle, self).__init__(env)
self._steps = None
def configure(self, skip_metadata=False, fps='default', **kwargs):
if fps == 'default':
fps = self.metadata['video.frames_per_second']
self.fps = fps
self.skip_metadata = skip_metadata
self.env.configure(**kwargs)
self.diagnostics = self.unwrapped.diagnostics
def _reset(self):
# We avoid aggregating reward/info across episode boundaries
# by caching it on the object
self._deferred_reward_n = None
self._deferred_done_n = None
self._deferred_info_n = None
observation = self.env.reset()
self._start_timer()
return observation
def _step(self, action_n):
if self._steps is None:
self._start_timer()
self._steps += 1
accum_observation_n, accum_reward_n, accum_done_n, accum_info = self._substep(action_n)
accum_info['throttle.action.available_at'] = time.time()
# Record which indexes we were just peeking at, so when we
# make the follow-up we'll be sure to peek there too.
peek_n = [any(spaces.PeekReward for peek in action) for action in action_n]
if self.fps is None:
return accum_observation_n, accum_reward_n, accum_done_n, accum_info
accum_info['stats.throttle.sleep'] = 0
while True:
# See how much time we have to idle
delta = self._start + 1./self.fps * self._steps - time.time()
# The following assumes that our control loop
if delta < 0:
# We're out of time. Just get out of here.
delta = abs(delta)
if delta >= 1:
logger.info('Throttle fell behind by %.2fs; lost %.2f frames', delta, self.fps*delta)
pyprofile.timing('vnc_env.Throttle.lost_sleep', delta)
self._start_timer()
break
# elif delta < 0.008:
# # Only have 8ms. Let's spend it sleeping, and
# # return an image which may have up to an
# # additional 8ms lag.
# #
# # 8ms is reasonably arbitrary; we just want something
# # that's small where it's not actually going to help
# # if we make another step call. Step with 32 parallel
# # envs takes about 6ms (about half of which is
# # diagnostics, which could be totally async!), so 8 is
# # a reasonable choice for now..
# pyprofile.timing('vnc_env.Throttle.sleep', delta)
# accum_info['stats.throttle.sleep'] += delta
# time.sleep(delta)
# break
else:
# We've got plenty of time. Sleep for up to 16ms, and
# then refresh our current frame. We need to
# constantly be calling step so that our lags are
# reported correctly, within 16ms. (The layering is
# such that the vncdriver doesn't know which pixels
# correspond to metadata, and the diagnostics don't
# know when pixels first got painted. So we do our
# best to present frames as they're ready to the
# diagnostics.)
delta = min(delta, 0.016)
pyprofile.timing('vnc_env.Throttle.sleep', delta)
accum_info['stats.throttle.sleep'] += delta
time.sleep(delta)
# We want to merge in the latest reward/done/info so that our
# agent has the most up-to-date info post-sleep, but also want
# to avoid popping any rewards where done=True (since we'd
# have to merge across episode boundaries).
action_n = []
for done, peek in zip(accum_done_n, peek_n):
if done or peek:
# No popping of reward/done
action_n.append([spaces.PeekReward])
else:
action_n.append([])
observation_n, reward_n, done_n, info = self._substep(action_n)
# Merge observation, rewards and metadata.
# Text observation has order in which the messages are sent.
rewarder.merge_n(
accum_observation_n, accum_reward_n, accum_done_n, accum_info,
observation_n, reward_n, done_n, info,
)
return accum_observation_n, accum_reward_n, accum_done_n, accum_info
def _substep(self, action_n):
with pyprofile.push('vnc_env.Throttle.step'):
start = time.time()
# Submit the action ASAP, before the thread goes to sleep.
observation_n, reward_n, done_n, info = self.env.step(action_n)
available_at = info['throttle.observation.available_at'] = time.time()
if available_at - start > 1:
logger.info('env.step took a long time: %.2fs', available_at - start)
if not self.skip_metadata and self.diagnostics is not None:
# Run (slow) diagnostics
self.diagnostics.add_metadata(observation_n, info['n'], available_at=available_at)
return observation_n, reward_n, done_n, info
def _start_timer(self):
self._start = time.time()
self._steps = 0
================================================
FILE: universe/wrappers/time_limit.py
================================================
import logging
import time
from universe import pyprofile
from universe.vectorized import core
logger = logging.getLogger(__name__)
DEFAULT_MAX_EPISODE_SECONDS = 20 * 60. # Default to 20 minutes if there is no explicit limit
class UniverseTimeLimit(core.Wrapper):
def __init__(self, env):
super(UniverseTimeLimit, self).__init__(env)
self._max_episode_seconds = self.env.spec.max_episode_seconds
self._max_episode_steps = self.env.spec.max_episode_steps
if self._max_episode_seconds is None and self._max_episode_steps is None:
self._max_episode_seconds = DEFAULT_MAX_EPISODE_SECONDS
self._elapsed_steps = 0
self._episode_started_at = None
@property
def _elapsed_seconds(self):
return time.time() - self._episode_started_at
def _past_limit(self):
"""Return true if we are past our limit"""
if self._max_episode_steps is not None and self._max_episode_steps <= self._elapsed_steps:
logger.debug("Env has passed the step limit defined by TimeLimit.")
return True
if self._max_episode_seconds is not None and self._max_episode_seconds <= self._elapsed_seconds:
logger.debug("Env has passed the seconds limit defined by TimeLimit.")
return True
return False
def _step(self, action_n):
assert self._episode_started_at is not None, "Cannot call env.step() before calling reset()"
observation_n, reward_n, done_n, info = self.env.step(action_n)
self._elapsed_steps += 1
if self._past_limit():
_ = self.reset() # Force a reset, discard the observation
done_n = [True] * self.n # Force a done = True
return observation_n, reward_n, done_n, info
def _reset(self):
self._episode_started_at = time.time()
self._elapsed_steps = 0
return self.env.reset()
TimeLimit = UniverseTimeLimit
================================================
FILE: universe/wrappers/timer.py
================================================
import logging
import time
from universe import pyprofile, vectorized
logger = logging.getLogger(__name__)
class Timer(vectorized.Wrapper):
"""
Calculate how much time was spent actually doing work. Display result
via pyprofile.
"""
def configure(self, **kwargs):
self.env.configure(**kwargs)
def _reset(self):
with pyprofile.push('vnc_env.Timer.reset'):
return self.env.reset()
def _step(self, action_n):
start = time.time()
with pyprofile.push('vnc_env.Timer.step'):
observation_n, reward_n, done_n, info = self.env.step(action_n)
# Calculate how much time was spent actually doing work
sleep = info.get('stats.throttle.sleep')
if sleep is None or sleep < 0:
sleep = 0
pyprofile.timing('vnc_env.Timer.step.excluding_sleep', time.time() - start - sleep)
return observation_n, reward_n, done_n, info
================================================
FILE: universe/wrappers/vectorize.py
================================================
import gym
import weakref
from universe import error
from universe.vectorized import core
class Vectorize(gym.Wrapper):
"""
Given an unvectorized environment (where, e.g., the output of .step() is an observation
rather than a list of observations), turn it into a vectorized environment with a batch of size
1.
"""
metadata = {'runtime.vectorized': True}
def __init__(self, env):
super(Vectorize, self).__init__(env)
assert not env.metadata.get('runtime.vectorized')
assert self.metadata.get('runtime.vectorized')
self.n = 1
def _reset(self):
observation = self.env.reset()
return [observation]
def _step(self, action):
observation, reward, done, info = self.env.step(action[0])
return [observation], [reward], [done], {'n': [info]}
def _seed(self, seed):
return [self.env.seed(seed[0])]
class Unvectorize(core.Wrapper):
"""
Take a vectorized environment with a batch of size 1 and turn it into an unvectorized environment.
"""
autovectorize = False
metadata = {'runtime.vectorized': False}
def _reset(self):
observation_n = self.env.reset()
assert(len(observation_n) == 1)
return observation_n[0]
def _step(self, action):
action_n = [action]
observation_n, reward_n, done_n, info = self.env.step(action_n)
return observation_n[0], reward_n[0], done_n[0], info['n'][0]
def _seed(self, seed):
return self.env.seed([seed])[0]
class WeakUnvectorize(Unvectorize):
def __init__(self, env, i):
self._env_ref = weakref.ref(env)
super(WeakUnvectorize, self).__init__(env)
# WeakUnvectorize won't get configure called on it
self.i = i
def _check_for_duplicate_wrappers(self):
pass # Disable this check because we need to wrap vectorized envs in multiple unvectorize wrappers
@property
def env(self):
# Called upon instantiation
if not hasattr(self, '_env_ref'):
return
env = self._env_ref()
if env is None:
raise error.Error("env has been garbage collected. To keep using WeakUnvectorize, you must keep around a reference to the env object. (HINT: try assigning the env to a variable in your code.)")
return env
@env.setter
def env(self, value):
# We'll maintain our own weakref, thank you very much.
pass
def _seed(self, seed):
# We handle the seeding ourselves in the vectorized Monitor
return [seed]
def close(self):
# Don't want to close through this wrapper
pass
================================================
FILE: universe/wrappers/vision.py
================================================
import logging
from universe import vectorized
logger = logging.getLogger(__name__)
class Vision(vectorized.Wrapper):
"""
At present, an observation from a vectorized universe environment returns a list of
dicts. Each dict contains input data for each modality. Modalities include 'vision'
and 'text', and it is possible to add other modalities in the future (such as 'audio').
The Vision wrapper extracts the vision modality and discards all others. This is convenient
when we only care about the visual input.
"""
def _reset(self):
observation_n = self.env.reset()
return [ob['vision'] if ob is not None else ob for ob in observation_n]
def _step(self, action_n):
observation_n, reward_n, done_n, info_n = self.env.step(action_n)
observation_n = [ob['vision'] if ob is not None else ob for ob in observation_n]
return observation_n, reward_n, done_n, info_n