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