Scrolling Game Development Kit Forum
SGDK Version 2 => Projects => Topic started by: v6v on 2012-08-07, 05:38:03 PM
-
Alright. This thread concerns my game, and it's basically me throwing ideas around and eventually posting my builds here.
Tigsource is way too crowded and I don't want THAT attention for my game until the halfway to completion point (Which is why my only devlog will be on these forums, mainly because I also feel as if I can get more trustworthy, personal opinions to my work here, not on a website geared towards thousands of developers without a unified toolset, or even platform goal.)
For now, I'd like to start this thread with questions.
Most of my concepts are from the greatest platforming non-linear 2D sidescroller of all time (To not be close minded if you know of anything better, I'd love to try it) Castlevania Symphony of the Night (If you haven't played it, it's a fairly long game, so unless you have the time, don't plan on beating it in an afternoon, but if you don't have enough time, I'd say the closest thing to it was Vincent's amazing Legacy of Kain SGDK2 game, which not only plays like it, but shares graphics with it.) I believe that SotN was a great game, but the concepts that it was developed on (Non-Linearity) but I think it it didn't go far enough. It's a great concept, but it can be taken even further with the addition of RPG elements from popular MMORPG games, such as a tasks/quest system, a clan system, and a system that incorporates vivid NPC interactions. )
Alright. So for the game I allow there to be 2 options. Host and Join. Each boasts 2 gameplay modes. Story Mode, and Custom Mode.
Story Mode has advantages:
More Bosses
Longer Storyline
Different abilities and weapons able to be found.
More tasks.
More Map Accessibility
At the current moment the protagonist of Story Mode has voice sound effects, which can be heard when jumping, taking damage, etc.
Story Mode also has Disadvantages
No leveling (Meaning you only gain strength from equipping strong weapons, and defeating bosses. This removes the need of grinding. http://en.wikipedia.org/wiki/Grinding_(video_gaming))
Inability to posses clothing items
Certain abilities aren't obtainable.
Static character- meaning that the clothes and appearance of the character is constant.
No replay value (I'm planning on making the file "lock" itself so that the player can't continue to play after the game has completed itself)
Custom Mode has advantages:
Leveling System: Basic RPG EXP leveling system.
Creating your own character to your tastes.
Specialized skills.
Ability to wear clothing and armor items.
Replay Value (Ability to continue gameplay after the game has finished)
Custom Mode has disadvantages:
Less tasks
Less Map Accessibility.
Certain abilities aren't available.
Silent Character: No sound effects for your character. You posses a silent character.
There's reasoning for all of this. I wanted Story Mode to focus on the story- NOT RPG elements such as defeating hundreds of the same enemies to become stronger. This is contrary to any metroidvania- but the best way to describe it would be Richter Mode of Castlevania Symphony of the Night. You gain strength boosts from defeating bosses, or equipping stronger weapons, but not minor enemies.
I don't have the time to record sound effects for all possible custom character creations, as I'm still wondering how I'll do effects just for the storyline.
Some portions of the game and items drive the main story along, which is why I only allow them to be accessible in story mode.
Another idea I'm thinking about is for quests in the game that require you to reach a certain location.
I want to add one of the "GO GO" arrows that points in the direction that you have to travel to complete a job. It's retro, but I figure that it should work out nice in a metroidvania. I want them to be AS ANNOYING as POSSIBLE. GoGo arrows have always been a favorite of mine in beatem ups. I hope they become just as useful in a metroidvania.
http://www.giantbomb.com/go-arrow/92-3391/
Yet another question I had.
I'm positioning the camera so that the player sees the majority of what's ahead of them. Similar to real life, (Where you only see what's ahead of you) I figure that this might add to the suspense of certain areas (I have it now so there's an area where you enter and hear a loud sound behind you)
I want to add a feature that lets the player see behind them as they run, (For example, pressing left while running right would allow them to run in the right direction but the camera should slow down to show what was behind them.) I think I could just record what input was being pressed first, and then go from there.
This wouldn't be annoying, would it?
Does this seem logical? Are there any other ideas you see for each?
(By the way I composed 2 tracks, I'm learning how to compose tracks on the side and I'm progressing away from MIDI tracks, they're pretty decent quality OGG files.)
EDIT
Need to add- say, the AudioVideo plugin that played videos in the DirectX SGDK2, would that work in this version? I was thinking of doing something like AVI to OpenGL Texture, but it's terribly challenging to do (Remember I'm not great with OpenGL :D and I'm sure there's a better way)
-
By the way, thanks for the compliements on my game! :)
I'm wondering about the "Go arrow". Metroidvania are usually open worlds where you have different areas to discover and tasks to accomplish but most of the time you can ignore some or undertake more than one task at once. The "Go arrow" would point toward a single objective I think. Wouldn't it reveal areas too easily? And if the player wants to ignore an objective or wishes to do another objective before the one pointed out by the "Go arrow" wouldn't the arrow be confusing / annoying?
Why remove the ability to customize the appearance of your character in single player mode?
No replay value? Are you sure this is what you mean? Ending the single player game sometime seems fine (after the story is finished), but it doesn't mean it should remove replay value. Especially in metroidvanias when you can speed run, get a higher % of items and map discovered, get a different ending based on your performance, etc. Once the game is completed (or almost) adding replay value by adding extra endings based on performance doesn't cost too much. But, for speed runs and performance evaluation, you have to design your game with multiple paths and optional upgrades, etc.
I wonder, since ou can customize your character appearance, why not allow to set a "voice track" based on a couple of choices. So your character wouldn't always be a mute. It could be as simple as "mute", "male" or "female". If there is not many sound effects, it's not too big to accomplish. By the way, the voice shouldn't be long texts, only sound effects in game like "ouch", "die", "aaaah", "oups"... Things like that.
I'm not sure of the usefulness of looking behind a running character, except for a cut-scene.
-
On the topic of the GoGo arrow, have you seen how Skyrim handles objectives? You can turn each one on or off depending on whether you want the system to be showing you toward it.
Would it improve replay-ability if, after finishing story mode once, you allowed the player to use all the custom mode features in story mode?
-
By the way, thanks for the compliements on my game! :)
I'm wondering about the "Go arrow". Metroidvania are usually open worlds where you have different areas to discover and tasks to accomplish but most of the time you can ignore some or undertake more than one task at once. The "Go arrow" would point toward a single objective I think. Wouldn't it reveal areas too easily? And if the player wants to ignore an objective or wishes to do another objective before the one pointed out by the "Go arrow" wouldn't the arrow be confusing / annoying?
Why remove the ability to customize the appearance of your character in single player mode?
No replay value? Are you sure this is what you mean? Ending the single player game sometime seems fine (after the story is finished), but it doesn't mean it should remove replay value. Especially in metroidvanias when you can speed run, get a higher % of items and map discovered, get a different ending based on your performance, etc. Once the game is completed (or almost) adding replay value by adding extra endings based on performance doesn't cost too much. But, for speed runs and performance evaluation, you have to design your game with multiple paths and optional upgrades, etc.
I wonder, since ou can customize your character appearance, why not allow to set a "voice track" based on a couple of choices. So your character wouldn't always be a mute. It could be as simple as "mute", "male" or "female". If there is not many sound effects, it's not too big to accomplish. By the way, the voice shouldn't be long texts, only sound effects in game like "ouch", "die", "aaaah", "oups"... Things like that.
I'm not sure of the usefulness of looking behind a running character, except for a cut-scene.
I see what you mean. I was thinking that it would add to the replay value if story mode had multiple endings, but once you completed it, you weren't able to continue the game on that file in story mode. It would make the player try again and again to get the better or worse endings (Like ClockTower for the SNES)
In story mode, the character's appearance (A very eccentric character) deals a lot to the story itself, so I really can't allow the player to change who they are entirely. I've remedied this with a custom mode, a mode in which the player is entirely free to change any aspect of themselves.
If I can find someone who can do a spectrum of voices for me, it would be great. the voice tracks would have to include 6 voices each.
It's hard to explain the necessity of looking behind you while running, until the game is released. It's one of those things I'm going to ask after the soon released Beta.
The GoGo arrows only show in Quests/Tasks :D where you're forced to reach a destination to complete the task.
By the way, thanks for the compliements on my game!
No problem, it's the best SGDK2 game.
On the topic of the GoGo arrow, have you seen how Skyrim handles objectives? You can turn each one on or off depending on whether you want the system to be showing you toward it.
Would it improve replay-ability if, after finishing story mode once, you allowed the player to use all the custom mode features in story mode?
It makes sense, I could have three options, a GoGo arrow, a compass, or simply nothing.
Reaping. It's definitely something that I was considering. After completing storymode, the savefile would lock, but it would allow you to start a new custom mode on the beaten file. Said custom mode would allow you to pick up a set number (Half of the total items you have) of items from the story mode game at a certain checkpoint. Maple Story does this- it's where I've taken the concept from. I could also add this feature to custom mode, although custom mode has infinite replay-ability value you can choose to terminate your file and transfer a set number of items from that file to a new game.
These are brilliant ideas you guys are giving me. Thanks. I'm going to have hands full implementing them.
Keep them coming if you have more. :D
-
Ah, so there still isn't a way to play videos in the current version of SGDK2?
-
GOOD NEWS, I got it working. Turns out that once I actually opened the code editor and tried to make it work myself I was able to get it working in less than 20 seconds, but what prevented me from doing this earlier was the fear of not being able to mix OpenGL and DirectX contexts.
BAD NEWS, this prevents cross platform function-ability. To my knowledge this will limit video playback to Windows Only. Sooo...
Now I need to find out how to.... find an OpenGL Video Player that can run under the Mono Project.
Boo
EDIT adding, I had to change the app.config because for some reason or another, the Video Playback library only works in NET2.0.
EDIT I think I could easily solve this by finding some way to load AVI frames as bitmaps..
EDIT Not a skilled enough programmer to do that either. So if I get my project working under Linux, video playback won't be included.. Boo.
Depressing.
-
You're quite resourceful. Sorry I didn't reply to that particular question, but glad you're making progress. Of course if you were making an HTML5 project, video would be a no-brainer ;D. Surely there must be some cross-platform solution short of converting to HTML5.
But I have another question - what exactly do you intend to make videos with (is it something that couldn't be rendered in real-time)? Are you going to film something or pre-render something or what?
-
You're quite resourceful. Sorry I didn't reply to that particular question, but glad you're making progress. Of course if you were making an HTML5 project, video would be a no-brainer ;D. Surely there must be some cross-platform solution short of converting to HTML5.
But I have another question - what exactly do you intend to make videos with (is it something that couldn't be rendered in real-time)? Are you going to film something or pre-render something or what?
I've always taken a side hobby in animation (Even when I just joined this site) so I want to have an animated intro for my game. If it was a recording of gameplay I could use the SpriteRecorder, but it's mainly a pre-rendered animation.
I was recommended by a user on another site to do just that- use "IExplorer controls" to play a video in HTML5.
If only there were some way to open a window of the default browser on the system and play a video in HTML5, fullscreen.
Resourceful? Thanks. :D
(To be honest blame it on the spare time I have. If it were either you or Vincent (or even durund) doing what I was trying to do, you all would have accomplished it with ease. I'm really not a programmer yet.)
You know what would be really nice? Since NeHe is telling me that I need to map an AVI to an OpenGL texture, if I could take an avi or wmv file, convert it to a series of bmp or png files, and go from there.
EDIT
Come to think of it, I'll probably be forced to use ffmpeg to convert an AVI to a series of png files to make this happen. For now I'll stick to the AudioVideo and focus on that later. I don't want to have all of the png files that form the video out in the open, and I certainly don't want to embed them (That's what causes the System.OutOfMemory exception) I wonder if I could just convert the .png files to .bin?
-
Update:
Finally implemented the code to give me OpenGL coordinates, now a parameter of sprite known as OpenGLX and OpenGLY.
(This means that advanced OpenGL functions, such as drawing hair for a sprite, or drawing a string above it's sprite that represents its name is now possible)
Savegames implemented
Game flow implemented - Title flows into settings, file select, character creation and then gameplay. No more having to manually select maps.
I'm in college now, and port forwarding isn't common (I'm not sure if I can do it here)
So I would really like it if someone would eventually volunteer to test Server/Client logic (Now that I'm linking the character creation screen to server/client logic). I had it working before I came to college, so it shouldn't be too buggy.
But if you guys are busy, it's understandable. :(
really
Screenshots:
(http://paradigm.heliohost.org/ScreenShot0.png)
(http://paradigm.heliohost.org/ScreenShot1.png)
Now I have 2 skydomes. One draws the sky. The other, as a translucent overlay, draws clouds. They move opposite to each other.
I can also pinpoint the exact location of a sprite now relative to the layer, so I can draw player usernames above the sprites.
Gentlemen, I have to say, out of my 7 years on this website, this is probably been the one game that I've been the most driven to finish.
(Neuro is actually going to be my next project, I played it last night and enjoyed it's simple mechanics. I'm going to make it HTML5. Hopefully by the time I finish this, browser embedded web technologies without third party plugins will have developed further for me to use more advanced features, such as stable WebGL and HTML5 sockets. And I really like the Skydome effect in my current project- so I'd love to be able to cram that in too.)
-
Maybe my wife and I can help with testing. Oddly, I can't run a Minecraft server on my mobile hotspot, but I have been able to map ports for other games with no problem. What do we need? Is the server-side code .NET or PHP or what? If the server code is something that can run on a Dreamhost server, I have that. Otherwise we'll have to use one of our desktops as a server.
-
Maybe my wife and I can help with testing. Oddly, I can't run a Minecraft server on my mobile hotspot, but I have been able to map ports for other games with no problem. What do we need? Is the server-side code .NET or PHP or what? If the server code is something that can run on a Dreamhost server, I have that. Otherwise we'll have to use one of our desktops as a server.
It is as simple as forwarding the port and starting the game (I haven't decided on an actual port yet, the one I've been using I may change, I want to use a port that won't be an inconvenience and isn't already used.) Thanks for helping!
I'm not using a master server yet :), so for now it will be Join by IP (Simple, right?)
I'm still frantically polishing my lousy programmer art and design flaws for release to this site.
The main thing I need to test is latency and responsiveness, as well as consistency between the players.
(I also forgot to mention. I have 2 people who've joined my team. One does voice work and the other does music, the composer has agreed to compose me an entire OST. He's extremely talented, and most of his songs are Orchestra-esque songs. And the other- I couldn't ask for a better voice actor. Not as important to mention, but it's nice to have my first team :D )
-
How/when can we get started?
-
Very soon. (Finally, because I've procrastinated this for the longest of time now) I've added the function that flips the sprite across the Y axis and converts the animation data to a flipped version by changing the points of origin. (So now the character can face both directions!)
What I'm going to attempt is to have you run the game in Host Mode through a console command, and I'll attempt to join to see if it does the following:
The client successfully registers with a username and password
The client downloads the appropriate sav file from the server and loads from it.
The server and the client both have the same sprites at the same position
The latency is at a minimum for 2 players. If I'm lucky I can eventually get 3 players by having another client join (I'm going to be forced to do this later)
I'm just going to use an existing map that I already had and use that was planned on being a local (non internet) map.
(What I don't have done yet, because I haven't dealt with the fact that players are going to switch maps..)
I'm wondering if the host should save the sprite properties for each sprite to some form of external text file. Then after creating a new file, setting the properties from the file, and sending the client that data.
(The method below is the way I was going to go even before I started the game, surprisingly)
But I think a more efficient way is to create a separate map, have a sprite on that map that has the same properties as the players, and when a player does something significant it updates the sprite on the separate map with the parameters. When a client or server player switches map, it first downloads the appropriate sav file from the server, adds the sprite at the right location on the new map, then copies the appropriate parameters from the sprite on the map though a foreach loop to the newly created sprite on both the client and the server (The client downloads 2 sav files. The save file of the new map, and the save file of the map that stores information, the server just looks at the information map and doesn't download a thing.)
So I'd think that it's safe to say that this is going to need TCP and UDP.
EDIT: I'm debating on which port to use, can you decide on one?
-
I don't have a *whole* lot of experience with networking, but my understanding is that TCP is built on UDP, so UDP is a simpler protocol. But I have never been able to figure out how TCP connections, at the UDP level, maintain bi-directional access through a router without NAT routings if it's true that TCP is built on UDP. I have heard that TCP is relatively inefficient compared to UDP. Not sure if that's enough information to base a decision on.
-
I don't have a *whole* lot of experience with networking, but my understanding is that TCP is built on UDP, so UDP is a simpler protocol. But I have never been able to figure out how TCP connections, at the UDP level, maintain bi-directional access through a router without NAT routings if it's true that TCP is built on UDP. I have heard that TCP is relatively inefficient compared to UDP. Not sure if that's enough information to base a decision on.
The reason I'm straying away from UDP is that I need to send .sav files from computers. UDP isn't reliable (From what I'm hearing) and Kaleta would have solved my problem had it not been built on an older version of Lidgren.Network. I don't want to use plain out of the box Lidgren.Network for this sort of thing (I don't think it was made to send entire files)
MSDN has a good article on downloading files from a location. I don't want to have another thread running for downloading files with TCP though, and I don't want to keep it on this thread (The old Net.cs object halted the game when it was waiting for information)
AFAIK, the Doom engine downloaded .sav files whenever joining a new server. I'm not sure how it did this exactly, but I know that Doom games were portforwarded only though UDP on port 5029. This means that UDP could potentially be used for this, but when playing Doom and using this, I find that it sometimes is buggy (It closes and throws the exception that "Recieved a file not Requested") or it simply freezes after downloading 1K of the file.
If there was a median line in this... I'm going to check Lidgren's documentation to see if I can find something else to help.
-
I just noticed you asked with port, not which protocol. Based on the list at http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers (http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers) 24886 seems to be available, picking kind of at random and googling the results.
True, UDP isn't reliable, but remember that TCP is based on UDP, and you can make an un-reliable protocol reliable with confirmation messages, and then you have control over when you add the overhead of confirmation/reliability. You can add them only in places where you *care* about the message getting to all clients or to the server. And you can skip confirmations in those cases that aren't critical like a status update that can easily be superseded by a subsequent update.
Edit: See also http://stackoverflow.com/questions/133879/how-should-one-go-about-choosing-a-default-tcp-ip-port-for-a-new-service (http://stackoverflow.com/questions/133879/how-should-one-go-about-choosing-a-default-tcp-ip-port-for-a-new-service).
-
I just noticed you asked with port, not which protocol. Based on the list at http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers (http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers) 24886 seems to be available, picking kind of at random and googling the results.
True, UDP isn't reliable, but remember that TCP is based on UDP, and you can make an un-reliable protocol reliable with confirmation messages, and then you have control over when you add the overhead of confirmation/reliability. You can add them only in places where you *care* about the message getting to all clients or to the server. And you can skip confirmations in those cases that aren't critical like a status update that can easily be superseded by a subsequent update.
Edit: See also http://stackoverflow.com/questions/133879/how-should-one-go-about-choosing-a-default-tcp-ip-port-for-a-new-service (http://stackoverflow.com/questions/133879/how-should-one-go-about-choosing-a-default-tcp-ip-port-for-a-new-service).
Thankfully Lidgren supports confirmation messages and I can determine what data needs to be sent reliably and in order, so I won't have to get my hands dirty.. I think I'll use Lidgren.Network, as Michael Lidgren said on google groups, if it's a one time only thing, you can simply just send it as a Message (Lidgren's Unit of Transferred Object Management) Right now I'm not using reliable transmission for input data, but I have a method called every now and again from the server that sends the accurate positional data, that's sent reliably.
Sav files aren't too large either. At least I'm not trying to send 50MB so things should be fine.
Anything to prevent game breaking lag.
-
Get ready for a long post, and if you read the entire thing I will reward you with whatever you want that's game related, everyone
It turns out Lidgren has a special class for just this, that actually downloads a file to the current working application directory. I tested it on my local machine (localhost) with one of the LoK Savefiles that was about 367KB.
1.4MB per second. It took 2 seconds. That might sound like nothing as seconds, but with networking I'm measuring everything in ms and that's around 2000ms.
I'm not pleased with that. (You'll all see why later when multiple players are on a server)
2 entire seconds on localhost for a 367KB file isn't good. I'm not sure if there's a mathematical rule for this, but anything which takes a certain amount of time through localhost will definetely take longer through networking outside of the local machine, especially outside of the local network.
It could even take 5 seconds.. *shudders*
The one thing I've been neglecting to account for is that the host will have to process all rules on multiple maps, which is another reason for me to decide against using more maps. (Of course I won't expect the host to Draw everything from those maps)
I already asked a question about this on the forums and it was answered a while back.
As I've said before multiple times on other threads on the forum, I'm saving each map with it's own save file.
I'm going to be forced to save the map and quickly freeze the game when a new player is downloading a save file, to keep the games consistent. Since Lidgren runs in it's own seperate non-gamebreaking thread, I'm hoping I can just use Thread.Sleep() on SGDK2 (Unless SGDK2 already has a method that freezes game logic on a certain map (Or even a method that processes game logic on a certain map that I can turn off for just one second). That would be even better because I could only use it to halt whatever map's save file was being downloaded and allow the other maps to continue unaffected.) until the files are downloaded from the server to the client. Then I can handle things from there.
Everything below only has relevance if I'm not a complete idiot who didn't realize that SGDK2 has a way to halt game logic for a certain map and leave other maps unaffected
The thing is though, if the server is calling Thread.Sleep to prevent current players from changing anything while a new player is downloading the savefile, I would be forced to halt the entire game, even for players that aren't on that map, because the server would be busy trying to make sure that the other clients "remain quiet, still, and well behaved *Frozen Thread*" while the new player is getting their information.
This is why I fear the 2 seconds that it takes to download the data. That means 2 seconds of stop-frame. 2 seconds of a halted game. Eventually it will aggravate players when new players are constantly switching maps or joining. Doom is famous for this (It freezes the game temporarily for new players to join and download the .Sav file to make sure that everything is consistent. It's even more noticeable if you're not using LAN)
(Of course, I just got the very clever idea of making it harder to switch maps by making longer, more difficult to traverse maps. I could use a value that waits a certain amount of frames before letting the player go to another map. It could also double as temp invulnerability flash that the old games gave you whenever you started a level or got hurt from an enemy. )
I was already planning on keeping my maps small and having multiple maps anyways, so there's no reason for a my game's savefiles to be large unless they're laden with sprites and players. (Well now I have to reconsider the amount of sprites on a map. Maybe I can exclude non-important sprites from the savefile.)
I'm getting ahead of myself.
But against what I said above about the size of the maps I'm changing my mind.
I'd rather have slightly longer download times and slightly longer game halts with fewer actual game halts than more frequent shorter game halts and shorter download times.
Ergo
I'm going to just create lesser, larger maps. Before I kept running into OutOfMemoryExceptions, but I'll try not to make the maps 14MB. anything smaller than 1MB will suffice.
This is also more efficient because the poor host is having to process all of the map's plans and sprites on that map's plans at the same time and then sending that data to the clients. Mapsize won't have an effect on this though because I'll only have the host use drawing operations on the map that the player who's hosting is playing. But this will cause the game to have less maps to be processed. That's a good thing. (Really it only sends save files and occasional reliable (checked to make sure that they were recieved correctly) coordinate updates for players. It relays unchecked input data from clients and sends it to all other clients.)
Tell me, if I extend the size of my maps to about 500 by 500 tiles for 5 layers each, which would bring the resources file to about 1 or 2 MB, would this necessarily cause a sharp increase on the size of a savefile?
-
1. By simply skipping the "CurrentMap.ExecuteRules" step of the main Run function in GameForm.cs you can effectively pause all processing on the current map (you may also want to skip OverlayMap.ExecuteRules). On the server side I assume you will have some custom code that is calling ExecuteRules on all active maps and not drawing anything. Of course there you would simply stop calling ExecuteRules on the paused maps.
2. I think there is a *lot* of room for optimization in what you are transferring when downloading a save. If you have 500x500 tiles and 5 layers, and 2 bytes per tile (that's up to 65536 tiles, which I can't imagine you'd need to exceed) that's 2.5 million bytes.
a. But you probably don't need 2 bytes per tile on all 5 layers. Aren't there some layers that have a limited variety of tiles? You could drop those to 1 byte per tile.
b. And you may not need each of those layers to fill the entire map. A background layer, that scrolls more slowly, for example, could be 100x40 tiles if it scrolls more slowly and mainly only horizontally.
c. If some layers only need to have data for part of the map you can use the offset properties of the layer to set where it is and it won't have to store tile data for the whole area.
d. If the layer is repetitive, you can make the virtual size 500x500, which determines how large the layer appears, and the actual size much smaller. This will cause the data to loop. For example if I have a background of just blue sky, my actual size will be 1x1 tile (1 byte) and the virtual size can be as big as I want. Sprites don't loop like the tile data does, so you could even put some sprites on this layer to make it vary if you only have a few features to put on a large layer.
e. Layer data may be highly compressible in some instances. It might be worthwhile running some simple compression algorithms on it before transmitting. I think .NET has some built-in compression streams.
f. It's highly likely that some of your layers are not changing that much. Aren't there some layers that don't change? You wouldn't need to send those at all since all clients would have the data for those already, knowing that they never change.
g. The parts of the layers that change are much smaller than the whole layers. Maybe you could write your own compression algorithm (or I could help) that only collects the tiles that are different than the initial state of the layers and sends just those tiles. Sending that information would be much smaller than sending the whole layer.
h. You might not need to send the whole layer in one frame. Maybe you could sync just the visible part of each layer. Then proceed to download the rest in subsequent frames, always making sure that at least the visible part of the layers are synced first.
I suspect you could eliminate 95%+ of what you need to transfer by following those optimizations. In fact if you made those last two optimizations, you could probably let the server handle all the rule processing and have the clients just download and draw the results on every frame, thereby eliminating the chance of cheats due to altered clients. Or to make the clients more responsive, you could do what I think I see in other games: allow them to process the rules and draw the results while they wait for the real updated data from the server, which will provide the actual state of the game and replace whatever happened on the client since the last update. But then you'd need to do one of 3 things: 1) Make sure the client's rule processing doesn't alter things that aren't being replaced by the server update and make sure the server update sends everything that the client might have altered; 2) Keep 2 copies of data on the client, one for local operations and one for server updates, and whenever a server update arrives, copy all the data from the server copy (after applying the update) to the client copy; this would effectively "undo" everything the client has done while waiting for the server update; 3) Not care about the things that might get out of sync between the client and the server because all the substantial things that matter are being processed on the server and the client is just drawing what it sends anyway. All client's games are essentially being played on the server and anything out of sync would just be a "drawing glitch", and might even be temporary.
-
1. By simply skipping the "CurrentMap.ExecuteRules" step of the main Run function in GameForm.cs you can effectively pause all processing on the current map (you may also want to skip OverlayMap.ExecuteRules). On the server side I assume you will have some custom code that is calling ExecuteRules on all active maps and not drawing anything. Of course there you would simply stop calling ExecuteRules on the paused maps.
2. I think there is a *lot* of room for optimization in what you are transferring when downloading a save. If you have 500x500 tiles and 5 layers, and 2 bytes per tile (that's up to 65536 tiles, which I can't imagine you'd need to exceed) that's 2.5 million bytes.
a. But you probably don't need 2 bytes per tile on all 5 layers. Aren't there some layers that have a limited variety of tiles? You could drop those to 1 byte per tile.
b. And you may not need each of those layers to fill the entire map. A background layer, that scrolls more slowly, for example, could be 100x40 tiles if it scrolls more slowly and mainly only horizontally.
c. If some layers only need to have data for part of the map you can use the offset properties of the layer to set where it is and it won't have to store tile data for the whole area.
d. If the layer is repetitive, you can make the virtual size 500x500, which determines how large the layer appears, and the actual size much smaller. This will cause the data to loop. For example if I have a background of just blue sky, my actual size will be 1x1 tile (1 byte) and the virtual size can be as big as I want. Sprites don't loop like the tile data does, so you could even put some sprites on this layer to make it vary if you only have a few features to put on a large layer.
e. Layer data may be highly compressible in some instances. It might be worthwhile running some simple compression algorithms on it before transmitting. I think .NET has some built-in compression streams.
f. It's highly likely that some of your layers are not changing that much. Aren't there some layers that don't change? You wouldn't need to send those at all since all clients would have the data for those already, knowing that they never change.
g. The parts of the layers that change are much smaller than the whole layers. Maybe you could write your own compression algorithm (or I could help) that only collects the tiles that are different than the initial state of the layers and sends just those tiles. Sending that information would be much smaller than sending the whole layer.
h. You might not need to send the whole layer in one frame. Maybe you could sync just the visible part of each layer. Then proceed to download the rest in subsequent frames, always making sure that at least the visible part of the layers are synced first.
I suspect you could eliminate 95%+ of what you need to transfer by following those optimizations. In fact if you made those last two optimizations, you could probably let the server handle all the rule processing and have the clients just download and draw the results on every frame, thereby eliminating the chance of cheats due to altered clients. Or to make the clients more responsive, you could do what I think I see in other games: allow them to process the rules and draw the results while they wait for the real updated data from the server, which will provide the actual state of the game and replace whatever happened on the client since the last update. But then you'd need to do one of 3 things: 1) Make sure the client's rule processing doesn't alter things that aren't being replaced by the server update and make sure the server update sends everything that the client might have altered; 2) Keep 2 copies of data on the client, one for local operations and one for server updates, and whenever a server update arrives, copy all the data from the server copy (after applying the update) to the client copy; this would effectively "undo" everything the client has done while waiting for the server update; 3) Not care about the things that might get out of sync between the client and the server because all the substantial things that matter are being processed on the server and the client is just drawing what it sends anyway. All client's games are essentially being played on the server and anything out of sync would just be a "drawing glitch", and might even be temporary.
Come to think of it, I'm not making a minecraft game (For the most part, but hey lets see where this game takes us), so the tiles on my layer are going to be static. Most of the obstacles (Locked Doors, Unpassable Walls) are going to be sprite based. This eliminates the need for me to even send tile data. Since this is true, the only thing that savegames need to store is everything BUT tile data. Thank god I'm not using 2 Bytes per tyle (I found a much better way. I have 3 tilesets. One is the normal tileset, one is the normal tileset flipped tileset across the Y axis, and the other is a tileset that's invisible that handles solidity. The sprite is on the invisible tileset. The other two are drawn in front of it. Each layer uses 1 byte per tile. I figured at 2 bytes per tile, I was using more that I even needed. My math here was that a 256 tile layer with 2 bytes per tile equaled 256 ^ 256 tiles. 3 tiles with 1 byte per tile equal 256*3 bytes. Significantly less than what 2 bytes per tile offered, but just enough for me to work with.)
I'm not sure how the current savegame system works, but I think I can just exclude tile data by changing a line or two somewhere.
There's 3 crucial layers that are important in my game, as stated above. I think what happens is that I just need to send positional data, as you've said, then since the rest of the layer doesn't change, I can just use the Map and Layer Data on the client to render the game and ignore that (Hey maybe that might reduce my sav files to 10KB :D an Ideal size). There's still a couple more values I need to send though.
I've given mapbase and layerbase a few extra properties. These properties are important to the game (They handle weather, and what texture is needing to be drawn to the skydome to represent day and night) I think in my version of SaveFiles, I can allow it to save the properties on MapBase and LayerBase, exclude the tile data for every one of the Layerbases and go from there. So my new version of Saves only have Map and Layer properties including sprite data, without the tile data.
That map I mentioned earlier, the one that only holds sprites that have the User and Password data, I think it will be an initial save only sort of thing. You download it alongside the starting map when you join the game, and after that, since the Host knows that you've logged in, I can just associate that username with an IP, and even if a malicious client attempts to change the value of the username at runtime (Which is nearly impossible thanks to DotPeek but anyways..) the server keeps a memory of the actual username associated with the IP. So when the IP of the client requests to change a map, the server looks at it's own information and not the information of the client before creating a new sprite on that map with the given properties.
Really and truly the server is running the game logic. The Clients are running their own game logic so things don't seem choppy while the server is updating all clients. But this game logic is for them and them only. Even if they change something on their end, ultimately, it's the server that handles things. I have a method that updates everything with the right coordinates and parameters every 10 seconds (Sounds reasonable, no?)
I'm thinking that NPC's and enemies should use a different system (I'm getting ahead of myself again but this needs to be planned for)
For NPC's I wanted to have a property called Action, which is an integer that usually ranges from 1-5 or 1-10. The server calls GetRandomNumber and stores it to that instance of the NPC's Action Integer. When the Integer is recieved, the NPC executes an action (Move Left) (Move Right) (Attack Left) and as soon as the server gets it, it's all a matter of sending the value to the client. Since this is a one integer value that updates every 3-7 seconds, I'm hoping that this won't be too halting.
So apart from client side things such as the processing of random numbers to predict the position of raindrops and snowflakes, I'm going to have to generate all random numbers in the server and send it to the client.
Again, I just repeated what you said ;)
I was thinking of a more efficient way though. I could have a RandomNumberTable (We used these in Statistics class in High School, it's essentially just a 2D array with randomly generated numbers) which was one giant array of integers randomly generated. I was thinking of having 10 embedded spreadsheets or comma seperated databases of these, as a start. When the client joins the server, it downloads the value that tells it which spreadsheet the server is currently (The client and server both have the same spreadsheets locally embedded) using of random numbers and the server sends the client the positional data of the spreadsheet/array (AKA array- where x is the value being received) Then random number generation is done on the server by just moving to the next value, and done on the client by just moving to the next value, separately from each other, so the client isn't waiting for the server's random number, and the method that I mentioned that executes every 10 seconds performs the checks required to see if the client has everything the server has.
I might have to call a method that kicks or bans if the spreadsheets aren't alike. But with the DotPeek obfuscator (I'm not putting all of my trust in it) this prevents anyone from decompiling and changing the data of the spreadsheet before recompiling.
Maybe I can even check to see if something in the server doesn't match the client, and kick or ban automatically. If the usernames no longer match, that's a good reason to ban, right? The one thing I don't want to end up doing is what DOOM does, it kicks players that are inconsistent with the netgame through it's Consistency Kicks. Consistency can be fixed without kicks from what I'm seeing.
I think I just repeated the last part of your message right there. It's only logical NOT to trust the client.
I noticed that you said you could help with compression, it should be even easier now that I'm excluding tile data from my save files (One I figure our how to do this). That would be wonderful. The only thing though is I'm afraid how long the decompressing process will take in the game.
Which method do you prefer of random number generation?
Come to think of it, if tile data isn't being saved, no longer will I be forced to optimize (Or even care about the filesize of) my maps. The only real effect it will have is on the size of the Executable (I created a map with one layer of 5000 by 5000 tiles with one byte per tile on the SGDK2 sample project. The map resources file was 14.2MB. It's amazing what effect maps can have on the filesize)
I suspect you could eliminate 95%+ of what you need to transfer by following those optimizations. In fact if you made those last two optimizations, you could probably let the server handle all the rule processing and have the clients just download and draw the results on every frame, thereby eliminating the chance of cheats due to altered clients.
Cloud gaming? In that case the clients could just render a video file of what was going on on the server. But it would force the clients to wait for the server.... Ack.
:o plus I'd have to completely rewrite my current networking architecture
EDIT: Depending on what response I get to this message, I'm going to try and implement this in the duration of the next 4 days. Professors are bombarding me with tests this week.
-
1. For random numbers, you don't need to use a table. Computerized random number generators can use a "seed". If both the server and client use the same random seed, then they will follow the same series of random numbers:http://en.wikipedia.org/wiki/Random_seed (http://en.wikipedia.org/wiki/Random_seed).
2. I think if you have managed to eliminate the need to communicate all the tile data, you probably won't need my help on compression.
3. I'm surprised the resources file was only 14 MB with a 5000x5000 tile map. 5000x5000 = 25,000,000. You'd think you'd need to store 25 million bytes at least. I don't find it surprising, but I do agree that it's amazing or remarkable. I don't know if you realize how big 5000x5000 is. If you worked 24 hours a day and filled in an average of 1 tile per second, it would take you 9 months to fill that size map. If you worked 8 hours a day, it'd take you more than 2 years. And if you were playing the game and could explore 1000 tiles per second, it would take you 7 hours just to look at all the tiles. So you can see that 5000x5000 tiles is probably big enough for a whole game, not just one map (In RPG games I'm sure I look at far less than 1000 tiles per second). 2-dimensional expansion is a fast-growing "function". That's why I like scrolling games. You can have such a huge detailed world in such a simple space.
4. Of course if you were to "render a video from the server" I wouldn't transfer it as traditional video, which would be terribly inefficient. I would just transfer it as a set of visible tiles/frames, which would be much smaller. But I don't think you need to do that now that you have found a way to eliminate so much data from your communication needs.
-
When I was starting to use SGDK2 I honestly had no idea that 5000 by 5000 was that large. I actually disregarded filesize and made maps with so much extra space that I didn't even need.
I think this is the first game where I've calmed a bit towards using high values.
I haven't even touched the code yet. So I'm actually relieved that you haven't said that creating saves that excluded tile data isn't impossible.
1. For random numbers, you don't need to use a table. Computerized random number generators can use a "seed". If both the server and client use the same random seed, then they will follow the same series of random numbers:http://en.wikipedia.org/wiki/Random_seed (http://en.wikipedia.org/wiki/Random_seed).
2. I think if you have managed to eliminate the need to communicate all the tile data, you probably won't need my help on compression.
3. I'm surprised the resources file was only 14 MB with a 5000x5000 tile map. 5000x5000 = 25,000,000. You'd think you'd need to store 25 million bytes at least. I don't find it surprising, but I do agree that it's amazing or remarkable. I don't know if you realize how big 5000x5000 is. If you worked 24 hours a day and filled in an average of 1 tile per second, it would take you 9 months to fill that size map. If you worked 8 hours a day, it'd take you more than 2 years. And if you were playing the game and could explore 1000 tiles per second, it would take you 7 hours just to look at all the tiles. So you can see that 5000x5000 tiles is probably big enough for a whole game, not just one map (In RPG games I'm sure I look at far less than 1000 tiles per second). 2-dimensional expansion is a fast-growing "function". That's why I like scrolling games. You can have such a huge detailed world in such a simple space.
4. Of course if you were to "render a video from the server" I wouldn't transfer it as traditional video, which would be terribly inefficient. I would just transfer it as a set of visible tiles/frames, which would be much smaller. But I don't think you need to do that now that you have found a way to eliminate so much data from your communication needs.
Using seeds is nice, but lets say that the server is using a seed, and has been using said seed for almost 10 minutes now, and a client joins and downloads the seed and begins using it. Although the client and server share the same seed for *Random(seed);* , the server would be at a different place in the series than a client beginning to generate numbers in the same series, right?
This is what I was afraid of when using seeds. Even if the numbers are in the same series, there isn't a way to tell the client where in the series to start generating numbers from when it joins. (Wait... is there?)
-
Judging from this thread,
http://gamedev.enigmadream.com/index.php?topic=1564.0
I'll need to call GetMap() on every map to load every(Networking) map into memory (Not all of my maps are networking maps. Some are local maps. (Save Rooms, Shops) for the server and then call ExecuteRules on all loaded Maps (While exempting the Host from Drawing Operations and other Operations such as FmodBase, etc). What concerns me is, is it inefficient to load every map into the memory while having to load and keep up with the TileData (for lack of a better term) for every loaded map? I don't need to update and check for updates to Tile data but I'll need to Execute Tile Based functions called by Sprites and Plans (TouchTiles, If isOnTile, etc)
What I'm really asking is I'm going to need tile data, but is there any way to just load Tile Data once, and keep it loaded without checking for Updated or Changed Tiles each iteration?
I was wondering if I could exclude the part of MapBase.ExecuteRules that checks for updated tile data each frame (Or even updates it). I want to optimize it to only focus on Sprites and Plans as well as Counters.
-
You can make reliable connections on UDP the same way TCP does, using ACKs and packet numbers to ensure full packet delivery in the correct order. The difference is that you can make it more efficient than TCP because TCP must ipso facto be generalized for any purpose and therefor is inefficient when you know more specifically what you're sending.
There are plenty of things you can do with this in mind. For example:
If you can get away with it, only sending deltas can additionally speed up data transfers considerably. By that I mean determine only what changed from the previous transmission to this one and send only that data. For example, the player moves horizontally X pixels. This is all the remote client needs to know. Other data, such as Y position, are not necessary
You can keep track of remote data loss and account for that by sending bigger deltas instead of re-sending dropped packets. For example, the player moves X pixels horizontally. It then moves Y pixels vertically for the next transmission, and finally Z pixels vertically for the next. The naive approach sends each packet (X,Y,Z) sequentially. If any packet is not acknowledged, it, and each packet after, are re-sent. Rather than simply sending the three packets with deltas X, Y, and Z, hoping each arrives, you could send each packet as (X), (X,Y), (X,Y+Z), accumulating deltas until you receive acknowledgement of receipt of any of those packets, then reset the delta from there. For example, if you receive acknowledgement of receipt of the packet containing the data X, then your next packet only needs to send (Y+Z) instead of (X,Y+Z). With this approach, you definitely need to keep track of packet numbers. That would be how you specify what the delta refers to.
In our previous example, packet 1 would send X, referring to a base NULL packet. Without receiving acknowledgement of receipt of packet 1, packet 2 would send (X,Y), referring to the same base NULL packet. Then you receive acknowledgement of packet 1. Then packet 3 would send (Y+Z), referring to a base packet 1 instead of NULL.
You can use interpolation on the remote client. For example, we know the player's previous velocity. Assume they keep going on that trajectory until we are told otherwise. Then update with the actual position based on the data received. You would also need to make sure your interpolation doesn't get skewed by a quick change of position due to an updated actual position.
-
I haven't even touched the code yet. So I'm actually relieved that you haven't said that creating saves that excluded tile data isn't impossible.
It's not directly possible, but if you look at the save game function, you will see it's relatively simple. It just tells the other objects to convert themselves into savable content (by "serializing" then). So you can serialize whatever objects you want into bytes and save them or send them directly over the network or whatever. Should be relatively easy.
Using seeds is nice, but lets say that the server is using a seed, and has been using said seed for almost 10 minutes now, and a client joins and downloads the seed and begins using it. Although the client and server share the same seed for *Random(seed);* , the server would be at a different place in the series than a client beginning to generate numbers in the same series, right?
This is what I was afraid of when using seeds. Even if the numbers are in the same series, there isn't a way to tell the client where in the series to start generating numbers from when it joins. (Wait... is there?)
Hmm... Interesting thought. *Experimenting...* Okay, if you take an existing random number generator and create another using the next random number as a seed, that doesn't synchronize the random number generators. Peeking into the internals of the random number generator, it appears it actually has 50 integers worth of seeds, which would all have to be synchronized. I think this helps to ensure that if you get the same integer generated twice, you don't end up in an endless loop going through the same series of integers. (If the current Integer result represented the whole seed then every time you arrived at that number, you'd be able to predict the same sequence coming up again. Not very random.). But I just verified there *is* a way to synchronize random number generators by serializing them and de-serializing them. I can show you the code if you like. The serialized random number generator is only 512 bytes, so that's all you'd need to transmit to synchronize the client's generator with the server's. Of course an easier option might be to re-seed all the random number generators, *including* the one on the server with a new value when someone joins. This would also synchronize them. You could just use the next random number from the server as the new seed for all the generators.
Ah, I might as well put the other solution's code in here so I don't lose it:
Random generator = new Random(1234567);
for (int i = 0; i < 10; i++)
Console.WriteLine(generator.Next());
Random generator2;
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream ms = new System.IO.MemoryStream();
bf.Serialize(ms, generator);
byte[] buf = ms.GetBuffer();
Console.WriteLine("The serialized random number generator is {0} bytes.", buf.Length);
ms.Dispose();
ms = new System.IO.MemoryStream(buf);
generator2 = (Random)bf.Deserialize(ms);
for (int i = 0; i < 10; i++)
{
Console.WriteLine(generator.Next());
Console.WriteLine(generator2.Next());
}
Judging from this thread,
http://gamedev.enigmadream.com/index.php?topic=1564.0
I'll need to call GetMap() on every map to load every(Networking) map into memory (Not all of my maps are networking maps. Some are local maps. (Save Rooms, Shops) for the server and then call ExecuteRules on all loaded Maps (While exempting the Host from Drawing Operations and other Operations such as FmodBase, etc). What concerns me is, is it inefficient to load every map into the memory while having to load and keep up with the TileData (for lack of a better term) for every loaded map? I don't need to update and check for updates to Tile data but I'll need to Execute Tile Based functions called by Sprites and Plans (TouchTiles, If isOnTile, etc)
What I'm really asking is I'm going to need tile data, but is there any way to just load Tile Data once, and keep it loaded without checking for Updated or Changed Tiles each iteration?
I was wondering if I could exclude the part of MapBase.ExecuteRules that checks for updated tile data each frame (Or even updates it). I want to optimize it to only focus on Sprites and Plans as well as Counters.
I'm not quite clear on what you're referring to about checking for updated tile data. SGDK2 code doesn't check for updated tile data during ExecuteRules, and you said your maps don't change, so why would you be adding code to check for updated tile data? SGDK2 framework only looks at tile data when it needs to draw the tile or when a sprite is interacting with the tile... or when it's loading or saving the tile to a file. That's all I can think of.
-
If you can get away with it, only sending deltas can additionally speed up data transfers considerably. By that I mean determine only what changed from the previous transmission to this one and send only that data. For example, the player moves horizontally X pixels. This is all the remote client needs to know. Other data, such as Y position, are not necessary
I suppose you're just using an example here, but it seems to me that it would be a lot simpler to just send all the properties for each sprite that has moved since the last acknowledged packet (I hope data for each sprite isn't *that* big). Then you don't have to track deltas at such a meticulous level or message numbers, you just have to reset a "dirty" flag for each sprite when an acknowledgement is received, right? Furthermore if off-screen sprites aren't having significant effects on the game, maybe you only worry about those that are visible. Or following from the previous idea, maybe you make sure that off-screen sprites don't move/change as often so they don't get "dirtied".
-
My example was contrived, yes. Your expansion on the idea is a better example: Only send data for sprites that moved; in the case of simple x,y coordinates, it's okay to send the complete state every time it changes. But if you're relying on specific animation sequences or hit boxes, or more data than just x,y coordinates, you will want to send deltas only, accumulating the data you need to send until packets are acknowledged. If the lag is considerable, it may make sense to eventually just refresh everything, fetching the world state from the server (or "authoritative client") and resetting everything from there.
-
I'm not expecting servers to be large anyways, so I'll only send inputs for sprites that share a map. (I'm starting with a 32 player max per server, and I don't think 32 people will share a map at any given time)
Since ExecuteRules doesn't do anything with tiles as you've said, I guess I don't have much to worry about.
EDIT: Durund posted as soon as I did :-/
@Durund I'm sending input data only, I'm most definetely not sending x and y coordinates (Could you imagine? :-/ Not only would things be choppy, but someone could EASILY manipulate the data..)
There's a method that the server calls every few seconds that sends the updated map data (The actual coordinates and properties of all sprites on the layer)
The inputs sent regulate animation sequences and whatnot.
EDIT: Whoa, Durund posted another message earlier that I didn't read. *Reads*
You can use interpolation on the remote client. For example, we know the player's previous velocity. Assume they keep going on that trajectory until we are told otherwise. Then update with the actual position based on the data received. You would also need to make sure your interpolation doesn't get skewed by a quick change of position due to an updated actual position.
Well, thank God that I don't have to worry about the bare technical logic behind this. Lidgren.Network shields me from that. I have many modes of how I send data and input data is sent unreliably. However, the actual updates, as well as the download of the map is reliable and is checked to make sure that each packet arrived to it's destination. But I will have to worry about that last thing you said- but it shouldn't be much of an issue at all (The distances between actual positions and client believed positions shouldn't be game breaking *But they will be slight*)
If push comes to shove I'll be forced to look into interpolation..
That's what the testing is for, right? I can't wait until I start!
EDIT: After reading through your comment bluemonkmn about random numbers, I'm liking the final option the most. Reseeding would be the most efficient way to go about things. Whenever someone joins, everyone gets a new seed and that's transmitted. You agree, right?
By the way, Serialization is something I should read into. I didn't know it was so easy to send things across a network. For the longest of time I just hoped that Kaleta.Network would recieve an update that would make it compatible with Lidgren Generation 3. That would have simplified nearly everything.
Edit: (Again) Everything below isn't important at all. (Why am I posting it?) But everything below is related to the game but off topic to the more important discussion
Off topic, but I haven't posted any screenshots here recently so I'll post some showing the registration interface. I'd like to show many of the bizarre combinations that exist so far of the human physiognomy ;P
(This part is actually done ^^)
(http://kaiyro.net46.net/Registration1.png)
(http://kaiyro.net46.net/Registration2.png)
(http://kaiyro.net46.net/Registration3.png)
(http://kaiyro.net46.net/Registration4.png)
At the current moment, hair and skin color are selectable though modifying the RGB value directly, similar to Terraria. Should I keep this system? Or should I limit hair and skin colors to a selection based selection so that only natural hair and skin colors are selectable?
I've been doing some graphical overhauls as well. I'm trying to achieve the graphical style Daft Punk uses in their Anime themed music videos while preserving graphical quality noticed on the Sega Genesis/Megadrive. Trying and failing at least. The composer who's helping me with my game is an orchestral composer... perhaps I can just ask him to use the MegaDrive soundfont for the music he's composing for my OST ( :-[ omg I actually have someone making me an OST already?!)
To any artists on this forum tired of hearing about network mumbo jumbo and want to give me input in an area where you can help me:
I realized that Daft Punk has blue aliens the EXACT hue of the Aliens in my game. (AND extremely SIMILAR costumes) notice the jeweled headband... This was purely coincidental.
(http://fc06.deviantart.net/fs71/f/2012/072/2/2/daft_punk___interstella_5555_by_lliizcrunk-d4sn0gd.jpg)
Hm. I'll have to recolor then. What's a good alien color then...? Magenta? ForestGreen?
If there aren't any better colors and you all truly like blue, I can just tribute this game to one of the greatest genre defying musical groups of France..
The shojo anime style Daft Punk uses focuses on elongated heads and proportionate faces. The blue lady's face in the background doesn't follow this, because that graphic was made before I performed the Daft Punk Graphical Overhaul.
I don't want to let this distract myself from the topic at hand, gameplay and decent internet values. As other indies tell me, focusing on graphics will force you to abandon your project eventually (I can attest to this) so the graphics are purely motivational. I'm going to eventually do a complete overhaul, hire another pixel artist, or create a system that allows the importing of graphic files so users can at least pretty things up. I'm proficient at pixel art at least. This game should serve as a nice fit in my portfolio for the future.
-
I'm not expecting servers to be large anyways, so I'll only send inputs for sprites that share a map. (I'm starting with a 32 player max per server, and I don't think 32 people will share a map at any given time)
...
@Durund I'm sending input data only, I'm most definetely not sending x and y coordinates (Could you imagine? :-/ Not only would things be choppy, but someone could EASILY manipulate the data..)
...
Well, thank God that I don't have to worry about the bare technical logic behind this. Lidgren.Network shields me from that. I have many modes of how I send data and input data is sent unreliably. However, the actual updates, as well as the download of the map is reliable and is checked to make sure that each packet arrived to it's destination. But I will have to worry about that last thing you said- but it shouldn't be much of an issue at all (The distances between actual positions and client believed positions shouldn't be game breaking *But they will be slight*)
I think you and durnurd are a bit out of sync in what you're talking about. I think durnurd is talking about what the server sends to the client and you're talking about what the client sends to the server. Naturally you wouldn't want to be sending sprite positions from the client to the server because that's too easily hacked. So yes, you only send the inputs from client to server. But the server has to process everything and send information back about the results of those inputs. This is where you send sprite positions and such, and try to optimize what you are sending. I don't see why this has to be any more reliable than what the client sent to the server. If the client doesn't get the update, the server can send it in a later update. It's not like the server is going to forget where the sprites are. And this is where allowing un-reliability is important for optimization because the server has so much to send.
I realized that Daft Punk has blue aliens the EXACT hue of the Aliens in my game. (AND extremely SIMILAR costumes) notice the jeweled headband... This was purely coincidental.
...
Hm. I'll have to recolor then. What's a good alien color then...? Magenta? ForestGreen?
If there aren't any better colors and you all truly like blue, I can just tribute this game to one of the greatest genre defying musical groups of France..
Would it be practical to add texture or something? Like make the aliens metallic or irridescent or furry :alien:? Or use a combination of colors like fractals or swirls or ... can you imagine an alien with skin like this?
(http://garmahis.com/wordpress/wp-content/uploads/image/Reviews/textures.jpg)
:surprise:
(I wonder what else they have at http://garmahis.com/reviews/top-10-free-stock-textures-resources-for-designer/ (http://garmahis.com/reviews/top-10-free-stock-textures-resources-for-designer/))
-
Sorry for another long post...
I'm not expecting servers to be large anyways, so I'll only send inputs for sprites that share a map. (I'm starting with a 32 player max per server, and I don't think 32 people will share a map at any given time)
...
@Durund I'm sending input data only, I'm most definetely not sending x and y coordinates (Could you imagine? :-/ Not only would things be choppy, but someone could EASILY manipulate the data..)
...
Well, thank God that I don't have to worry about the bare technical logic behind this. Lidgren.Network shields me from that. I have many modes of how I send data and input data is sent unreliably. However, the actual updates, as well as the download of the map is reliable and is checked to make sure that each packet arrived to it's destination. But I will have to worry about that last thing you said- but it shouldn't be much of an issue at all (The distances between actual positions and client believed positions shouldn't be game breaking *But they will be slight*)
I think you and durnurd are a bit out of sync in what you're talking about. I think durnurd is talking about what the server sends to the client and you're talking about what the client sends to the server. Naturally you wouldn't want to be sending sprite positions from the client to the server because that's too easily hacked. So yes, you only send the inputs from client to server. But the server has to process everything and send information back about the results of those inputs. This is where you send sprite positions and such, and try to optimize what you are sending. I don't see why this has to be any more reliable than what the client sent to the server. If the client doesn't get the update, the server can send it in a later update. It's not like the server is going to forget where the sprites are. And this is where allowing un-reliability is important for optimization because the server has so much to send.
I realized that Daft Punk has blue aliens the EXACT hue of the Aliens in my game. (AND extremely SIMILAR costumes) notice the jeweled headband... This was purely coincidental.
...
Hm. I'll have to recolor then. What's a good alien color then...? Magenta? ForestGreen?
If there aren't any better colors and you all truly like blue, I can just tribute this game to one of the greatest genre defying musical groups of France..
Would it be practical to add texture or something? Like make the aliens metallic or irridescent or furry :alien:? Or use a combination of colors like fractals or swirls or ... can you imagine an alien with skin like this?
(http://garmahis.com/wordpress/wp-content/uploads/image/Reviews/textures.jpg)
:surprise:
(I wonder what else they have at http://garmahis.com/reviews/top-10-free-stock-textures-resources-for-designer/ (http://garmahis.com/reviews/top-10-free-stock-textures-resources-for-designer/))
I see your point. I could have both input and updates sent unreliably, updates are important, but as long as the server ultimately holds the information, it's alright. The problem that I had with that was, what if the update was a huge gap from where the sprite was? (from what the client believed) which is where durund was going.
@durund - I've been sending data sequentially and I'm guessing that's a bad thing.. (I've tested networking with Lidgren once and it worked extremely well, but the person who helped me isn't contactable right now)
The system I use works with deltas. It only sends data when an input is pressed differently from the last frame (Look at me thinking about optimization when I just started working on the game! ;D I guess it was because I was tired of playing laggy games already) It sends a binary value. "0000000000" The first two digits correspond to the player number, say, player 1 is 01, player 2 is 02, etc.
The next 4 digits correspond to input data (Up, Down, Left, Right)
The 4 after that correspond to input button data (Button1, Button2, Button3, Button4)
0 Represents that a button is not pressed
1 Represents that a button IS pressed
For the longest of time I was afraid to change that, because I thought that if I did, it would slow down things to a gamebreaking level. Thankfully to the CS courses I'm taking, I'm learning about different kinds of numbers, more specifically, Base 16.
I'm eventually going to convert input data to Base16 so I can add more input buttons later on. 4 isn't proving to be enough buttons..
But I'm getting off topic
It sends the delta each time a new input is pressed, or if the inputs pressed on the last frame AREN'T pressed on this frame. Sequentially this wouldn't be smart, because if something about another client isn't recieved by your client from the server, your client will think that another client is either standing still, not attacking, or, still pressing a button.
Then this is game breaking because say the remote client was attacking an enemy and had defeated the enemy, but your client didn't get the data telling you about the attack.. Since the clients execute sprite rules by themselves to minimize data, then an update sent telling your client that the client that was attacking is now FINISHED attacking which you did receive (When really the server and that client know that the attack defeated the enemy so the enemy is dead on their ends, but since you didnt recieve an update telling you that it was attacking, on your client, the enemy is still alive) would make your client just see the other player casually strolling off with a false sense of accomplishment, as the enemy was still alive in your game.
I know you both could probably say: "Well then send updates about enemies and NPC's- that way when the client receives the positional update, it'll get information saying that the enemy will be dead, right?"
While that would solve the problem, the game implements the item drop system I mentioned earlier, where a defeated enemy drops an item. If an enemy sprite has 0 HP, it creates an item sprite before Deactivating that can be picked up.
Say another player has already picked up the dropped item. (Item pickups are only recognized by the server. Only the item picked up by the client's sprite on the server is recognized to have the item) Your (Late)client recieves the update telling you that the enemy just died (And an item should be dropped)
But another player has already picked up that item, right?
This creates an entire mind-jar where the client thinks that it has an item, when on the server, and to other clients, it doesn't. I can't send the entire inventory data for each sprite (Their inventory data for each sprite is colossal!! It's one of the largest arrays in the game!)
One solution I have is to just do this:
Item Pickups are only recognized by the server, right? Well I'll keep it that way. Clients don't have the power to pick up items on their own then. The server sends the data to the client about the pickup when a sprite on the server picks up an item, telling it that Player<X> should get Item <ID>. THIS however, HAS to be reliably sent. The server sends all of the clients on that map the data about the pickup, and when it does it also sends the data to the client logged in with the sprite that picked up the item. When the data is received by that client, that data also holds a confirmation ("Coin collected") that tells it that it not only that it should have the item, but it also indicates that the item was successfully received. This way people aren't confused and they don't think they've picked up an item that they don't actually have.
I know this isn't how most MO's would do it, because they have item shops that people spend real money for and cant have players compromising data this way. But it works. I think this is an area that'll take care of itself though. Even if a player attempts to hack by giving themselves an item that they aren't supposed to have by changing the data the server tells them that they're supposed to have, things reset when they change maps or log back in. So I don't have much to worry about. Their game will be bugged and out of sync for attempting to hack, and it won't mess up anyone else. (It's their fault and their problem for trying to hack anyways, right? ;D) So who cares about them. If it's even an extra healing item, their health will be updated by what I'm planning to do. (read below)
I've found something else that I'm also going to have to send Unreliably. HP, MP, and SP. Say by some error or complication, all the sprites have different values for a certain client's HP, MP, and SP. This is bad because it's game breaking. I'm going to have to incorporate it inside of positional updates. I can't send HP to the clients everytime a person gains or loses HP, because that would be terribly inefficient. (Say someone just walks into a pit of spikes? ;))
So now I have to devise another system to send positional updates.
I can't send this as a variable length value, (Ex: 1 instead of 0001, because I know that each map will be 9999 by 9999 pixels of scrollable size.)
Here's my new way, that I'm hoping for:
I'll send this from the server to each client as a positional update.
00000000000000000000
Where each digit isn't binary, but, instead decimal, each 4 digits represents what needs to be updated. The first 4 are X the second 4 are Y, the third 4 is the new HP value, the next is the new MP value, and the next is the SP value. Since I know that HP, MP, SP, and X and Y all have a max value of 9999, I don't have to use variable length values. It ranges from 0000 to 9999. And these are the most important values of the sprite-
think of it as 0000-0000-0000-0000-0000
HP MP and SP are all values that the sprites rely on.
I'm going to try and build everything from this system of reliable and unreliable updates, so I don't have to tear apart my code later to fix things.
I could actually optimize that value above even more actually. I'll figure out how to later.
This is why I was so concerned on making positional updates reliable. (And by positional updates, I mean the one that occurs every 10 seconds) But if I use the system I'm using now- it should work with unreliable transmission.
as for using a texture instead of a solid color..
(http://kaiyro.net46.net/PulioTitle.png)
T'was the old title screen to my game. (I didn't like it for being an still image, so I replaced it with something more attention grabbing) I used carpet, a purple metallic bowling ball, and aluminum foil for the texture.
Strangely genius using a texture instead of a solid color for aliens. I love the idea. Metal would be a nice fit too. Maybe even vast, endless stars.
I thing I'll go with a combination of metal and stars for the texture.
-
(In response to my previous post)
From the looks of things, it seems the only way to actually only include sprite properties in saved map data without saving actual tile data to the file itself is to modify mapbase, or to just create a new system for saving data.
Very difficult agh, I might be forced to rewrite MapBase.cs...
-
I see your point. I could have both input and updates sent unreliably, updates are important, but as long as the server ultimately holds the information, it's alright. The problem that I had with that was, what if the update was a huge gap from where the sprite was? (from what the client believed) which is where durund was going.
Happens all the time. Sometimes my character dies while waiting for the server to respond in Diablo III. Sometimes the other player playing with me ends up in a very different place on the map.
The system I use works with deltas. It only sends data when an input is pressed differently from the last frame (Look at me thinking about optimization when I just started working on the game! ;D I guess it was because I was tired of playing laggy games already) It sends a binary value. "0000000000" The first two digits correspond to the player number, say, player 1 is 01, player 2 is 02, etc.
The next 4 digits correspond to input data (Up, Down, Left, Right)
The 4 after that correspond to input button data (Button1, Button2, Button3, Button4)
0 Represents that a button is not pressed
1 Represents that a button IS pressed
Why not just sent Sprite.inputs, which includes all that information (except the player number) in a single 4-byte integer?
Then this is game breaking because say the remote client was attacking an enemy and had defeated the enemy, but your client didn't get the data telling you about the attack.. Since the clients execute sprite rules by themselves to minimize data, then an update sent telling your client that the client that was attacking is now FINISHED attacking which you did receive (When really the server and that client know that the attack defeated the enemy so the enemy is dead on their ends, but since you didnt recieve an update telling you that it was attacking, on your client, the enemy is still alive) would make your client just see the other player casually strolling off with a false sense of accomplishment, as the enemy was still alive in your game.
I've seen that happen too. I'll be playing merrily along in Diablo III and my wife is desperately trying to survive a huge monster on her screen in the same place. (Maybe Diablo III isn't the best model. :) ) But the server has the authoritative answer and decides whether that monster really exists and whether it has really killed one of us.
This creates an entire mind-jar where the client thinks that it has an item, when on the server, and to other clients, it doesn't. I can't send the entire inventory data for each sprite (Their inventory data for each sprite is colossal!! It's one of the largest arrays in the game!)
I guess that's why most games don't allow clilents to pick up items nor affect their own inventory at all. You can waltz over that item all you like, but nothing's going to happen until the server knows what your doing and send you your inventory update (just one item, not the whole inventory if that's suitable).
Item Pickups are only recognized by the server, right? Well I'll keep it that way. Clients don't have the power to pick up items on their own then. The server sends the data to the client about the pickup when a sprite on the server picks up an item, telling it that Player<X> should get Item <ID>. THIS however, HAS to be reliably sent. The server sends all of the clients on that map the data about the pickup, and when it does it also sends the data to the client logged in with the sprite that picked up the item. When the data is received by that client, that data also holds a confirmation ("Coin collected") that tells it that it not only that it should have the item, but it also indicates that the item was successfully received. This way people aren't confused and they don't think they've picked up an item that they don't actually have.
Yes, unless you're regularly updating all of inventory, that's one update you'd want to make reliable/acknowledged.
I know this isn't how most MO's would do it, because they have item shops that people spend real money for and cant have players compromising data this way. But it works. I think this is an area that'll take care of itself though. Even if a player attempts to hack by giving themselves an item that they aren't supposed to have by changing the data the server tells them that they're supposed to have, things reset when they change maps or log back in. So I don't have much to worry about. Their game will be bugged and out of sync for attempting to hack, and it won't mess up anyone else. (It's their fault and their problem for trying to hack anyways, right? ;D) So who cares about them. If it's even an extra healing item, their health will be updated by what I'm planning to do. (read below)
Hacking simply wouldn't work because say, for example, they give themselves a suit of heavenly armor that prevents all damage. Well, when the enemy hits them on the server, it's going to assign damage, and there's nothing the client can do about it. Or say they give themselves a key to open a door. The client might open that door, but the server isn't going to, and all their position updates will leave them standing at the door unable to get through. (And if the server is the one responsible for opening all doors, the door won't even open on the client.)
I'll send this from the server to each client as a positional update.
00000000000000000000
Where each digit isn't binary, but, instead decimal, each 4 digits represents what needs to be updated. The first 4 are X the second 4 are Y, the third 4 is the new HP value, the next is the new MP value, and the next is the SP value. Since I know that HP, MP, SP, and X and Y all have a max value of 9999, I don't have to use variable length values. It ranges from 0000 to 9999. And these are the most important values of the sprite-
Why not just serialize the sprite objects (including "inputs")? Binary serialization is much more efficient than decimal serialization. That's what the save/load game function does. The sprites have all this info. Do your sprites not have HP/MP/SP on them? If not, serialize whatever structure it is that contains that info.
From the looks of things, it seems the only way to actually only include sprite properties in saved map data without saving actual tile data to the file itself is to modify mapbase, or to just create a new system for saving data.
Very difficult agh, I might be forced to rewrite MapBase.cs...
You saw how easy serialization was. Why not just write a simple function that returns all the sprites of a map in serialized form?
-
Why not just serialize the sprite objects (including "inputs")? Binary serialization is much more efficient than decimal serialization. That's what the save/load game function does. The sprites have all this info. Do your sprites not have HP/MP/SP on them? If not, serialize whatever structure it is that contains that info.
At the moment I'm just sending the properties that I need. By only sending the input state, it's only 0000000000.
If I'm sending all serializable data in SpriteBase, I'm sending more data than I need to.
Normally there's only two things I need to send:
Input Data (I'm going to look into Sprite.inputs)
I don't want to send anything BUT Sprite.inputs for each frame, I'm looking on MSDN about Serialization, is there a way for me to Serialize ONLY THAT field on SpriteBase? It seems that on MSDN to exempt fields from serialization, I have to use attributes set on my class. However, there's times I'm going to want to serialize certain fields and other times where I'm going to need to serialize other fields.
I'd love to send only a serialized version of the input data for SpriteBase, but I'm unsure if my method results in much less to send, or if serialization results in much less to send..
Here's another thing about serialization I'm wondering about..
I know serialized objects are stored as bytes, but is the output format bytes or a byte array? Lidgren can easily send byte arrays. But here's my other question-
Is this serialized data extremely large? Or reasonable to send to all clients every 10 seconds? If I'm sending all clients a serialized version of a SpriteBase object for positional updates, and there's about 4 objects on the map, would it be too much to handle?
X/Y/HP/SP/MP/ well, come to think of it- I'm going to need to send everything BUT the array that holds inventory data. So I'm going to be forced to serialize that..
So for positional updates I understand that I'll need to send serialized fields for SpriteBase, but for optimization's sake, will I need to serialize for input data?
Huh..
Edit Ignore my silly question about the byte array format. I've solved half of it I guess. I can send the serialized byte array..
http://www.digitalcoding.com/Code-Snippets/C-Sharp/C-Code-Snippet-Object-to-byte-array.html
but I'm unsure about how to load data from a file that stores the byte array back into the Map to tell it how many sprites are in the Map's SpriteCollection, and then load the sprites' individual parameters and "instances" for lack of a better term, back into SpriteCollection.
I'm going to have to look into LoadGame. If I have any more issues I'll try and post them here
Edit Ignore that one too, Deserialization is extremely simple. (Insanely simple)
Ok, but this all doesn't solve my problem for the SaveFiles. It stores the current map to a hashtable in saveunit, but by storing the map, it's also storing the map's tile data automatically...
I can't be selective about this. I'd probably have to go and find the map that was saved in the SaveUnit, and then find out how MapBase handles it's tile data through individual instances of LayerBase, which I'd have to go into individually and use a method from GeneralRules.MapBaseObject.AllInstancesOfLayerBaseforThisMapBaseInstance to clear their tile data.
Then there's the matter of sending serialized sprite data. I'd have to find some way to tell the client which serialized data belonged to which sprite when the server sends serialized data to the clients, which is another mind-jar within itself.
The one way of doing things is having the server handle XML files alongside the save files for each map. Through each of these XML files I could have tags such as <Players> <Player 01> *Serialized Data </Player01> <Player 02> *Serialized Data </Player 02> etc.
(I never mentioned this, Lidgren.cs and NetController, the two objects that I use for Netgames at the moment, keep an array of players for each map, assigning each of these players's sprites a unique incremental ID from 00 to 99 *It increments every time a new player joins and fills in gaps whenever a player leaves, or that was my recollection of how it worked when I last tested it almost 8 months ago.. :)* I can just use these unique values to determine serialization data. I was already using it for input data.)
Then I would have to send the XML file to the client for each positional update and let the client handle deserialization and then the processing of each field from the data by reading the XML file.
Come to think of it, this could work for inputs as well. But instead, I could serialize SpriteBase.inputs and send that as a byte[SerializedDataByte[]] instead of using the XML file where byte[] holds PlayerNumber at index 0 and serialized data at index 1. It would be much faster than having to wait and process an XML file.
Or I could use the above system and just exclude XML files altogether. What do you think?
(It looks like I'm going to be spending more time on optimization again. Just when I thought I was finished. But then again, isn't most time usually spent on optimization?)
-
OK, I don't know if I can address everything you're bringing up but some of the basics:
1. To serialize Sprite.inputs (a single scalar value) all you need to to is convert it (cast it) to an integer and then call BitConverter.GetBytes() on it.
2. I estimate a sprite by itself could serialize to about 100 bytes. I estimate you could transmit about 1 Mbps, which is 125,000 bytes per second. Given that the average network latency I see is about 104 milliseconds (1 tenth of a second) I estimate you could send about 5 updates per second, which would amount to 25,000 bytes per update, which means you could send about 250 whole serialized sprites per update (five times per second) if that's all you were sending.
3. However, serializing a sprite is not quite as straightforward as I wish I could tell you it is. I'd like to tell you, just take the map's sprite array, call serialize on it, and send it over the wire and you're done. But each sprite has a reference to the layer it's on and some other objects. Because of the way the .NET serializer works, this will cause it to automatically serialize the layer and other objects linked to the sprite as part of the serialization process. And I know you don't want to include all the tile data from the layer. The next best thing to to create a structure like SaveUnit. SaveUnit is really quite a simplifying structure -- it basically exists solely to bundle together for the serializer all the objects that we want to serialize. So you can just make up any object/class you want (your own version of SaveUnit... maybe call it NetUnit) that stores all the information you want in a friendly format. Put arrays of integers and floating point numbers and hashtables in there. So long as everything you put in there is serializable (which all scalar and array types are) the binary serializer will serialize it very efficiently. So you could just make a hashtable where the key of each element is a sprite name or identifier of some sort and the value is an array of numbers that represent copies of that sprite's important properties. Then you just serialize it, transmit it, decode it and copy those values into the client's copy of the sprites. I wish I could go on, but I don't have the time or energy to write a book right here in this post :). I hope this is making sense to you. .NET really can work some miracles for you here if you know how to use it.
-
OK, I don't know if I can address everything you're bringing up but some of the basics:
1. To serialize Sprite.inputs (a single scalar value) all you need to to is convert it (cast it) to an integer and then call BitConverter.GetBytes() on it.
2. I estimate a sprite by itself could serialize to about 100 bytes. I estimate you could transmit about 1 Mbps, which is 125,000 bytes per second. Given that the average network latency I see is about 104 milliseconds (1 tenth of a second) I estimate you could send about 5 updates per second, which would amount to 25,000 bytes per update, which means you could send about 250 whole serialized sprites per update (five times per second) if that's all you were sending.
3. However, serializing a sprite is not quite as straightforward as I wish I could tell you it is. I'd like to tell you, just take the map's sprite array, call serialize on it, and send it over the wire and you're done. But each sprite has a reference to the layer it's on and some other objects. Because of the way the .NET serializer works, this will cause it to automatically serialize the layer and other objects linked to the sprite as part of the serialization process. And I know you don't want to include all the tile data from the layer. The next best thing to to create a structure like SaveUnit. SaveUnit is really quite a simplifying structure -- it basically exists solely to bundle together for the serializer all the objects that we want to serialize. So you can just make up any object/class you want (your own version of SaveUnit... maybe call it NetUnit) that stores all the information you want in a friendly format. Put arrays of integers and floating point numbers and hashtables in there. So long as everything you put in there is serializable (which all scalar and array types are) the binary serializer will serialize it very efficiently. So you could just make a hashtable where the key of each element is a sprite name or identifier of some sort and the value is an array of numbers that represent copies of that sprite's important properties. Then you just serialize it, transmit it, decode it and copy those values into the client's copy of the sprites. I wish I could go on, but I don't have the time or energy to write a book right here in this post :). I hope this is making sense to you. .NET really can work some miracles for you here if you know how to use it.
Right. I see serializaition of inputs to be the more rapid update, sending those each time a new input is pressed. I don't think I'll be sending anything each and every frame, I only send data on state changes (and after thinking heavily: NPC's and enemies have unique timers that they count down on that if it ends, they execute an action such as attacking or walking away and restart). And since sending serialized sprites only occurs every 10 seconds (And with that I'm going to be forced to send the server's current timer values and server's overall IsActive state for each of the enemies and NPC's to sync everything on all of the clients but I can contain both of those values in a single byte per NPC/enemy)
But if I serialize a SaveUnit-ish object containing the sprites rather than serializing each sprite individually, it won't serialize the other "objects" and "attached layers" assosiated with the sprites, right?
Right?
I see a breakthrooooouuuugggghhh :)
EDIT: After re-reading your post I see what you mean now. Fair Enough. I can simply copy these parameters across, rather than saving the actual SpriteBase objects themselves :). Wheeeeeeee :D
I have approximately everything that I need to complete it now. Wish me luck and thank you for the help.
-
Working through this now and (destroying my old Lidgren.cs and NetController.cs classes entirely to re-write things in a much more efficient format)
You said I could send 250 whole serialized sprites per second. (Thank God I'm only sending serialized sprite data as a single one frame update every 10 seconds because I'm definitely planning on having more than 250 players + NPC's and entity sprites combined)
With that being said I guess I'll have to send updates every 10 seconds relative to the clients on a certain map, but not at the same time as I'm sending updates to another map. Since I'm not serializing SpriteBase (at 100 bytes an instance), and my new SaveUnit class won't be too large because it only stores important properties, I can make this work.
I'm going to use this thread to sort of just devlog and publish my silent achievements and thoughts. The following comments wont be important unless they're a question (I don't think I'll have any for a while) so happy reading I guess... :P
TODO in the far future: (Create ProjectileSprite.cs to handle projectiles)
Might as well document this just so I remember
When the sprite is attempting to use an item it has, or equip armor it has, it sends the request to the server. If the sanity check ran on the server says that this is possible, it uses the item or equips the armor on all instances of that player's sprite for all clients including the server. Like other methods dealing with items, the data sent from the clients and server is reliably sent.
I might be forced to stick with ASCII for the chat messages... It's no big issue at all though.
(its kind of inconsiderate to those who don't chat with english, but maybe I can just have the server decide the language of whatever's going on and the clients determine which font to use according to that. and maybe I'll replace characters not used often with grave and acute accents)
-
Do you plan on having 250 sprites on one screen on one server all at once? Because every client doesnt need to know the location of every sprite, just the near by ones.
Also, it's highly unlikely that you will be able to have hundreds or even dozens of real-time, persistent connections into one server at a time. Even large scale MMOs try to keep the load on any one server down, and unless you have your own rack of dedicated servers with a high-density connection to the Internet backbone, the server would probably lag immensely or perhaps just melt into a sad puddle of silicon and wires on the floor.
Moral of the story: 250 or more sprites is fine, but not all on one screen simultaneously.
-
I guess I worded that wrong. I plan on having more than 250 sprites on a server, not all on the screen at once. These sprites aren't going to be users. Some will be user sprites, but others will be other significant sprites such as NPC's enemies, etc. (This is actually one of the things I hated about most 2D ORPG's. They had so many players on the screen that it was impossible to distinguish your own sprite, or even read any chat messages)
I'm starting out with a 32 player connection limit per server. That's the default connection limit for the Skulltag game engine, even Doom. I highly doubt servers are going to reach 32 players before someone attempts to host their own, because even Doom and Skulltag engines begin to lag at around 20 players (Which usually happens in games that allow you to host your own server)
But that's the beauty in allowing users to host their own servers. Instead of having a string of dedicated servers, from the start of developing this game, I knew that this would be impossible for me. I'm hoping that a large group of smaller servers could work much better. Most servers will probably start lagging at around 16-20 players. So having more servers by allowing any player with a port forwarded connection to host and allowing the hosts of those servers to moderate through kicks and bans is probably best. (The only real centralized server I plan on having is a simple MasterServer that shows all other servers that are online)
The clients only receive updates about other clients on the map. If things get worse, I'll look into limiting it to only sprites within certain pixels of other sprites.
Also, it's highly unlikely that you will be able to have hundreds or even dozens of real-time, persistent connections into one server at a time. Even large scale MMOs try to keep the load on any one server down, and unless you have your own rack of dedicated servers with a high-density connection to the Internet backbone, the server would probably lag immensely or perhaps just melt into a sad puddle of silicon and wires on the floor.
Can you go further with this?
EDIT: I'm not quite understanding how the InputBits works. Can I only get it? Or can I also set it? I want to serialize an integer casted InputBits and then set it on the client's sprite by having the client cast it back to InputBits.
Then again I can't just explicitly cast an integer to InputBits. Agh please, don't let this be an impasse...
EDIT: I asked the previous question wrong. How exactly does the enumeration handle multiple key-presses?
I see that inputs (the enumeration) has a value for each keypress, but what if I'm pressing 2 or more inputs at once? Will explicitly integer casted data for that frame hold both keypresses? If I cast it back with (InputBits)castedData; will it hold both keypresses? And can I set inputs on a SpriteBase instance? Or is the input enum only used for retrieval?
EDIT: I see how it works, I think. SetInputState and IsInputPressed demonstrate it. It looks like my extremely and unnecessarily complicated way of doing it before can be scrapped now.
I'm anxious to see how far I can get with this.
Oh and hey look what I found: *cough*
http://gamedev.enigmadream.com/index.php?topic=1568.0
Guess I should ask questions about things that I won't decide to finish later.
-
Some art?
I realize that I'll divide things into multiple game modes.
Adventure mode will prove to be the most taxing on the server because that's the mode that handles multiple maps simultanously.
For most of the other game modes, I'll try and contain things on the same map.
So Adventure Mode will be the last mode I'll work on.
For now I'll start on a Lobby Mode. One map that displays all of the users waiting in a Purgatory-Like Map.
With the exception of Adventure Mode, I've decided to make most online game modes constrain all users to the same map.
Thanks for reading!
-
Well, thanks for the news! :)
-
Thanks Vincent, and I probably won't post anything else news related here unless it's a download. ;)
In the meantime, I'm working on the lobby.
The system I'm using right now assigns new players as Anons until they change their name in the lobby or in Character Creation.
Anons might only be able to spectate and watch other players.
-
Just so people don't think that this is dead, I'll post some pretty motivating updates.
There is no download in this post (yet) I apologize for that.
I'm switching to protobuf as my serializer, I've spoken to some other people who have done online gaming in C# and they recommend this.
I've done a huge resources overhaul. Music and multimedia work well.
If anyone knows of a way for me to switch away from SGDK2's usage of OpenGL in Immediate Mode, please share, I'd love to use vertex buffers, because I have a good amount of geometry that needs to be drawn (I've added an option to turn off fancy graphics) I can't say I know anything about OpenGL past GL.Begin(BeginMode.Triangles)
On my system, this means the difference between 45FPS and 18 FPS.
Here are some motivating screenshots. I need to get things done.
I have scrolling text made with adjustable text speed, and the item system works: since players can only hold a certain number of items at any given time, the game prioritizes itself on searching for areas to hide and store items. Checking and examining everything leads you to some pretty interesting discoveries, as most storage areas may blend in the background and yield some interesting items.
The Status area GUI is pretty simple on the eyes, and is nostalgic to the GBC era (My era of gaming) . I don't want something that will force the player to have to strain themselves too much looking at the detail in something so unimportant.
Finally, the game starts off from the start of my first game: formerly Eureka Star, now called Child of Streams 1. You're instantly faced with an onslaught of alien enemies and a boss as soon as you start the game.
It's still going, I'll never quit this.
-
Very nice! Keep going! :)