Re: Reworking the control flow for my tactical role-playing game

2024-03-25 Thread harakim via Digitalmars-d-learn
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?

2024-03-25 Thread electricface via Digitalmars-d-learn

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?

2024-03-25 Thread Liam McGillivray via Digitalmars-d-learn
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?

2024-03-25 Thread Liam McGillivray via Digitalmars-d-learn
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?

2024-03-25 Thread rkompass via Digitalmars-d-learn

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

2024-03-25 Thread Per Nordlöw via Digitalmars-d-learn
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?