Giter Site home page Giter Site logo

cat-400's Introduction

Cat 400

"Cat 400" (c4) is a game framework for Nim programming language.

Brief overview

"Cat 400" is a cross-platform framework designed to provide nice experience in game development. Written in nim language, it benefits from the language's elegance and expressiveness, as well as compilation to fast native code.

Core of "Cat 400" is platform-independent and may be run on every target platform supported by nim. However, default systems (which are optional) have external dependencies which may restrict their usage on some platforms.

Key features

  • client-server multithreading architecture (even for single-player games) with network support
  • modularity: all code is split into "systems" (video/user input/networking etc) which work independently
  • systems communicate only by sending messages, thus avoiding tangled code
  • ECS (entity-component-system) with custom user components support
  • simple overwriting of existing systems and ability to create custom systems
  • templates which include some reasonable defaults for specific game genre

So why another game framework?

I've made a research about game engines and frameworks for Nim, and none of them had solid and thoughtful design. It's super easy to display something on screen, and many people think that opportunity for a program to display cubes on screen makes it a game engine. It doesn't. When I see another game framework, I read its documentation (or code, if there are no docs at all) and try to find answers to these questions:

  1. What about physics, i.e. collisions, collision shapes, force, velocity, position etc?
  2. Can I do 3D graphics?
  3. What if I want multiplayer over network?
  4. How do I map user input to specific actions?
  5. How do I occupy all cores effectively?
  6. What about logging? How to debug?
  7. How easy it is to create large project with thousands lines of code? How does the engine help me not get lost?

Usually many of these questions aren't covered at all, but for Cat-400 I do my best to address all of them and beyond.

Is 2D/3D supported?

Yes, 2D via SDL, 3D via SDL+Ogre3d. Also note that C4 is a framework, which means that you can use any backend for any of your systems. So, unlike game engines, you can make a 2D, 3D or even 4D (wat?!) game using any graphics library of your choice.

GUI / Game Engine

Cat 400 is not a game engine (and will never be), and everything is quite low-level - there's no gui and every aspect of game is done within source code. No level editor, too.

Feel free to create high-level tools on top of Cat 400 and share them with community if you have such an opportunity.

But where do I write code?

I know many game engines (for example, Godot) have integrated basic code editors - but they probably haven't heard about IDEs which 1) are designed for writing code, 2) already exist, are mature and just work, and 3) suit better for coding cause have extra features like plugins. So, open your favorite IDE which supports Nim and start coding a game.

But where do I edit graphics?

I know many game engines (for example, Godot) have integrated basic mesh editors - but they probably haven't heard about 3D modeling software which 1) are designed for creating 3D models, 2) already exist, are mature and just work, and 3) suit better for modeling cause have extra features like plugins. So, open your favorite 3D modeling software and start creating models.

Documentation

WARNING! Due to active development documentation is very outdated. It will be fixed once framework's API is stable.

Tutorials

Visit docs/tutorials folder - it's the best place to learn Cat 400.

Reference generation

In order to make repo clean, autogenerated reference is not included. You may generate it yourself: from repo root run

nimble genDocs

Generated reference files will be located in docs/ref folder.

Examples

All examples are available in examples folder.

You may also check templates folder. Templates are just app skeletons which contain initialization and minimal game environment, like a movable player and couple of enemies.

Submodules

Although these modules are part of Cat-400, they may be used separately in any project.

c4.entities module - entity-component system, allowing to create lightweight entities and attach any user-defined components to them, with some basic CRUD operations.

c4.messages module - Message type and any user-defined subtypes, which may be packed and unpacked using msgpack, correctly preserving type information.

c4.threads module - module for spawning named threads and sending messages between them; allows programmer to focus on his multithreaded app, not on settings up connection between threads.

Wrappers

There are several wrappers which are subpackages of "Cat-400" and may be installed and used separately, see lib folder.

Statistics

Stargazers over time

cat-400's People

Contributors

0xe111 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cat-400's Issues

State management - in-place changing and dynamic dispatch

Current c4 implementation heavily uses method dispatching. I want to have state module usage as simple as possible, so I would like to change states by calling

# passing `instance` to `switch` is optional but usually helpful
instance.state.switch(new(ref Loading), instance=instance)

Here switch internally changes instance.state, so you don't need to write

instance.state = instance.state.switch(...)

This is achieved by using var ref State and assigning self = newState.

method switch*(self: var ref State, newState: ref Running, instance: ref Server) =
  if self of ref Loading:  # <-- ugly
    self = newState  # actually swich current state to Running
    echo("Hello world")

However, now we have to check initial state manually and write ugly if self of ref Loading. I wish one could write smth like this and make dynamic dispatch do all the job:

method switch*(self: var ref None, newState: ref Running, instance: ref Server) =
  # this method will be called only when switching from None to Running state
  self = newState  # <-- oops

However, this doesn't work cause self is of type ref None and newState is of type ref Running, so assigning is forbidden. Of course we know that self is a ref State (because it's a ref to subtype), and newState is also a ref State for the same reason. But the assignment is still a problem.

Nim 1.4.0 has changes to onThreadDestruction

Using the spawn template on nim 1.4.0+ fails due to the proc for onThreadDestruction not having {.raises: []}.

In my current project I have fixed this locally with:

template spawn*(name: ThreadName, code: untyped) =
  ## Creates a new thread with name `name` and runs `code` inside this thread.
  ## Usage example:
  ##   spawn("thread1"):
  ##     echo "This block is executed inside thread"

  withLock threadsLock:
    if name in threads:
      raise newException(KeyError, "Thread with name '" & name & "' already exists")

    threads[name] = ThreadInfo()
    threads[name].channel.open()
    threads[name].thread.createThread(
      param = name,
      tp = proc(nm: ThreadName) {.thread.} =
        currentThreadName = nm
        onThreadDestruction(proc() {.raises: []} =
          withLock threadsLock:
            try:
              threadsPtr[][nm].channel.close()
              threadsPtr[].del(nm)
            except KeyError:
              quit("Impossible key error somehow happened!")
        )
        code
    )

Not sure if nm causing a key error is actually impossible, but I assume the exception on top of spawn should catch it before it gets to that point?

After executing sandbox, mouse is gone and no visuals got rendered.

Expected behaviour:

The sandbox should show up and render its content.

Real behaviour:

Sandbox openes but mouse cursor is gone and no video output is rendered.
When the (not visible) mouse pointer is over the sandbox window, console output is echoed.

Nim version:
Nim Compiler Version 0.19.1 [Linux: amd64]

Framework version: current

Loop warnings

Loop should emit warning if user code didn't succeed in timedelta (== sleep() was not called)

Migrate to Haxe

I just did star your repo, just as thanks for Nim sample, but
I hope not to encourage more waste of your time.

Haxe lang is able to do similar compile to C and JavaScript that Nim is able, but
Haxe has already many games ready and built.

Also Open-FL(ASH) library, which allows migrating most .swf games, is written in Haxe as well.

God knows why Haxe has 3 times less stars (6K at time of writting) than Nim (16K).

Nim's current progress is not suited for Game dev projects, but I love that Nim has a self-contained compiler, while Haxe made progress on everything except removing need for OCaml in their compiler.

How to terminate game loop from message processing method?

As I understand, I need to return from run proc in order to finish this thread's loop.

I can patch loop to have a bool variable which controls the while statement. Or I can patch process to return something and use it to return from run. What is the recommended design here? Both my suggestions require modifying the core framework systems.

method process(self: ref VideoSystem, message: ref Message) {.base.} =
  logging.warn &"No rule for processing {message}"
method process(self: ref VideoSystem, message: ref WindowQuitMessage) =
  logging.debug "processing quit event"

proc run*(self: ref VideoSystem) =
  loop(frequency = 30) do:
    while true:
      let message = tryRecv()
      if message.isNil: break
      self.process(message)
    self.update(dt)

Error: undeclared identifier: 'csize_t'

[david@eb ping-pong]$ pwd
/home/david/cat-400/c4/examples/ping-pong
[david@eb ping-pong]$ nim c -r project.nim
Hint: used config file '/home/david/Nim/config/nim.cfg' [Conf]
Hint: used config file '/home/david/Nim/config/config.nims' [Conf]
Hint: used config file '/home/david/cat-400/c4/examples/ping-pong/project.nims' [Conf]
Hint: system [Processing]
Hint: widestrs [Processing]
Hint: io [Processing]
Hint: project [Processing]
Hint: logging [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: math [Processing]
Hint: bitops [Processing]
Hint: macros [Processing]
Hint: algorithm [Processing]
Hint: unicode [Processing]
Hint: times [Processing]
Hint: options [Processing]
Hint: typetraits [Processing]
Hint: posix [Processing]
Hint: os [Processing]
Hint: pathnorm [Processing]
Hint: osseps [Processing]
Hint: net [Processing]
Hint: nativesockets [Processing]
Hint: sets [Processing]
Hint: hashes [Processing]
Hint: monotimes [Processing]
Hint: strformat [Processing]
Hint: processes [Processing]
Hint: osproc [Processing]
Hint: strtabs [Processing]
Hint: streams [Processing]
Hint: cpuinfo [Processing]
Hint: linux [Processing]
Hint: tables [Processing]
Hint: parseopt [Processing]
Hint: threads [Processing]
Hint: unittest [Processing]
Hint: sequtils [Processing]
Hint: terminal [Processing]
Hint: colors [Processing]
Hint: termios [Processing]
Hint: messages [Processing]
Hint: msgpack4nim [Processing]
Hint: endians [Processing]
Hint: enet [Processing]
Hint: enet [Processing]
/home/david/cat-400/c4/lib/enet/enet.nim(32, 20) Error: undeclared identifier: 'csize_t'
[david@eb ping-pong]$ 

Messages thread safety

Messages are sent by new(Message).send() which means that memory allocated in original thread is used in another one, which is not thread-safe.

Impressive Work

You're putting on a common sense clinic here. Love the architecture.

Thank you for open sourcing your project.

Setup outOfMem hook

import system, tables

var outOfMem: ref OutOfMemError
outOfMem.new()
outOfMem.msg = "No memory left."

proc memError() {.gcsafe, locks: 0.} = raise outOfMem
system.outOfMemHook = memError

Compiling 2d template / example

Really cool project, I love the design you are going with. Also I know the tutorials aren't done but nice work with them so far!

I feel like I'm missing something completely obvious when it comes to compiling as the enet wrapper is complaining about csize_t on both linux and windows.

/home/jason/.nimble/pkgs/c4-#head/c4/lib/enet/enet.nim(32, 20) Error: undeclared identifier: 'csize_t'

Are the included examples and 2d template are in a working state?

My list of steps:

  1. nimble install https://github.com/c0ntribut0r/cat-400@#head
  2. git clone https://github.com/c0ntribut0r/cat-400.git
  3. cd cat-400/c4/examples/ping-pong
  4. nim c -r project

nim -v

Nim Compiler Version 1.0.6 [Linux: amd64]
Compiled at 2020-01-23
Copyright (c) 2006-2019 by Andreas Rumpf

git hash: 89b39ee8fe271d0e1b75c15a4c6cf82eb9c13aea
active boot switches: -d:release

sandbox's collectAssets does not copy gls4 shaders.

Expected behaviour:

What should happen?
collectAssets should copy all required assets into build folder.
Also gls4 shaders.

Real behaviour:

What happens?
collectAssets does not copy gls4 shaders.

Nim version: Nim Compiler Version 0.19.1 [Linux: amd64]

Framework version: current

Not stable dependencines in default Nimble installation

Today, trying to redownload most of libraries for my brand new Nim 2.0 installation, decided to give Cat400 a redownload as well.
However, I received such message:

Downloading https://github.com/c0ntribut0r/cat-400 using git
   Warning: The package has no tagged releases, downloading HEAD instead.
  Verifying dependencies for [email protected]
       Tip: 3 messages have been suppressed, use --verbose to show them.
nimble.nim(107)          processFreeDependencies

    Error:  Unsatisfied dependency: nim (>= 2.1)

I assume it may be automatic Nimble generation of Nim version being worked on (indev Nim I think?), but this makes it kinda bad on user side: setting requirement on >= 2.0 Nim is already a bit rough for users who use old Nim considering 2.0 was released roughly a month ago - but putting requirement on indev version makes it extremely awkward, considering most users will rather use stable version.

I'd suggest changing the cap to 2.0 (or lower, if it's possible) to address my concerns. I really hope for it at least, because I was really eager to give Cat400 second try on new installation and would love to do it fairly soon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.