• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

[Dev] 0.1 "Babylon" Progress Thread

Quintillus

Resident Medieval Monk
Super Moderator
Supporter
Joined
Mar 17, 2007
Messages
9,304
Since Puppeteer has given his approval to the "Carthage" thread, it probably makes sense to have threads for the other milestones.

Babylon consists of:
  • World map
  • Map visibility
  • Prototype mods
  • Movable units
  • Place cities
  • Prototype screen navigation
--------------

Status:

I'm trying something new by trying to summarize the status of the objectives in this area. This is subject to interpretation, and will change with time, but having it in one place might help build consensus on where we are milestone-wise. Last updated on December 12, 2021.

DONE
  • Place cities.
ADEQUATE
  • Prototype screen navigation. We have some of this. We should probably have a little bit more, e.g. wire up the menu button in the top left to do some things. But we know how to do it.

IN PROGRESS
  • World map. Still remaining are Marshes, rivers, flood plains along rivers, and small things like deltas and waterfalls.
  • Movable units. You can now move them with the Num Pad, but not with the mouse.
  • Prototype mods. I believe Puppeteer has done some work along this line.

TODO
  • Map visibility (fog of war, right?)
----------------

World map/map visibility somewhat spans "Aztec" (prototype tilemaps, which is what I'd say we have now), "Babylon" (world map, which I'm taking to mean a more "real" map rather than a tile assortment; map visibility, is that fog of war/player map tile visibiilty?), and "Carthage" (save/load, where load would be be helpful for loading saved maps). I somewhat suspect we may use part of the "load" for the world map initially, as loading the map part of a BIQ is probably easier than generating a random map. But maybe others have good random map generation ideas.

Post your Babylon updates here!
 
Last edited:
Scroll/Zoom

Oh yeah, that reminds me I forgot to reply to your mention of zooming in another thread. I recall working on several forms of scrolling and zooming, and of trying to get wraparound to work.

With zooming in particular, scroll with with a mouse wheel, pinch zoom on a touchscreen laptop, and pinch zoom on the Mac trackpad all needed different trigger code.

I thought I had scrolling working pretty well, but it's been a while since I've messed with it. It's entirely possible I broke it while working on something else either temporarily intentionally or inadvertently. Oh yeah, I also had touchscreen drag-to-scroll working I think. I'd have to get back into that a bit to re-figure it out.

Fog of War

If visibility is fog of war, it should be trivial to include at least not showing undiscovered tiles. The Civ3 SAV file has what I call "is visible" and "is visible now" values, the latter apparently indicating either culture-based visibility or culture and unit/tower visibility. (Or maybe the latter is actually marking owned tiles...needs a bit more investigation.)

Getting the pretty edges is something I haven't done yet, and enabling "fog peeking" would require a bunch more logic to draw then mostly obscure visible-adjacent tiles. Maybe it's not as hard as I think, but I never previously considered putting in the effort for my purposes. For C7 I'm sure someone will eventually ask about it, and the map will eventually need non-jagged edges.

In short, I expect to have fog of war to a point where it only draws non-spoiling map tiles for (an) arbitrary player(s), but obviously at first we'll have that hard coded to player 1's view.
 
I was thinking some today about the Movable Units, Place Cities, and things like that, that are part of Babylon. And, how we would want to go about tackling them.

Obviously there are some graphics involved with those. But I think the more core thing necessary to proceed on those is the very basics of a data structure. I reviewed the proposal document, particularly section 6. Among other things, it points out a desire to be an event-driven architecture; to use the Type Object pattern (an example could be that each unit has a Type, which points to e.g. a Sea or Air or Land or Artillery instance, rather than creating each unit as a SeaUnit object, or AirUnit object); and to use the Toolbox Pattern which "provides global static access to component instances and game state data, so that components can access each other without holding references." (the link for that is dead in the document; I've linked a fairly recent archive.org copy).

WildWeazel has bequeathed us with the beginnings of this, in particular ComponentManager.cs and TurnCounter.cs, which if my readings are correct, demonstrate the start of the Toolbox Pattern; the StartGame method in Game.cs also links up the TurnCounter to an event. Although I should note that like most things I've looked at in C7 this week, I've been traveling 98 miles per hour rather than taking things slowly and carefully, so my understanding is likely imperfect.

This has me thinking that perhaps we should add some new components that are accessible via the ComponentManager soon, for example, ones for Tiles, Units, and Cities. Tiles is probably the most logical one to start with, since we are already dealing with them via the map, if only graphically. But I'm thinking that if we have a TileComponent that has all the tiles, a logical next step would be having the user click on a tile on the map, and then using the TileComponent to print out what type of tile it is, e.g. Grassland. That would prove we can map a click on the map to a tile (probably a Godot Tile that is part of a TileMap), to an actual Tile object in our data structure.

As Puppeteer has been doing more tile work than I have (as has Flintlock over the past day), I'll leave my tile thoughts at that.

Though I did touch on "data structure". As Puppeteer has mentioned a few times, we don't have a native data format yet. WildWeazel wrote in section 6.1:

No more obscure binary files. Saves, scenario files, configs, and anything else with serialized data will use a common human readable format, likely JSON. We will develop editors for these files or you can use generic third party tools.

I agree with this direction, but it doesn't tell us exactly how to implement it in C#. My thinking for now is that we start with something very basic, just so we can do proofs of concepts that things are wired up and interacting with each other. In Java terminology, that would mean creating POJOs - plain old Java objects; I'm not sure what the term is in C#. But the gist is that a MapUnit might have several fields, e.g. xLocation, yLocation, MovesAvailable, as well as a pointer to a UnitType object. One of the benefits of POJOs is that serializing them is often quite easy; some libraries will do it automatically as long as you don't have e.g. circular references, and you can exclude certain fields that aren't supposed to be serialized. But it's also worth noting that I think in the short term (i.e. November), we can ignore serialization as it's likely easy to add on a little bit later (not when the game's done, but in a month or two), and for now it's more important to have some of these basics in Babylon working that to load or save based on C7's native format. Saving and Load, for reference, is in 0.2 "Carthage", so the document is in alignment with my thinking that it's a relatively early thing, but not as early as having enough set up to move units and build a city (without the ability to save the game and resume it later).

I am also thinking that, for now, we would automatically add a small number of units when you start a new game, ignoring the fact that someday we'll be reading that in based on a scenario file, BIQ or our own format. A few things like unit graphics and unit abilities can be hard-coded long enough to prove we can display a unit and the player can use that first ability (move unit, then maybe build city so we can prove that interaction works). Once those very basics are working, we'll build out more things, maybe three UnitTypes (Settler, Worker, Warrior) that allow the earliest unit mechanics to be developed, and progress to be made towards the "Babylon" goals without having to have the entire scenario format/input piece built out yet.

I may start adding some code in this direction tomorrow, though I'm definitely interested in hearing whether this makes sense to others, as well as how the current state of the map would apply.
 
Today I polished the map some more. I simplified the algorithm so that instead of the four cameraTileX/Y & cameraPixelX/Y member variables there is now only a cameraLocation variable which tracks the total offset of the camera from the origin. The cameraTile and cameraPixel vars are computed as needed in RefillMapView. This let me adjust the map to also work in cases where edges don't wrap, which should be easy but wasn't before the simplification. Since I think it's in a good place now I'll merge the repeating map branch into Development soon unless there are any objections.

It's in a good place but the map movement is not yet perfect, there are two things left to do:
1. Clamp scrolling around unwrapped edges. Right now you can still just scroll off the edges. This is easy for the top and left since the boundary is zero but it will require some thought for the bottom and right edges.
2. Keep the map centered on the same tile as the zoom level is changed or the window is resized. Part of the problem here is the Godot camera is still a factor and it moves when the window is resized so we could either recenter the map on the camera or anchor the camera in place and center the map on the window. I prefer the second option.
I may start adding some code in this direction tomorrow, though I'm definitely interested in hearing whether this makes sense to others, as well as how the current state of the map would apply.
I agree it's time to add a Tile object. I've also been thinking about factoring out a Map object from Game, that would make the code neater especially once we have multiple map layers. A simple and natural first step would be to convert the game map into a 2D array of Tiles instead of an array of ints (specifying terrain) like it is now. I don't know about the toolbox architecture, I'll have to read up on that, and I'll admit I don't know how the event architecture is supposed to work or even what problem it's supposed to solve. None of this would conflict with what I've been doing with repeated tile maps since the MapView is a step removed from the map itself.

Also, have any of you given any thought to implementing multiplayer? IMO that's the biggest open architectural question. We should plan for that before we start working on the gameplay code because it would be very difficult to patch in after the fact. That's what Firaxis did for Civ 3 and is why its multiplayer is so buggy and crashy.
 
Also, have any of you given any thought to implementing multiplayer? IMO that's the biggest open architectural question.

I vaguely recall discussing it. (It may have been in email before this subforum was created...don't recall at the moment.) Of course there is hotseat / play-by-email multiplayer, but I presume you mean online live multiplayer.

Godot *does* have a standalone headless server, but since our game logic is all C#, I think we can just have a C# server.

Beyond that I know we brainstormed a bit, but I don't think there was really a particular direction.
 
Today I polished the map some more. I simplified the algorithm so that instead of the four cameraTileX/Y & cameraPixelX/Y member variables there is now only a cameraLocation variable which tracks the total offset of the camera from the origin. The cameraTile and cameraPixel vars are computed as needed in RefillMapView. This let me adjust the map to also work in cases where edges don't wrap, which should be easy but wasn't before the simplification. Since I think it's in a good place now I'll merge the repeating map branch into Development soon unless there are any objections.

It's in a good place but the map movement is not yet perfect, there are two things left to do:
1. Clamp scrolling around unwrapped edges. Right now you can still just scroll off the edges. This is easy for the top and left since the boundary is zero but it will require some thought for the bottom and right edges.
2. Keep the map centered on the same tile as the zoom level is changed or the window is resized. Part of the problem here is the Godot camera is still a factor and it moves when the window is resized so we could either recenter the map on the camera or anchor the camera in place and center the map on the window. I prefer the second option.

I have no objections. I've been taking the backseat on the map, and it's been progressing well.

I agree it's time to add a Tile object. I've also been thinking about factoring out a Map object from Game, that would make the code neater especially once we have multiple map layers. A simple and natural first step would be to convert the game map into a 2D array of Tiles instead of an array of ints (specifying terrain) like it is now. I don't know about the toolbox architecture, I'll have to read up on that, and I'll admit I don't know how the event architecture is supposed to work or even what problem it's supposed to solve. None of this would conflict with what I've been doing with repeated tile maps since the MapView is a step removed from the map itself.

My understanding of the event architecture direction is that by emitting events in certain areas, and receiving them in others, it should help avoid tying the code too tightly together (which could make modding or modifying it more difficult), and perhaps also make it easier to separate the UI from the back end. Although I'm a little fuzzy on the event driven versus simply separating the UI logic from the core game mechanic logic (probably a C# "server" as Puppeteer mentioned, although I'm not sure I like the term "server" since it would be running locally in single-player).

So what I'm thinking now is that in the near future, we should probably add a could more sub-modules to our existing C7, ConvertCiv3Media, and QueryCiv3. One would be the C# server, or back-end, which will handle the mechanics. It will handle things like checking whether you can move a unit from Tile A to Tile B, calculating combat, and AI, if AI doesn't wind up in a separate module. Another would be a data model. This could be in the C# server, but I'm leaning towards keeping it separate so it's easier to keep the high-level mechanics separate from low-level data.

The front-end C7 Godot project would then call the back-end C# Server and say, hey, the player would like to move this unit here, or, the player changed which item they're building in Tokyo. The C# server could respond with an appropriate status, perhaps the unit is moved to a tile with another unit, and it responds with the results from the combat. Hypothetically, let's say your Regular Archer attacks my Veteran Spearman, maybe it responds with something like:

result = attack
target = spearman, position 24, 17
combatRounds = win, win, lose, win, lose, win

(Probably formatted differently)

The Godot UI could then play the appropriate animations based on that response, and the game state would already be updated for the end of combat on the server.

Also, have any of you given any thought to implementing multiplayer? IMO that's the biggest open architectural question. We should plan for that before we start working on the gameplay code because it would be very difficult to patch in after the fact. That's what Firaxis did for Civ 3 and is why its multiplayer is so buggy and crashy.

Not a lot specifically about multiplayer. But you're right, patching that in after the fact would be a challenge.

I think it would be a reason to make sure the UI and back-end mechanics piece are separate. And in theory what I described above could even be done across a network. But I also really don't know about things like synchronization between multiple players. E.g. while your Archer attacks my Spearman, Puppeteer's Swordsman moves into the area. In web programming, the thing that comes to mind is WebSockets, an example being you have a metro tracking application, and one of the trains move, you can broadcast its new position to all the clients really easily. But how that would translate to a desktop Godot game in C#, I don't know.

The other thing that I am reminded of is Civ4's infamous Out of Sync errors (and to be fair, it's not just Civ4 that has those, it just had a lot of them). There is probably a lot of complexity is synchronizing state across machines that may be across the world and may not have great network connectivity. I'm wondering if it would make sense to have an incoming action queue? So you move your Archer, the server locks an "update" resource, and if Puppeteer's Swordsman request arrives at the exact same time, it'll wait until the Archer update is processed, so e.g. if his Swordsman is trying to move to the same tile, we won't wind up with two units on the same tile (or my Spearman losing to your Archer but somehow defeating his Swordsman), but instead the server will reply something to the effect of "invalid move". The server could also send out an update to all the clients after your Archer defeats my Swordsman, although at that point we're getting into questions such as, "if Ozymandias's client is viewing a different part of the world, does it care about that update? if not, what about when he moves the map to view that part of the world?"

Yeah, that sounds complicated the more I think about it. I'm thinking maybe each client keeps a copy of all the data, so the Godot UI can request things for the map directly from its copy of the data. But to make any changes to the data, it has to go through the server. Which leads to questions about how the "C# server" would behave differently on the "real" server versus a networked player's server. Maybe if a networked player moves a unit, it calls the "move unit" method on their local C# server, but that in turn calls the "real" server and awaits a response, whereas if you're playing single player, it just makes the update.

No wonder things could get a bit laggy in multiplayer in Civ III and Civ IV.

And that's also not covering things like port forwarding, or alternatives to that.
 
On the subject of an event architecture, as referenced by @Flintlock , I found that my most recent commit message, as influenced by the code I wrote, was touching on that very point. To quote the relevant part:

Commit Message said:
As I've been working on making the advisor work well again, it's also been occurring to me that if we have all of the buttons be handled in Game.cs, there is going to be kind of a lot going on there. Which is less good if we have several people working on it that in my editor where it's just one person and there are never any conflicts... and even there it eventually becomes a bit much. I think this is part of why WildWeazel was thinking we should have an event based system. The keyboard listener hears F1 and emits, "show the domestic advisor". The advisor button is clicked and emits "show the domestic advisor". Somewhere out in the program, there's an advisor component that's listening for "show the domestic advisor", and when it does, it makes its UI components visible. That would de-centralize Game.cs as the communication hub for the program.

For this to work, we'll have to have a system other than GetParent().GetParent().GetParent().EmitSignal("someSignal"), which is handled in Game.cs. I'm going to ponder the code and perhaps the Godot docs a little bit. I have read in the Godot blogs that the EmitSignal is foreign to C# native, and there's a more C# way that they're moving towards, but I'm neither sure if that's ready yet, not what it is, not being a C# native myself (in Java, we'd probably use a third-party library for this problem).

-----

Edit: Making a little bit of progress through reading Godot docs and experimenting with the domestic advisor. I've figured out how signals are connected via the .tscn files, and the domestic advisor now only has to call GetParent().EmitSignal(), which is an improvement. I've also determined that e.g. the advisor could send a signal to the GameStatus node directly, if we had a reason to do that.

I think I'm going to try to see if I can create a Node for the top-left buttons as a whole, which for now are stuffed in the Toolbox individually, and stuff them in there as a group. If I get a TopLeftControls node, then in theory it should be able to emit a Signal that is received directly by the Advisor node, which could set itself to visible. That does mean getting scripts for both nodes; right now the Advisor node is just a container without any scripts. But if it works, it would show how to handle Signals directly between components.
 
Last edited:
Adding on to the "multiplayer" question. I looked in WildWeazel's document, and he lists "Network Programmer" under stretch goals:

Stretch goals

These roles will only come into play if and when we achieve feature parity with Civ3 in all other areas, and the project is moving towards a proper game release.

  • Mapmaker: someone experienced with procedural generation, to develop a data-driven map generator that uses arbitrary terrain definitions.

  • Network programmer: someone experienced with networking code particularly in the chosen engine, to help develop a remote multiplayer solution.

  • AI programmer: someone to develop a data-driven AI that can play the game with any mod.
Community manager: if the project really takes off, this person will be responsible for promoting C7 online and interfacing with players about updates and feedback.

AI programmer is also under stretch goals. We'll need either network programming or at least a little bit of an AI to have it be more than one civ in play though.

Multiplayer does not appear to be listed in the milestones ("Babylon", "Egypt", etc.), though neither does AI. The milestones are more mechanic focused.

My current thinking is we should give some thought to designing so it will be amenable to multiplayer, but not focus on implementing it, unless we have someone who wants to focus on it. I'm definitely interested in what WildWeazel thinks though, or anyone else with multiplayer game experience.
 
On the subject of an event architecture, as referenced by @Flintlock , I found that my most recent commit message, as influenced by the code I wrote, was touching on that very point. To quote the relevant part:
Interesting. Does EmitSignal block execution until the signal is handled? Is it possible to make it do that? Because that's what you'd want to do for any UI interaction where the player is asked to make a choice, like if the player captures a city and must decide whether or not to raze it. As I remember from reading WildWeasel's Google doc there were going to be events for gameplay occurrences like that.



About multiplayer, I've thought about it myself a bit and I don't have any better idea than a client/server architecture like you described. I think with any kind of peer-to-peer architecture we'd have a difficult time keeping things synchronized. I wonder if it would be plausible to have all state updates done on the server and broadcast to the clients or if the server would instead broadcast a stream of accepted moves and have the clients update their own states. I don't have a good sense of how much bandwidth the first option would require. The second one would require less bandwidth and be easier to implement though it might be more prone to desyncs.

The server could also send out an update to all the clients after your Archer defeats my Swordsman, although at that point we're getting into questions such as, "if Ozymandias's client is viewing a different part of the world, does it care about that update? if not, what about when he moves the map to view that part of the world?"
Easiest thing to do is probably to give every client the complete state. Though one advantage to giving them only the state they can see is that they can't cheat the map visibility by inspecting the program's memory.

Another thing that ocurred to me re: multiplayer is that it would be a good idea to give each game object a unique ID (it need not be any more complicated than a serial number). That solves a potential synchronization problem around object creation/destruction. Like if you ordered that spearman to retreat but the before that order reached the server the spearman was killed and another unit was created with the same ID.
 
For IDs, @WildWeazel has been very particular about object GUIDs for everything, but I haven't yet grokked what that's going to look like. So far I'm trying to design code using interfaces and having IDs like IEquatable so I can use ints for now and swap out to GUIDs when appropriate.

Re: websockets...psst: http long polling. It traverses more networks better. And scales much better if anyone were to ever have a popular multigame server.

Edit: By the way, I'm in a "chasing all the shiny things" mode right now, so I'll probably stay out of Game.cs for a bit. Re: scrolling, I keep forgetting if old Civ3 even has vertical wrap maps available. And I wonder if we want the ability in C7. Probably, as we want to remove limits, but lots of code would have to check and consider vertical wrapping when calculating distances and pathfinding.

Edit 2: @Quintillus when you start rearranging the control nodes, be aware that it's easy to break the input handling. I ran across a comment in the code about that today:

Code:
        // Control node must not be in the way and/or have mouse pass enabled

Fixing it is a matter of reordering the Control nodes. I think the bottom-most one gets the input first?
 
Last edited:
Interesting. Does EmitSignal block execution until the signal is handled? Is it possible to make it do that? Because that's what you'd want to do for any UI interaction where the player is asked to make a choice, like if the player captures a city and must decide whether or not to raze it. As I remember from reading WildWeasel's Google doc there were going to be events for gameplay occurrences like that.

I am not sure whether it blocks execution. So far all of mine have been handled quickly; it may be worth adding an intentional wait state to a handler to test that. Although I'm not sure it's wholly the relevant thing to the example of the player having to decide whether to raze a city, before e.g. sending another unit somewhere. We'd have an event that would cause the pop-up to appear, and yes, we wouldn't want the player to be able to do other things between the event being emitted and received, and the pop-up displayed. But probably the more relevant problem is even once the event is handled and the pop-up is visible, how do you prevent them from clicking on some other city or unit?

On a former project I worked on, we solved that sort of problem with a "shield of justice". If there's some sort of overarching modal like "raze or don't raze?" that needs to be handled, we'd display a translucent "shield of justice" over the whole UI, which prevents clicking anything below it, and then display the relevant item above it. Of course, it could be that Godot has built in modal support that makes this sort of thing easy, but if we need it...


About multiplayer, I've thought about it myself a bit and I don't have any better idea than a client/server architecture like you described. I think with any kind of peer-to-peer architecture we'd have a difficult time keeping things synchronized. I wonder if it would be plausible to have all state updates done on the server and broadcast to the clients or if the server would instead broadcast a stream of accepted moves and have the clients update their own states. I don't have a good sense of how much bandwidth the first option would require. The second one would require less bandwidth and be easier to implement though it might be more prone to desyncs.

Easiest thing to do is probably to give every client the complete state. Though one advantage to giving them only the state they can see is that they can't cheat the map visibility by inspecting the program's memory.

Another thing that ocurred to me re: multiplayer is that it would be a good idea to give each game object a unique ID (it need not be any more complicated than a serial number). That solves a potential synchronization problem around object creation/destruction. Like if you ordered that spearman to retreat but the before that order reached the server the spearman was killed and another unit was created with the same ID.

I'd have to ponder peer-to-peer versus client/server. Either way, I suspect bandwidth is not going to be a major problem, as long as we're only sending status updates. Those might be famous last words, but beyond the initial setup (e.g. a whole map), the updates should be in the bytes to kilobytes size.

For IDs, @WildWeazel has been very particular about object GUIDs for everything, but I haven't yet grokked what that's going to look like. So far I'm trying to design code using interfaces and having IDs like IEquatable so I can use ints for now and swap out to GUIDs when appropriate.

Re: websockets...psst: http long polling. It traverses more networks better. And scales much better if anyone were to ever have a popular multigame server.

Edit: By the way, I'm in a "chasing all the shiny things" mode right now, so I'll probably stay out of Game.cs for a bit. Re: scrolling, I keep forgetting if old Civ3 even has vertical wrap maps available. And I wonder if we want the ability in C7. Probably, as we want to remove limits, but lots of code would have to check and consider vertical wrapping when calculating distances and pathfinding.

Edit 2: @Quintillus when you start rearranging the control nodes, be aware that it's easy to break the input handling. I ran across a comment in the code about that today:

Code:
        // Control node must not be in the way and/or have mouse pass enabled

Fixing it is a matter of reordering the Control nodes. I think the bottom-most one gets the input first?

Good point re: ids (for both of you). Reusing them could be hazardous. Http Long Polling... I remember using that before WebSockets was a thing. I thought WebSockets was supposed to replace it? But I've only used WebSockets on hobby projects (worked great, but probably never had more than 10 clients at a time), and haven't used HttpLongPolling since 2014 or 2015. But I've also only used WebSockets on a web site, so I wouldn't have run into any network traversal problems that using it in Godot would introduce.

I probably need more clarification on the hazard with breaking input handling. My understanding is that the controls are essentially in a stack, so e.g. the blinking "end turn" button would block input to what is behind it, unless we enable mouse passing. And I did see tonight that when I created a node to contain advisors, with the Domestic Advisor inside of it, and I only hid the Domestic Advisor but not the node to contain advisors, the now-invisible node to contain advisors blocked input behind it. I'm pretty sure I caught that before committing though.

The buttons should wind up at the same place as they were before, just placed there via the .tscn rather than in code via Game.cs. But let me know if I've unintentionally broken something. I think it should be okay, but am definitely learning as we go.
 
If the controls work, you're good. I added something the other day and literally nothing worked: scrolling, zooming, pushing buttons...and I had to reorder the nodes to fix it.

Websockets are a new (-ish) shiny, but in particular proxies and load balancers tend to block or just not support it. I've only messed with them some with chat programs and for the http API version of cia3. Websocket is a pain in the butt and not really needed in most cases. It connects http and then upgrades the connection and then keeps it open indefinitely, each of which can and often does run into problems.

Http long polling was adequate, seems to be a default fallback for chat programs, and has libraries to make it about easy to use as http/https. Latency is a hugely touted advantage of Websockets, but for the traffic I've seen the few extra ms it saves doesn't really make a difference. And http long polling is still low-latency on the client end because the client is mostly just waiting.

For C7, data up will be player-driven, anyway, not a stream of constant data.

Websockets may have a use in high-reliability environments like same-machine or same-LAN, but my impression so far is not to mess with them unless somehow required over a large network.


I'm not sure I've ever played Civ3 multiplayer online. If you can have more than two human players, then peer-peer sounds problematic. Even if there are good mesh libraries these days–I'm not sure of that, but it sounds like it could be a thing–traversing NATs would seem to be a challenge. Or maybe uPnP has come a long way since I've messed with it. Or maybe I'm still in the stone age with respect to peer-peer networking.
 
I am not sure whether it blocks execution. So far all of mine have been handled quickly; it may be worth adding an intentional wait state to a handler to test that. Although I'm not sure it's wholly the relevant thing to the example of the player having to decide whether to raze a city, before e.g. sending another unit somewhere.
Maybe city razing isn't the best example, I was trying to think of a case where you'd run some game logic, ask the player to make a choice, then run more game logic that depends on the choice made. In cases like that it would be easier to block execution of the game logic while waiting for the decision instead of shelving everything and resuming it later. That's how it's done in the original code and it usually makes things simple and easy even though it entangles game logic with UI. Though now that I've thought about it some more, I think it's probably easier overall to do the shelving because in multiplayer the state of the game could change underneath you anyway while you're waiting for the player to make a decision.
Of course, it could be that Godot has built in modal support that makes this sort of thing easy
I should hope so.
 
Signals are non-blocking, but in an event-driven context we get to decide what gets...well, not blocked, but delayed.

Take for example the open file dialog in the TempTiles scene. A UI button is on the screen, and when it's clicked it sends a signal, and there is a listener that takes that signal and knows to run _on_OpenFile_Pressed() . That function just calls the Popup_() method of a custom file dialog object previously attached to the scene tree during setup. That dialog is mostly stock from Godot, but I think I made it find the Civ 3 location and open there.

That dialog (as provided by Godot) sends a predefined signal, and the tscn file (or a coded line) has a dev-defined listener (Connect()) that knows to run
_on_OpenFileButton_pressed in the proper scene script (as specified). In this case, the string path for the file is passed as part of the signal. And that handler function calls the code needed to load the selected save into the game and switch to the game UI scene.

Nothing is blocked. But neither is anything run until we tell it to, either via _Ready(), an explicit function call, or an event with a listener (a "connected signal" in Godot parlance).

If I wanted to have code calculating pi to infinity at the load screen, all that I described above could happen while it's doing that.

In fact, the C# code we're making has no native entry point. There is no main function. It's a collection of class definitions, and Godot intrinsically calls their constructor when instantiated, and then explicitly calls _Ready() when the object is properly attached to the scene with all its scene-defined children (I think it calls bottom-up, but double check me on that), and our persistent game logic, if any, will be started probably from a ready method.

Nothing happens unless and until we tell it to happen, asynchronously.
 
Alright. It makes sense that signals are non-blocking otherwise they'd be basically the same thing as a function call, though I guess you might want to call a function on a different thread.

Do you guys think it's worth it to block game logic while waiting for the player to respond to popups? The reasons I can think of to do that are (1) it makes the code simpler in some cases and (2) it's what the original Civ 3 does so we'd be closer to an exact reproduction. For example, in the original when you get the "city finished production" popup, you can zoom to the city then scroll to other cities that haven't had their interturn update run yet and change things around. There's also a serious exploit you can do by going into the science advisor screen through the "tech researched" popup then flipping over to domestic and changing your tax slider settings. I don't think it's a big loss if we leave behind the game's exploits.



Today I pulled the map viewer code out of the Game class into its own class called MapView. MapView is a Node2D that gets added to the scene tree like the old TileMap of the same name. Sometime soon-ish I'd like to add the concept of map layers so we can draw more things, like units especially. I'm thinking there will be two kinds of layers: ones that are TileMaps (terrain, civ borders, resources, pollution?) and others that are loose sprites (units, cities?). But first I'm looking into more basic things like figuring out which tile is at a certain screen location so we can click on tiles.

By the way @Puppeteer, I changed how you had the rows staggered in TerrainAsTileMap so x starts at y%2 instead of 1-y%2. This way there is a tile at (0, 0) like in the original game. Is there some reason you did it that way in the first place?
 
Last edited:
I changed how you had the rows staggered in TerrainAsTileMap so x starts at y%2 instead of 1-y%2. This way there is a tile at (0, 0) like in the original game. Is there some reason you did it that way or just an oversight?

That's on purpose. The graphics tiles are offset from the real tile. Each real coordinate is made up of four corners of graphic tiles at the not-real coordinates. If you load a game file in TempTile with y % 2 instead of 1-y%2 you'll see it's really messed up.

And actually I'm not sure if the graphic should be on the left or right. We'll find that out.

What's more confusing is that the overlay terrain, resources, cities, and such are centered (-ish) on the true coordinate. I think rivers and flood plains are off-center like the base terrain. Not sure offhand about roads and railroads.

But I kind of have in mind drawing them at their true coordinates but just having those layers' Node2d shifted 64 pixels instead of changing coordinate systems.

I'm really hoping a native future format won't have these issues.
 
I don't quite understand. I know the tiles are not centered on their nominal locations, i.e. the tile at (x, y) is a diamond centered on (x+1, y+1), but the tile data should still be stored according to the tiles' locations. The way you had it set up there was no tile at (0, 0), in other words the value of Map[0,0] was never set. So I'm not sure how you could load a game properly since the coordinates wouldn't match. BTW I also made the corresponding change in the viewer so the randomly generated maps still look proper.
 
Ok, but load a real game map from a real civ 3 game into TempTiles.tscn under the UtilScenes directory. (Open the scene in Godot and then use the run scene–not project–button or control-R.) If not laid out as I put them, it wouldn't look right.

Oh, and I just realized that a Node2D shift won't work because each row shifts different directions. :p . Oops.

I'll try it and attach a screenshot to this post.


Edit: Ok, maybe I'm confused. The TempTiles script doesn't have the 1-y%2 pattern, and LegacyMap just uses the file id and tile index. So you're right. I guess when I made the display code that was in Game.cs I had remembered incorrectly.

Edit 2: Now I'm wondering if the current random terrain generation is also flawed. Now that I'm looking at it critically, there is a definite skew from the top right to bottom left that shouldn't be in the random noise. I don't mean from what you did; I think it was like that before.
 
Last edited:
@Flintlock , your scroll wrap code is in Development now, right?

I just cleaned up branches–I'm pretty darn sure I only axed the ones that were already merged (0 ahead of Development), but now I'm panicking a bit because my map isn't trying to wrap.

(If I did horribly screw something up, it's not gone; I'd just have to go digging through the raw git data to find the reference.)

Edit: Ok, I found the reference still in my local repo and re-added the branch to GitHub. It is also in Development commit history, but I can't find the mapWrap bools in the head code right now. Trying to figure out where they went.

Moar edit: Ah, I see now; it's refactored and segregated. All is well.

By the way, since I've switched back to the Godot noise for the in-Game.cs generator, the tiles aren't wrapped right now. I figure on fleshing out the generator in GameData and map loading and generation doesn't really belong in the main Godot project.
 
Last edited:
I had left wrapping turned off because I liked being able to zoom out and see the whole map, it's more convenient for what I'm doing right now. As I guess you've already figured out, the MapView is now a separate class that receives the wrapping settings when it's constructed.
 
Back
Top Bottom