Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 22:37:06 UTC, Liam McGillivray wrote: ...a tick system would go hand-in-hand with making animations happen in a separate thread, but it sounds like you're talking about the same thread. Are you suggesting a fixed framerate? I have done both ways. If you're new to programming, I think the single thread would be better since your game logic is fairly simple. Simply update your world, then draw everything then sleep. Steven Schveighoffer does a lot of entry-level games so hopefully he will respond if he disagrees. You can have a bool flag you set when an action happens that requires the win condition to be checked and then if that flag is set, check the win condition at the end of the loop, assuming only one player can achieve a win condition at a time. This is during the Unit's constructor, where it gives the `Tile` object a reference to itself. What exactly is wrong with this? Can memory addresses change when a constructor completes? I assumed that objects come into existence at the beginning of the constructor function. I think the idea is the object is not ready to use until the constructor is complete and if you leak a reference, someone else who doesn't know could use it and they might have expectations about it's state that are not upheld midway through the constructor. I used to do this a lot and never had a problem with it. In an application where you're the only developer and it's single threaded, it won't be a problem. It's just bad practice because on a team you want to be able to trust that a reference to an object is of a completely constructed object and you don't want to have to check it's constructor every time. Anyway, I can change this by calling `Unit.setLocation` after creating a new `Unit` object. That's a good plan I suppose the current system for setting up objects from a `JSONValue` is messy because much of the data applies to the base `Tile` & `Unit` objects, but then some of it needs to be in `VisibleTile` & `VisibleUnit` because it has sprite information. That's fine. I would move the code out because I only care about it when I'm working on loading from JSON, so it wouldn't be helpful to see it all the time when I'm working on game logic. The value comes in having less to see and think about at a time but it will work just the same (assuming you don't break it when you move it :). If you like it there, then leave it. Do you really think it would be better if I removed `Unit.currentTile`, and just used `Unit.xlocation` & `Unit.ylocation` as parameters for `Map.getTile` instead? Go with your gut. If you start having problems with them getting out of sync, then I would separate them. Most of my suggestions boil down to: if you have problems and the fix breaks something else and that fix breaks something else or it's just hard to understand, then my suggestion will probably help. If it ain't broke, don't fix it. I have considered doing a rewrite of the rendering system, in which all the rendering, and possibly input during game are handled by a new `Renderer` object, which may have it's own array with all the units in it. This sounds a little like my idea of the `Renderer` object, in which the state of what's on screen would be updated by calling it's methods, but having a log of the UI wasn't what I had in mind. I like the renderer idea. I would keep it in the same thread for now unless you really want to make a separate thread. Before going with the current approach, I intended to have a module called `loadData` which would read JSON files and make objects out of them I think you made the right call. A generic json file loader is overkill at this point. If you make a second game, make a generic and reusable json loader. I try to never make things generic unless I have multiple actually use cases that I can design against or if it's pretty obvious. //Always set destroy to false when calling from the Unit destructor, to avoid an infinite > loop. I was just doing some work on the AI system, and I had a segfault every time the enemy was in reach to attack a unit. It turns out this was because the destructor was called. I replaced destroy(unit) with map.deleteUnit(unit), and it solved the problem. Nevermind. It turns out this was because the call to the Unit destructor was missing in Map.deleteUnit. The segfault happens whenever a unit is destroyed. This is exactly the kind of problem circular references cause. You're not quite sure what makes the bug or when to consider it done, especially when there's multiple entry points into the cycle. In this case, how should I handle safe destruction of units, given that there are multiple arrays that contain references to it? Here are my options, in inverse preference order (neglecting the time it would take): 1. Keep it like you have it 2. Switch to the tick system. At the end of your updating the world phase, clean up and for each
Re: How to use eventcore write an echo server?
On Thursday, 14 March 2024 at 18:49:54 UTC, Dejan Lekic wrote: On Tuesday, 12 March 2024 at 05:13:26 UTC, zoujiaqing wrote: How to fix it? than you ;) Try the following: ``` class Connection { StreamSocketFD client; ubyte[1024] buf = void; // Add these two lines before the constructor: nothrow: @safe: this(StreamSocketFD client) ``` Also you will need to either comment out calls to writeln() or surround them in try/catch as writeln() may throw so it can't really be called in nothrow code... For starters just comment all calls to writeln() out to make it compile, and then move from there. This is the code I modified that can be compiled and run successfully. ```d import eventcore.core; import std.functional : toDelegate; import std.socket : InternetAddress; import std.exception : enforce; import core.time : Duration; import std.stdio : writeln; void main() { auto addr = new InternetAddress("127.0.0.1", ); auto listener = eventDriver.sockets.listenStream(addr, toDelegate()); enforce(listener != StreamListenSocketFD.invalid, "Failed to listen for connections."); writeln("Listening for requests on port ..."); while (eventDriver.core.waiterCount) eventDriver.core.processEvents(Duration.max); } nothrow @safe: void onClientConnect(StreamListenSocketFD listener, StreamSocketFD client, scope RefAddress) { import std.array: appender; import std.algorithm: copy; try { writeln("onClientConnect"); Connection connection = new Connection(client); auto w = appender!(ubyte[]); string str = "Welcome to use my echo server."; copy(str, w); connection.write(w[]); } catch (Exception e) { // pass } } class Connection { StreamSocketFD client; ubyte[1024] buf = void; nothrow: @safe: this(StreamSocketFD client) { this.client = client; eventDriver.sockets.read(client, buf, IOMode.once, ); } void write(ubyte[] data) { eventDriver.sockets.write(client, data, IOMode.all, ); } void onWriteFinished(StreamSocketFD fd, IOStatus status, size_t len) { try { writeln("Send size: ", len); } catch (Exception e) { // pass } } void onRead(StreamSocketFD, IOStatus status, size_t bytes_read) { if (status != IOStatus.ok) { try { writeln("Client disconnect"); } catch (Exception e) { // pass } eventDriver.sockets.shutdown(client, true, true); eventDriver.sockets.releaseRef(client); return; } this.write(buf[0 .. bytes_read]); eventDriver.sockets.read(client, buf, IOMode.once, ); } } ```
Re: How do I use libdparser, or any library for editing D code?
Unfortunately there's no "edit" option here, but the library I was referring to is actually "libdparse".
Re: Can a D library have some types determined by the client program?
On Thursday, 7 March 2024 at 22:18:40 UTC, Richard (Rikki) Andrew Cattermole wrote: There are two ways to do this. 1. Use templates. https://tour.dlang.org/tour/en/basics/templates Thank you for teaching me how to do this. This is where I first learned to use templates in D, and I have been using them since for functions, and as of yesterday, my first mixin template. That being said, I'm pleased to announce the return of the `Map` class. As of yesterday's commit, the class template `class MapTemp(TileType:Tile, UnitType:Unit)` and `interface Map` have now been replaced with a single `Map` class as they were before. At the time I had asked about this, I had not yet discovered that an array of objects can be filled with objects of a derived type by just using the regular cast syntax. Now that I know this, I've decided to just create the objects under the `VisibleTile` and `VisibleUnit` classes, place them in the `Map` object's arrays (cast to `Tile` and `Unit`), and then cast them back whenever I need to access anything specific to the derived classes. Now I no longer have to deal with the limitations of interfaces. Things should be easier now. The straw that broke the camels back was when I made my first mixin template, which adds functionality to a class for manipulating arrays of `Unit` objects. The compiler wasn't allowing it in the `Map` class because the array that I was trying to give it access to was of `UnitType`, not specifically the base `Unit` class. I'm happy to have it back.
Re: Why is this code slow?
On Sunday, 24 March 2024 at 23:02:19 UTC, Sergey wrote: On Sunday, 24 March 2024 at 22:16:06 UTC, rkompass wrote: Are there some simple switches / settings to get a smaller binary? 1) If possible you can use "betterC" - to disable runtime 2) otherwise ```bash --release --O3 --flto=full -fvisibility=hidden -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L=-dead_strip -L=-x -L=-S -L=-lz ``` Thank you. I succeeded with `gdc -Wall -O2 -frelease -shared-libphobos` A little remark: The approximation to pi is slow, but oscillates up and down much more than its average. So doing the average of 2 steps gives many more precise digits. We can simulate this by doing a last step with half the size: ```d double leibniz(int it) { double n = 1.0; for (int i = 1; i < it; i++) n += ((i%2) ? -1.0 : 1.0) / (i * 2.0 + 1.0); n += 0.5*((it%2) ? -1.0 : 1.0) / (it * 2.0 + 1.0); return n * 4.0; } ``` Of course you may also combine the up(+) and down(-) step to one: 1/i - 1/(i+2) = 2/(i*(i+2)) ```d double leibniz(int iter) { double n = 0.0; for (int i = 1; i < iter; i+=4) n += 2.0 / (i * (i+2.0)); return n * 4.0; } ``` or even combine both approaches. But of, course mathematically much more is possible. This was not about approximating pi as fast as possible... The above first approach still works with the original speed, only makes the result a little bit nicer.
Re: Limits of implicit conversion of class arrays
On Saturday, 23 March 2024 at 11:04:04 UTC, Dmitry Olshansky wrote: The first and second is unsound (infamously allowed in Java). In the general case, yes. But, do you see any errors with the code ```d class Base {} class Derived : Base {} @safe pure nothrow unittest { Base b; Derived d; b = d; // pass Base[] bs; Derived[] ds; bs ~= ds; // pass bs = ds; // fail [1], should pass bs = cast(Base[])ds; // fail [2], should pass } ``` Once you cast the slice you can populate it with Derived2 objects that are not Derived, hence breaking type safety of the ds slice. Again, in the general case, yes. So what is different in this code example compared to the general case? Hint: this has overlaps with a missing compiler optimization in dmd (and many other statically typed languages) enabled by a specific kind of data flow analysis. Which one?