clojure-gamedev

Jakub Zika 2021-02-22T08:12:08.006300Z

What are you using on FE? Re-frame etc. or do you paint in canvas?

Fabim 2021-02-22T11:12:39.006600Z

@jakub.zika-extern I normally use re-frame for my projects. For the game instead I use html divs and css animations. I would switch if I find a good what to handle the animations

2💯
Jakub Zika 2021-02-22T11:16:17.006800Z

@merklefabian I am playing with Quil currently (wrapper over processing(js)), it is nice. You can create simple 2d/3d games with it. http://quil.info/api You can also provide atom to quil so it can be easily connected to RUM (and reagent and reframe - but i did not try it yet), so you can control your canvas by custom html elements… + I would recommend tips taken from: https://github.com/alexkehayias/chocolatier Tips Here are some tips for optimizing performance of game loops: • Use the Chrome dev tools to do a CPU profile and trace where time is being spent • Don’t use `partial` or `apply` as they are slow • Always specify each arrity of a function instead of using `(fn [& args] ...)` • Don’t use `multimethod`, use `cond` or `condp` and manually dispatch • Use `array` when you need to quickly append items to a collection (mutable) • Use `loop` instead of `for` or `into` with transients or arrays as the accumulator • Avoid boxing and unboxing i.e multiple maps/fors over a collection, use transducers, reducers or loops • Don’t make multiple calls to get the same data, put it in a `let` • Avoid heavily nested closures as the lookup tree becomes very long and slow • Favor eager operations over lazy operations i.e `reduce` instead of `for` • Don’t use `concat` or `mapcat` as they can be slow and generate lots of garbage • Don’t use `last` as it will need to traverse the whole sequence, use `nth` instead if you know how many elements are in the collection • Don’t use hashmaps as functions `({:a 1} :a)`, instead use `get` or keywords as functions • Always return the same type from a function (V8 can then optimize it)

Fabim 2021-02-22T11:27:24.007100Z

@jakub.zika-extern Thanks for the extensive tips. I saw chocolatier before but had the impression that it was discontinued. Quil looks interesting. Why do you say Don’t use multimethod` `

Fabim 2021-02-22T13:11:19.007400Z

@oconn Thanks for your answer. Would you share the code how you do 1. with me, please?

oconn 2021-02-22T13:34:26.007600Z

@merklefabian No problem. That workflow is going to be dependent on what you’re using for your websocket server & authentication / user pool. It may be more useful if you share architecture / code for your specific setup. With that said, I’m doing this; 1. Each time a player opens the browser, I check to see if they are authenticated. (Do they have a valid auth token) 2. If the player does have a valid auth token then jump right to an authenticated connection

(let [connection-url
      (cond-> ws-url
        (some? credentials)
        (str "?token=" credentials)

        (nil? credentials)
        (str "?token=anonymous"))

      set-new-connection
      #(let [ws (new js/WebSocket connection-url)]
         (doto ws
           (aset "onopen" (partial ws-on-open ws on-open))
           (aset "onclose" (partial ws-on-close ws on-close config))
           (aset "onmessage" (partial ws-on-message ws))
           (aset "onerror" (partial ws-on-error ws on-error))
           (aset "connection-identifier" connection-identifier))

         (reset! last-connection-attempt (time-now))
         (reset! conn ws))])
3. On the server, before the authenticated connection is created, the credentials are checked against the user pool to identify the player. 4. The player can now send / receive messages over the websocket. You would adjust the onopen onclose onmessage onerror handlers to work with your app. Not sure what you’re using for your websocket server, but I’ve had luck using https://github.com/ptaoussanis/sente and http-kit (one of its supported web servers) before.

Jakub Zika 2021-02-22T16:21:20.008300Z

Chocolatier is discontinued, yeah. Multimethod can get slower than using condp . Your code can get ugly once you will start optimizing JS / Java in Clojure - or you can keep your game simple and it should be fast enough 🙂.

Fabim 2021-02-22T16:46:08.011300Z

Slower of call time or initialization? I’m using multimethod a lot to separate out the logic of different cards. Didn’t notice any huge speed difference.

Jakub Zika 2021-02-22T16:46:56.011500Z

I remember that i had got some better call time results with condp but dont take it as granted. It is long time ago…

Fabim 2021-02-22T16:48:07.012100Z

What are you using Quil for?

Jakub Zika 2021-02-22T16:51:11.012300Z

I did some fun stuff like walking in 3d environment with wasd and jumping etc. But nothing is finished yet. I am more interested in creating the engine and the toolset than the game itself to be honest.

2021-02-22T18:53:56.012800Z

why not edn in a text field?

Fabim 2021-02-22T21:09:37.012900Z

how would you persist ‘a text field’?

2021-02-22T21:36:28.014500Z

Postgres is highly optimized for indexing and searching data at a granular level. If you do JSONB, you get all the indexing and search stuff for free. If you do EDN in a text field, it’s just an opaque blob as far as Postgres is concerned, so you can’t look inside it and search on / index its constituents.

Fabim 2021-02-23T13:48:53.016100Z

@paul.legato What are lib are you using to convert the edn game state into JSONB before saving it into Postgres?

2021-02-23T17:34:37.016300Z

JSONB just means ‘JSON binary’; it’s an optimized storage format used internally by Postgres. You can put any standard JSON in there. I use Cheshire, but any JSON will work.

2021-02-22T21:37:39.015700Z

you will wind up adding redundant metadata to the row that allows you to find, say, “inventory for player 1234” or “all players who have item XYZ”, which will eventually drift out of alignment with what it says in the EDN blob. Synchronizing two sources of truth is a hard problem.