An extension for Arma 3 that lets you write extensions in Python 3

Overview

Pythia logo

Pythia

An Arma 3 extension that lets you to write python extensions for Arma 3. And it's really simple and straightforward to use!

Pythia's Discord server.

TL;DR:

Having the following function defined:

def my_function(some_string, number, the_truth):
    return ["awesome", 42, True, (1, 3.5)]

And calling this SQF code:

["MyAwesomeModule.my_function", ["arg1", 3.14256, False]] call py3_fnc_callExtension

Will return this to your SQF code:

["awesome", 42, True, [1, 3.5]]

Features:

  • Full type conversion both ways: pass in an SQF array of ints, get a python array of ints. Return a tuple of a float, a bool and a string, get an SQF array containing the float, bool and the string
  • Embedded python installation (you don't have to install anything; just run the mod)
  • Python code stored inside @YourOwnMod directory
  • Python modules can call and reuse each other, even between separate Arma mods
  • Background Python threads
  • Cython and other compiled python extensions "just work" (C-like speed for performance-critical modules)
  • Extendable python environment through pip
  • Proven to work with libraries such as numpy, scipy, matplotlib, PyQt5, etc...
  • Automatic python code reloader for easier development
  • Calling SQF back from Python (experimental, using asyncio syntax)
  • Allows returning more than 10240 characters from the extension transparently
  • Annoys sloppy SQF developers with correct code indentation since Day One ;)

Potential features

These features could be implemented quite easily but for some reason have never been done. Want them in or want to help developing them? Contact the developers!

  • Mods contained inside single .pbo files
  • Calling functions in the background and polling for them

Example mods:

The following are mods that use Pythia to accomplish their goal.

Frontline

Dynamic Frontline in action

Frontline is like Squad but done in Arma. Like Project Reality: Arma 3 but better. With a Dynamic Frontline feature that moves as you conquer terrain (and a bunch of other features).

The frontline computation is done in Python, with the use of numpy, scipy, matplotlib and custom Cython code.

ObjectPlacementTBH

ObjectPlacementTBH

It's just a tool for object placement, to be honest... ;)

Pythia is used for loading in xml files, file IO, writing images using PIL, loading layers.cfg (using Armaclass). The newest version is using PyQt5 to display Qt widgets over the Arma window.

Status

Current status: Finishing touches before 1.0. You can use it right now - it's stable. Yes, really.

If you are serious about using Pythia, see the issues page and especially this one. You can contact me to ask for planned changes, on Pythia's Discord server. I don't bite :).

Example usage

Your directory structure:

@MyAwesomeMod/
├── Addons/  # (...)
└── python_code/  # Can be named however you want; you can have more than one
    ├── $PYTHIA$  # Contains the name of your python package, for example: MyAwesomeModule
    ├── __init__.py
    ├── module.py
    ├── cython_module.cp37-win_amd64.pyd  # Compiled Cython code, because we can!
    └── cython_module.cp37-win32.pyd      # Same code but for 32-bit Arma

__init__.py:

def my_function(my, arguments):
    return ["awesome", 42, True, (1, 2)]

module.py:

from .cython_module import stuff  # You can import code from other modules, obviously

def call_stuff():
    return stuff()

Now run Arma 3 with [email protected];@MyAwesomeMod and execute the following:


Console:

["MyAwesomeModule.my_function", [3.14256, False]] call py3_fnc_callExtension

Result:

["awesome", 42, True, [1, 2]]

Console:

["MyAwesomeModule.module.call_stuff"] call py3_fnc_callExtension

Result:

["Hello world from a Cython module!"]

Note: MyAwesomeModule is the string that was in the $PYTHIA$ file. You can use any string you want here, obviously.

Performance:

The code is written with performance in mind, meaning that function lookups are cached to limit the number of getattrs, for example. However, keep in mind that the accessibility requirements (SQF <=> Python type conversion) and the general overhead caused by BIs design choice of allowing passing only strings to callExtension must take its toll. I'm open to implementing an alternate set of restricted commands that swap convenience for speed, if required, though...

As such, it is suggested to limit the number of python calls in each frame. It is still faster to call one function with two sets of arguments than two functions, one right after the other.

Concrete numbers:

The test below was executed by calling pythia.ping with different types of arguments, meaning that each list of arguments had to be converted to python and the return value had to be converted back to SQF. Each test was conducted 3 times and the lowest value was written down.

The exact arguments for each test can be found on the scratchpad.

# Type of arguments 10 arguments 100 arguments
1 Integers 0.0198 ms 0.0858 ms
2 Floats 0.0225 ms 0.1091 ms
3 Booleans 0.0155 ms 0.0580 ms
4 Strings 0.0161 ms 0.0580 ms
5 Arrays with ints 0.0318 ms 0.2086 ms
6 Empty arrays 0.0153 ms 0.0555 ms

Note that you will probably usually pass a number of arguments lower than 10 (and if you don't, your function will most probably be slower than the (de)serializing overhead) so you can assume that each Pythia call takes around 0.02 ms on a recent computer.

This allows for under 150 Pythia calls per frame if you want to stay under the 3ms arbitrary limit (arbitrary because the callExtension calls are not really limited by the engine, they just block execution until finished).

Note: You may get away with calls that take even 100 ms server-side, if they happen rarely enough.

Note: If your code is REALLY big and slow consider using a background Python thread. See below.

Your own mod development

Code reloader

Note: The reloader currently only works for native python code! If your code uses Cython or custom C extensions (dll/pyd files) you will get errors when reloading.

To turn the reloader on, simply call:

["pythia.enable_reloader", [True]] call py3_fnc_callExtension

The reloader will then watch all the imported Pythia modules for changes and if any source file is updated, every loaded module will be reloaded on the next Pythia call. Builtin python modules and modules installed with pip are exempt from reloading.

Important: objects in the global namespace will stay as they are. If such an object points to data inside a module, that data will point to the old (not reloaded) module instance, even after the reload! (the module will not be garbage-collected as long as there is something pointing to it).

This can lead to situations where you have two instances of the same module loaded in-memory. The recommended way is to create pre_reload/post_reload hooks for your module and reinitialize your new module and global variables with the data from the old module, on reload. See below.

Keeping your module data between reloads

To prevent losing data during reload your module needs to have __pre_reload__ and __post_reload__ hooks declared. __post_reload__(state) will be called with the exact same arguments as have been returned by __pre_reload__() before reloading the module.

The reloader works as follows: (simplified pseudo-python code)

def reload_all_modules():
    for module in pythia_modules:
        modules_state[module.__name__] = module.__pre_reload__()

    reload_modules(pythia_modules)

    for module in pythia_modules:
        module.__post_reload__(modules_state[module.__name__])

Creating those functions is purely optional.

Note that using the reloader causes a time penalty for each call and shouldn't be used in production.

Threads

TODO

Calling SQF back from Python

TODO

Note that you need to be comfortable with using asyncio-type code to use this feature!

Installing

  • Build the mod yourself or get a prebuilt version.
  • Copy @Pythia to Arma 3 directory.
  • (Optional for development) Create a python directory in Arma 3 directory. Put all your python functions there.
  • If any of your pythia mods contains a requirements.txt file, simply drag it onto @Pythia\install_requirements.bat to install all the requirements to both python installations before running the game.

Pythia development

Building requirements

  • Python 3.7 64-bit and 32-bit (you need to build both the 32-bit and 64-bit extensions)
  • Visual Studio Community 2017

Building

  • File -> Open -> CMake: CmakeLists.txt, make sure that Release configuration is selected (instead of Debug) and CMake -> Build All. Remember to build for both x86 and x86_64.
  • Run python tools\make_pbos.py to build the required PBO files.
  • Run python tools\create_embedded_python.py @Pythia to download and prepare x86 and x86_64 python installations.

Contributing

All contributions are welcome! Is something in the documentation unclear? Feel free to submit a PR or drop a note on Pythia's Discord server.

Comments
  • Build the python embedded executable from sources

    Build the python embedded executable from sources

    https://stackoverflow.com/a/52930398

    In case I need to rebuild Python 3.7 myself, in light of the latest CVE (the official download page doesn't have binary builds for the last Python 3.7 release, only sources, as they want people to move to 3.8)

    opened by overfl0 5
  • Lower level interfacing

    Lower level interfacing

    Hello, If I am missing the point completely here I apologize, but is it possible to, for example create a group or unit in python directly? or just return primitive datatypes? Is this possible currently, will it be possible, can I help make it possible? and is it possible to hook into events such as pre init, post init and event handlers such as hit and killed? Cheers. Lonja

    opened by Tirpitz93 5
  • Port to Python 3.9

    Port to Python 3.9

    • [x] Update create_embedded_python.py to be able to be parametrized from github actions
    • [x] Python 3.8 - https://docs.python.org/3/whatsnew/3.8.html
    • [x] Python 3.9 - https://docs.python.org/3/whatsnew/3.9.html
    • [x] Python 3.10 (optionally) - https://docs.python.org/3/whatsnew/3.10.html
    opened by overfl0 4
  • Python thread may not be fully working

    Python thread may not be fully working

    Context: http://www.indelible.org/ink/python-reloading/

    Preparation: pip install reloader Write the file locally: https://github.com/jparise/python-reloader/blob/master/monitor.py

    Test:

    import reloader
    from .monitor import Reloader
    reloader.enable(blacklist=['itertools', 'logging', 'math', 'matplotlib', 'numpy', 'scipy', 'sys', 'skimage'])
    r = Reloader()
    
    ...
    
    r.poll() # In functions calls
    

    A strange thing is happening: after adding logger.critical() messages inside ModuleMonitor::_scan it looks like the Monitor thread is only running when a foreground function is called. As soon as that function is finished, the background thread looks to be frozen, without any activity.

    opened by overfl0 4
  • Ensure additional paths are added when using `pywin32`

    Ensure additional paths are added when using `pywin32`

    This is probably caused by the site module being enabled, but we need to make sure we can enable site while not leaking the environment through user site directories, etc...

    opened by overfl0 3
  • Getting Error while iterating iterator. Report a bug!!!

    Getting Error while iterating iterator. Report a bug!!!

    When running the following code:

    async def connectToTwitch(channelName, oAuthKey, nickName):
        try:
            nickname = nickName
            token = oAuthKey
            channel = channelName
            client = Client()
            await client.login_oauth(token, nickname)
        except Exception as e:
            return str(e)
    

    I get: image This piece of code is run in game using the following:

    _channelName = twitchBot_chat_channelName; 
    _oAuthKey = twitchBot_chat_oAuth; 
    _nickName = twitchBot_chat_nickName;
    ["CCB.connectToTwitch", [_channelName, _oAuthKey, _nickName]] call py3_fnc_callExtension;
    

    The client object comes from pytmi

    opened by JessevGool 3
  • Move to python 3.7

    Move to python 3.7

    • [x] Change the installing script used by AppVeyor
    • [x] Replace instances of python35 in the source code
    • [x] Replace instances of python35 in the project include dirs and linker dirs
    • [x] Check the ._pth file is read correctly
    • [x] Ensure the user_site is not added to the path
    • [x] Have a separate directory per python version
    • [x] Install pip correctly
    • [x] Check if it works fine with all the other currently used mods
    • [x] Check the default paths passed in the registry

    https://docs.python.org/3.6/using/windows.html#finding-modules https://pymotw.com/2/site/

    low priority 
    opened by overfl0 3
  • Create a self-contained python distribution with Pythia

    Create a self-contained python distribution with Pythia

    This is almost done, but does not seem to be working for some people.

    Some investigation is required. Probably logging will need to be fixed for this to work, as well. #23

    opened by overfl0 3
  • Not joining python threads prevents Arma from terminating the process

    Not joining python threads prevents Arma from terminating the process

    See #11 for a reproduction.

    When a Python thread is created but not terminated (joined). It will continue running after Arma is shut down, keeping the arma3.exe process running.

    It is unclear what is causing this, but at first sight, it should have received a DLL_PROCESS_DETACH. So either it doesn't receive it, or a running python ignores the Py_Finalize(); call we're running in the destructor.

    opened by overfl0 3
  • Use parseSimpleArray - benchmark and consider implementing

    Use parseSimpleArray - benchmark and consider implementing

    Use parseSimpleArray instead of call compile.

    Check if we are generating anything more than what is reported to be parsable by parseSimpleArray.

    We've gotten rid of SQF from Python so, in theory, this should not matter anyway, now.

    • [x] Check python outputs
    • [x] Check what happens when the extension throws error messages
    • [ ] Extend the unit tests for overflows, etc...
    • [x] Replace the call and test
    • [x] Benchmark
    opened by overfl0 2
  • Bump numpy from 1.21.0 to 1.22.0 in /tests/@CythonNumpyMod

    Bump numpy from 1.21.0 to 1.22.0 in /tests/@CythonNumpyMod

    Bumps numpy from 1.21.0 to 1.22.0.

    Release notes

    Sourced from numpy's releases.

    v1.22.0

    NumPy 1.22.0 Release Notes

    NumPy 1.22.0 is a big release featuring the work of 153 contributors spread over 609 pull requests. There have been many improvements, highlights are:

    • Annotations of the main namespace are essentially complete. Upstream is a moving target, so there will likely be further improvements, but the major work is done. This is probably the most user visible enhancement in this release.
    • A preliminary version of the proposed Array-API is provided. This is a step in creating a standard collection of functions that can be used across application such as CuPy and JAX.
    • NumPy now has a DLPack backend. DLPack provides a common interchange format for array (tensor) data.
    • New methods for quantile, percentile, and related functions. The new methods provide a complete set of the methods commonly found in the literature.
    • A new configurable allocator for use by downstream projects.

    These are in addition to the ongoing work to provide SIMD support for commonly used functions, improvements to F2PY, and better documentation.

    The Python versions supported in this release are 3.8-3.10, Python 3.7 has been dropped. Note that 32 bit wheels are only provided for Python 3.8 and 3.9 on Windows, all other wheels are 64 bits on account of Ubuntu, Fedora, and other Linux distributions dropping 32 bit support. All 64 bit wheels are also linked with 64 bit integer OpenBLAS, which should fix the occasional problems encountered by folks using truly huge arrays.

    Expired deprecations

    Deprecated numeric style dtype strings have been removed

    Using the strings "Bytes0", "Datetime64", "Str0", "Uint32", and "Uint64" as a dtype will now raise a TypeError.

    (gh-19539)

    Expired deprecations for loads, ndfromtxt, and mafromtxt in npyio

    numpy.loads was deprecated in v1.15, with the recommendation that users use pickle.loads instead. ndfromtxt and mafromtxt were both deprecated in v1.17 - users should use numpy.genfromtxt instead with the appropriate value for the usemask parameter.

    (gh-19615)

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
  • CVE-2007-4559 Patch

    CVE-2007-4559 Patch

    Patching CVE-2007-4559

    Hi, we are security researchers from the Advanced Research Center at Trellix. We have began a campaign to patch a widespread bug named CVE-2007-4559. CVE-2007-4559 is a 15 year old bug in the Python tarfile package. By using extract() or extractall() on a tarfile object without sanitizing input, a maliciously crafted .tar file could perform a directory path traversal attack. We found at least one unsantized extractall() in your codebase and are providing a patch for you via pull request. The patch essentially checks to see if all tarfile members will be extracted safely and throws an exception otherwise. We encourage you to use this patch or your own solution to secure against CVE-2007-4559. Further technical information about the vulnerability can be found in this blog.

    If you have further questions you may contact us through this projects lead researcher Kasimir Schulz.

    opened by TrellixVulnTeam 0
  • Add Python 3.11 support

    Add Python 3.11 support

    We're NOT planning to switch to Python 3.11 anytime soon (because there are no wheels released for 3.11 yet) but it would be good to be ready to do so in the future.

    https://docs.python.org/3.11/whatsnew/3.11.html

    opened by overfl0 0
  • Alternative callExtension syntax - benchmark and consider implementing

    Alternative callExtension syntax - benchmark and consider implementing

    • [ ] Replace SQF side with error handling
    • [ ] C++ side handling with argument list
    • [ ] Check what happens on timeouts
    • [ ] Check if everything works
    • [ ] Benchmark
    opened by overfl0 0
  • Ability to include other files from adapter.py

    Ability to include other files from adapter.py

    This enhancement may be blocked by #7.

    Allow the storage of multiple .py files that could then be included by adapter.py so that all the code is not in one big file.

    This won't impact performance as once a module is loaded to memory, import something has no effect so it will be loaded only once.

    low priority 
    opened by overfl0 3
Releases(1.0.0)
Owner
Lukasz Taczuk
C/C++/Python developer with a penchant for IT Security and Reverse-Engineering.
Lukasz Taczuk
A python library for writing parser-based interactive fiction.

About IntFicPy A python library for writing parser-based interactive fiction. Currently in early development. IntFicPy Docs Parser-based interactive f

Rita Lester 31 Nov 23, 2022
PyGo custom language, New but similar language programming

New but similar language programming. Now we are capable to program in a very similar language to Python but at the same time get the efficiency of Go.

Fernando Perez 4 Nov 19, 2022
Powerful Assistant

Delta-Assistant Hi I'm Phoenix This project is a smart assistant This is the 1.0 version of this project I am currently working on the third version o

1 Nov 17, 2021
The purpose of this script is to bypass disablefund, provide some useful information, and dig the hook function of PHP extension.

The purpose of this script is to bypass disablefund, provide some useful information, and dig the hook function of PHP extension.

Firebasky 14 Aug 02, 2021
🏆 A ranked list of awesome Python open-source libraries and tools. Updated weekly.

Best-of Python 🏆 A ranked list of awesome Python open-source libraries & tools. Updated weekly. This curated list contains 230 awesome open-source pr

Machine Learning Tooling 2.7k Jan 03, 2023
A corona information module

A corona information module

Fayas Noushad 3 Nov 28, 2021
A simple desktop application to scan and export Genshin Impact Artifacts.

「天目」 -- Amenoma 简体中文 | English 「天目流的诀窍就是滴水穿石的耐心和全力以赴的意志」 扫描背包中的圣遗物,并导出至 json 格式。之后可导入圣遗物分析工具( 莫娜占卜铺 、 MingyuLab 、 Genshin Optimizer 进行计算与规划等。 已支持 原神2.

夏至 475 Dec 30, 2022
Learning objective: Use React.js, Axios, and CSS to build a responsive YouTube clone app

Learning objective: Use React.js, Axios, and CSS to build a responsive YouTube clone app to search for YouTube videos, channels, playlists, and live events via wrapper around Google YouTube API.

Dillon 0 May 03, 2022
Install Firefox from Mozilla.org easily, complete with .desktop file creation.

firefox-installer Install Firefox from Mozilla.org easily, complete with .desktop file creation. Dependencies Python 3 Python LXML Debian/Ubuntu: sudo

rany 7 Nov 04, 2022
Create standalone, installable R Shiny apps using Electron

Create standalone, installable R Shiny apps using Electron

Chase Clark 5 Dec 24, 2021
A subleq VM/interpreter created by me for no reason

What is Dumbleq? Dumbleq is a dumb Subleq VM/interpreter implementation created by me for absolutely no reason at all. What is Subleq? If you haven't

Phu Minh 2 Nov 13, 2022
JSEngine is a simple wrapper of Javascript engines.

JSEngine This is a simple wrapper of Javascript engines, it wraps the Javascript interpreter for Python use. There are two ways to call interpreters,

11 Dec 18, 2022
Modelling and Implementation of Cable Driven Parallel Manipulator System with Tension Control

Cable Driven Parallel Robots (CDPR) is also known as Cable-Suspended Robots are the emerging and flexible end effector manipulation system. Cable-driven parallel robots (CDPRs) are categorized as a t

Siddharth U 0 Jul 19, 2022
A pet facts python api

Pet-Facts-API A pet facts python api Project Links API :- https://pet-facts-api.vercel.app Docs :- https://fayasnoushad.github.io/Pet-Facts-API

Fayas Noushad 3 Dec 18, 2021
Cloud Native sample microservices showcasing Full Stack Observability using AppDynamics and ThousandEyes

Cloud Native Sample Bookinfo App Observability Bookinfo is a sample application composed of four Microservices written in different languages.

Cisco DevNet 13 Jul 21, 2022
Contains the code of my learning of Python OOP.

OOP Python This repository contains the code of my learning of Python OOP. All the code: is following PEP 8 ✅ has proper concept illustrations and com

Samyak Jain 2 Jan 15, 2022
NeurIPS'19: Meta-Weight-Net: Learning an Explicit Mapping For Sample Weighting (Pytorch implementation for noisy labels).

Meta-Weight-Net NeurIPS'19: Meta-Weight-Net: Learning an Explicit Mapping For Sample Weighting (Official Pytorch implementation for noisy labels). The

243 Jan 03, 2023
Inspect the resources of your android projects and understand which ones are not being used and could potentially be removed.

Android Resources Checker What This program will inspect the resources of your app and help you understand which ones are not being used and could pot

Fábio Carballo 39 Feb 08, 2022
Python library for generating CycloneDX SBOMs

Python Library for generating CycloneDX This CycloneDX module for Python can generate valid CycloneDX bill-of-material document containing an aggregat

CycloneDX SBOM Standard 31 Dec 16, 2022
MindF**k it's a programming language as BrainFuck, but with some cool features.

MindF**k Description MindF**k it's a programming language as BrainFuck, but with some cool features. Symbol What does symbol mean Next slot Previo

tixcode 0 Jun 15, 2022