Snakia is a modern Python framework for creating applications with Entity-Component-System (ECS) architecture, event system, and reactive programming. Built with performance (maybe) and modularity in mind, Snakia provides a clean API for developing complex applications ranging from games to terminal user interfaces.
- ποΈ ECS Architecture - Flexible entity-component-system for scalable game/app logic
- π‘ Event System - Asynchronous event handling with filters and priorities
- π Plugin System - Modular plugin architecture for extensibility
- π¨ TUI Framework - Rich terminal user interface with reactive widgets
- β‘ Reactive Programming - Observable data streams and reactive bindings
- π οΈ Rich Utilities - Decorators, properties, platform abstraction, and more
- π― Type Safety - Full type hints and Pydantic integration
β οΈ Experimental Framework
This framework is currently in beta/experimental stage. Not all features are fully implemented, there might be bugs, and the API is subject to change. Use at your own risk! π§
- Python >= 3.12
- pip or uv (recommended) package manager
pip install snakiagit clone https://github.com/RuJect/Snakia.git
cd Snakia
pip install -e .Here's what we're working on to make Snakia even better:
- Plugin Isolation: restrict plugin access to only events and components statically defined in manifest
- Async & Multithreading: implement proper async/await support and multithreading capabilities
- Platform Support: expand platform abstraction to support more operating systems
- Random Implementations: add various random generations implementations
- TUI Widgets: create more ready-to-use TUI widgets and components
- Code Documentation: add comprehensive docstrings and inline comments
- Documentation: create detailed API documentation and tutorials
from snakia.core.engine import Engine
from snakia.core.loader import Meta, Plugin, PluginProcessor
from snakia.core.ecs import Component
from snakia.types import Version
# Creating a component
class HealthComponent(Component):
value: int = 100
max_value: int = 100
# Creating a processor
class HealthProcessor(PluginProcessor):
def process(self, system):
for entity, (health,) in system.get_components(HealthComponent):
if health.value <= 0:
print(f"Entity {entity} died!")
# Creating a plugin
class HealthPlugin(Plugin, meta=Meta(
name="health",
version=Version.from_args(1, 0, 0),
processors=(HealthProcessor,)
)):
def on_load(self): pass
def on_unload(self): pass
# Starting the engine
engine = Engine()
engine.loader.register(HealthPlugin)
engine.loader.load_all()
engine.start()Snakia is built on a modular architecture with clear separation of concerns:
Snakia/
βββ core/ # Framework core
β βββ engine.py # Main engine
β βββ ecs/ # Entity-Component-System
β βββ es/ # Event System
β βββ loader/ # Plugin loading system
β βββ rx/ # Reactive programming
β βββ tui/ # Terminal User Interface
βββ decorators/ # Decorators
βββ property/ # Property system
βββ platform/ # Platform abstraction
βββ utils/ # Utilities
βββ random/ # Random number generation
βββ field/ # Typed fields
βββ types/ # Special types
from snakia.core.engine import Engine
from snakia.core.ecs import Component
from snakia.core.es import Event
from snakia.core.loader import Meta, Plugin, PluginProcessor
from snakia.types import Version
from pydantic import Field
class HealthComponent(Component):
max_value: int = Field(default=100, ge=0)
value: int = Field(default=100, ge=0)
class DamageComponent(Component):
damage: int = Field(ge=0)
ticks: int = Field(default=1, ge=0)
class DeathEvent(Event):
entity: int = Field()
class HealthProcessor(PluginProcessor):
def process(self, system):
# Processing damage
for entity, (damage, health) in system.get_components(
DamageComponent, HealthComponent
):
health.value -= damage.damage
damage.ticks -= 1
if damage.ticks <= 0:
system.remove_component(entity, DamageComponent)
if health.value <= 0:
system.remove_component(entity, HealthComponent)
self.plugin.dispatcher.publish(DeathEvent(entity=entity))
class HealthPlugin(Plugin, meta=Meta(
name="health",
version=Version.from_args(1, 0, 0),
processors=(HealthProcessor,)
)):
def on_load(self): pass
def on_unload(self): pass
# Usage
engine = Engine()
engine.loader.register(HealthPlugin)
engine.loader.load_all()
# Creating a player
player = engine.system.create_entity(
HealthComponent(value=100, max_value=100)
)
# Dealing damage
engine.system.add_component(player, DamageComponent(damage=25, ticks=1))
engine.start()from snakia.core.tui import CanvasChar, RenderContext
from snakia.core.tui.render import ANSIRenderer
from snakia.core.tui.widgets import TextWidget, BoxWidget, VerticalSplitWidget
import sys
class StdoutTarget:
def write(self, text: str): sys.stdout.write(text)
def flush(self): sys.stdout.flush()
def main():
# Creating widgets
title = TextWidget("Snakia TUI", CanvasChar(fg_color="cyan", bold=True))
content = TextWidget("Welcome to Snakia!", CanvasChar(fg_color="white"))
box = BoxWidget(20, 5, CanvasChar("β", fg_color="green"))
# Layout
layout = VerticalSplitWidget([title, content, box], "-")
# Rendering
renderer = ANSIRenderer(StdoutTarget())
with RenderContext(renderer) as ctx:
ctx.render(layout.render())
if __name__ == "__main__":
main()We welcome contributions to Snakia development! Whether you're fixing bugs, adding features, or improving documentation, your help is appreciated.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests if applicable
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Add type hints to all new code
- Write clear commit messages
- Update documentation for new features
- Test your changes thoroughly