Jump to content

  • Log In with Google      Sign In   
  • Create Account

Nypyren

Member Since 19 Aug 2002
Offline Last Active Yesterday, 11:52 PM

#5293502 Do you usually prefix your classes with the letter 'C' or something e...

Posted by Nypyren on Yesterday, 11:51 PM

My keyboard doesn't have a '' key. So I an't prefix my lasses. :(

It's pretty hard to ode in /++ or #.


#5293066 how much PC do you need to build a given game?

Posted by Nypyren on 23 May 2016 - 09:41 AM

(For some reason quoting your last post pulls in a ton of font formatting info, so I'm avoiding that.)

RE: Windows background processing:

Right now, monitoring process use on Windows 10: I have about 70 processes running, and about two dozen of them are using "0.01%" of my CPU at any given time. The rest are using 0%. It's insignificant, at least for me.

The only time I ever notice a "background process" sucking down CPU time is at work where our mandated Kaspersky antivirus software does its periodic scans that bring my work laptop to its knees. But that was true in Windows 7 through Windows 10, and isn't the OS's fault.

Back when CPUs were just getting two cores, I noticed a big improvement when I got my first 2-core PC, but I haven't noticed any difference in background task handling since then.


#5292911 Issue with convertion C++ code to C# for TGA loading

Posted by Nypyren on 22 May 2016 - 01:21 PM

If you tested the C++ code with the image you're using, and it works, then the image isn't relying on cross-scan packets (programs that save the TGA files sometimes intentionally split runs when the end of a scanline is reached). In that case there's probably a bug in your transliteration of the C++ code.

The main gotcha that I have run into when transliterating C++ -> C# is the differences in implicit casting between different sizes of integers that C# uses.

What I would do next is run the C++ and C# code in debuggers and step through them in parallel with the same TGA file until you see where values diverge.


#5292690 how much PC do you need to build a given game?

Posted by Nypyren on 20 May 2016 - 02:35 PM

Cache is part of the CPU and cannot be purchased separately.

If you want to build a PC from parts, this is the process I use:

- Find desired CPU in my price range.
- Find desired GPU in my price range.
- Find a motherboard that is compatible with both (Supported CPU socket type/generation, PCI Express version).
- Find a case which is large enough for everything (Large video cards can sometimes be too large to fit in certain cases).
- Find RAM that is compatible with the motherboard (there are RAM compatibility lists provided by motherboard makers). Buy them in bundles, not separately. Bundles are tested together.
- Find a good quality PSU with enough watts to feed your CPU and GPU.
- (Optional) Get an aftermarket CPU HSF (Heat Sink + Fan) if you want it to be quieter/cooler than the stock HSF. These are specific to the CPU socket type, and you also need to make sure the motherboard layout has room if the HSF is large.
- Find a SSD/HDD with zero customer complaints related to hardware incompatibility and minimal complaints about failures.

My current PC is:
CPU: AMD FX-6300 (midrange)
GPU: Nvidia GTX 980 (high end)
MB: Gigabyte GA-990FXA-UD3 (midrange)
RAM: 32GB (4x8GB) G-Skill Ripjaws X (midrange)
Case: Cooler Master CM690 II
PSU: Corsair 750-watt (This is way more than I need for my PC but I reuse it each time I upgrade so it's somewhat future-proof)
HSF: Zahlman CNPS 9500

Overall this is a "high end" PC for my use cases simply due to the GTX 980's outstanding performance and the fact that most games I play are GPU-bound. This PC would be overkill for game development.

Suggestions:
- Take all benchmarks with a grain of salt; I have had a perfect time with AMD processors even though they look terrible according to benchmarks.
- AMD CPUs and compatible motherboards are MUCH cheaper than Intel's.
- AMD's motherboard chipset drivers are much more reliable than Intel's. Intel tends to be really flaky when it comes to drivers.
- Nvidia is far better at supporting new games with updated drivers than AMD is. However, as a game developer you MUST test on both.
- For game development, buy midrange GPUs of both brands so you can tune your performance and cater to a larger audience.
- Try to get your game to use 4GB or less. The more RAM you use the more likely you will cause your players' unknown amount of RAM to start paging to disk, which kills performance.


#5292341 What is the top factor for MMO engines limiting world size?

Posted by Nypyren on 18 May 2016 - 02:47 PM

If the game world was huge you could travel for hours before encountering another player, and all the players would congregate in cities leaving the rest of the game world a boring empty ghost town.


You've just described Elite: Dangerous! :)


#5290158 Colorimetry: Violet

Posted by Nypyren on 04 May 2016 - 05:09 PM

https://en.wikipedia.org/wiki/Photoreceptor_cell#Humans

The cells receive WIDE ranges of frequencies that all overlap (see the frequency response graph on the right of that section). If they received a narrow range, "visible spectrum" would be constrained to those small slices of wavelengths. Each cone variant is biased towards a particular frequency range, but it can still receive the other frequencies as well.

The 'colors' we see are the ratios between the strengths that the different *overlapping* cone types receive. This is also why the RGB output of monitors APPEAR to reproduce colors representing frequencies that they don't actually emit.


#5290156 How can I share a .exe game?

Posted by Nypyren on 04 May 2016 - 05:05 PM

You could use an obfuscator to make decompiling slightly more of a hassle.

https://en.wikipedia.org/wiki/List_of_obfuscators_for_.NET


#5289821 witcher 3 combat

Posted by Nypyren on 02 May 2016 - 09:08 PM

The Witcher 3 would work in first-person. It would just make you motion-sick; combat is full of side-stepping and dive rolls. There's only one first-person game I have played that has the same amount of dodging: Lichdom: Battlemage. That game makes me motion sick extremely quickly, probably due to the crazy ways the camera moves during fast-paced combat.

Skyrim can be played both first person and third-person. In first-person you miss a lot of the cool-looking attack animations that your character is doing. Melee is kind of dull - you have one block move and two attack moves. Attack responsiveness is terrible (cooldown, no good feel of flow or chaining attacks). Enemies can block you in absurd circumstances which feels terrible (same problem in Fallout 4 - people can block a chainsaw with their bare arms).

I think the only games that I'd say have good combat systems are fighting games like the Soul Calibur series. Everything else is kind of "meh" after a while. I like Skyrim and Witcher for the exploration aspect much more than the combat.


#5289172 I Have a Problem in "char *" & "LPCWSTR"

Posted by Nypyren on 28 April 2016 - 06:47 PM

The W in LPCWSTR stands for "Wide" and usually if you see errors about that it means you're mixing Unicode and Multibyte strings in your program due to incorrect settings or incorrect string literals.

You can either try making everything consistently Unicode (put L prefixes on all of your string literals or the _T macro like you've already tried) or consistently Multibyte (open your project properties and change "Character Set" to "Use Multibyte Character Set".

If that doesn't help then the problem is much deeper and you might need to post more code.

New VS2015 C++ windows projects default to Unicode, I believe. Older tutorials like NeHe probably assume the older multibyte default.


#5289153 Network Library that supporst RPCs

Posted by Nypyren on 28 April 2016 - 04:32 PM

For most types, the sender and receivers both know how big they are based on agreed-upon information. You typically do NOT need to send the size of every field.

For example, you could write code that agrees that all 32-bit integers are 4 bytes. Or you could agree to write them using SQLite4 varint encoding, or VLQ encoding, or whatever.

Let's look at a bare-bones BinaryReader/BinaryWriter pair in C#:
 
public class Foo
{
    public int A;
    public int B;

    public void Read(BinaryReader reader)
    {
        A = reader.ReadInt32();
        B = reader.ReadInt32();
    }

    public void Write(BinaryWriter writer)
    {
        writer.Write(A);
        writer.Write(B);
    }
}
The Read and Write functions don't need to write bytes indicating that A is 4 bytes and B is 4 bytes, because it's implicit.


You only really need lengths for two main things:

1. Knowing how much data to wait for in your network receiver before you attempt to deserialize the whole message.
2. Skipping fields of unknown types if you have a protocol that allows for different client versions to talk to each other. You probably won't need this, though.


For RPC, you could do something like this (very barebones example):
 
public void HandleRPC(BinaryReader reader)
{
    var function = (RPCFunction)reader.ReadInt32(); // RPCFunction being an enum

    switch (function)
    {
        case RPCFunction.ConsoleWriteLine:
        {
            // This is a bit of a security risk if you were to do this in C++, but in C# the worst you'll get is an exception.
            var formatString = reader.ReadString(); // Internally it has the string length first
            var numArgs = reader.ReadInt32();
            object[] args = new object[numArgs];
            for (int i=0; i<numArgs; ++i)
                args[i] = ReadPolymorphicType(reader);
            
            Console.WriteLine(formatString, args);
        }

        // Other functions read their expected parameters, then call the appropriate function in the same way.
    }
}

public object ReadPolymorphicType(BinaryReader reader)
{
    int typeId = reader.ReadInt32();
    switch (typeId)
    {
        case 0: return null;
        case 1: return reader.ReadByte();
        case 2: return reader.ReadInt32();
        case 3: return reader.ReadSingle();
        case 4: return reader.ReadDouble();
        case 5: return reader.ReadString();
        case 6: { var foo = new Foo(); foo.Read(reader); return foo; }
        // etc.
    }
}
This is a manually-written, tedious way to go about it, but it serves as an example for how you can serialize/deserialize data. You would ideally want to use whatever is most convenient for your project.

I would personally use varints for all integers, have string backreference support, etc.


#5289111 Network Library that supporst RPCs

Posted by Nypyren on 28 April 2016 - 11:14 AM

Definitely put the size first. It's so much easier than worrying about delimiters, EOM indicators and dealing with escaping other data somehow.

Really the only nuisance part of doing size first is you have to build up your whole message in a temporary buffer first, then measure its length immediately before sending. But that's not nearly as bad as dealing with escaping data (which inevitably leads to bugs).

If you use a binary format you don't have to worry about any kind of string escaping or delimiters. Your data is arranged in consistent blocks with the leading information letting you know exactly how much data comes next, either using lengths, or type IDs, or any other deterministic approach.


#5288967 Network Library that supporst RPCs

Posted by Nypyren on 27 April 2016 - 01:25 PM

You can use delegates instead of MethodInfo, I think:

https://msdn.microsoft.com/en-us/library/53cz7sc6(v=vs.110).aspx


#5288957 Network Library that supporst RPCs

Posted by Nypyren on 27 April 2016 - 12:27 PM

Why do they have to be in the same order? Isn't it somehow possible to use a dictionary that contains the allowed rpc methods and gives me the method via the methodname as a string? Don't know if this is possible with delegates, but I've read reflection isn't really a performant way of doing this, I could use reflection only once at the program start to get all the allowed rpc functions and save them in a dictionary with methodname (string ) -> method ( delegate or something ). This way I can get the method in O(1) time and invoke it with the parameters...
 
Another thing I thought about was that the rpcmessage won't contain the methodname, but rather a number ( or a hash value or something ) that corresponds to a method ( it's also less data sent ), so when someone is looking into the packets, that are sent, he doesn't exactly know the methodname. The client however has to know those numbers then and since my code should be readable I have to create an enumeration or something so I still know which method is represented and I have to manually add to this enumeration if I add an rpc function. I guess I don't like to manually do stuff, when I think they can be automatically done, so the server has to provide some sort of interface for the rpc methods... but that's another topic....


If you use strings, it's O(1)... but you still have to transmit the whole string over the network (which will be larger than an integer), and the Dictionary<string,MethodInfo> will use GetHashCode on the string when looking up the dictionary entry. GetHashCode depends on the length of the string - it's pretty fast, but if you use integers you don't need it at all.

If you use integers, you can make a List<MethodInfo> instead and have an O(1) lookup with no hashing, and slightly less memory use than the Dictionary (the size of your function table will be insignificant compared to texture memory though, so don't worry about that).

You would still need a way on the sending side to find the function's ID before you know what integer to send over the network. In the first case you could reuse the same Dictionary and just send functions by name as well. In the second case you can look up the ID in any way you see fit (A Dictionary<string,int> for example).

In both cases, you would search for your functions once at startup and put them into the collection(s). After that you would use your function ID (either string or integer) to pick the method from the collection. If you use integers, you just need to make sure every client/server has the same IDs for the same functions. That's what I meant by the "in the same order" thing above.

Generally I only use Dictionary when the keys are sparse. If I have a Dictionary where the keys are all integers from 0 to N, I just use a List or possibly even an array instead.

MethodInfo aren't very fast to invoke, either. If you had a hardcoded switch statement, it would be much faster. Although since the RPC already has the delay of being transferred over the network, a MethodInfo.Invoke probably won't factor into performance noticably.


#5288945 Network Library that supporst RPCs

Posted by Nypyren on 27 April 2016 - 11:43 AM

I thought of maybe using attributes to declare a function as an RPC method, or to have a naming convention, so only methods with a name, that starts with "RPC" will be invoked.


Using attributes is a good way to do it. You just need to make sure that the way you scan for attributes always keeps the results in the same order. You could search for everything that has the attribute, and then alphabetically sort the search results so that they're always guaranteed to be in the same order.


#5288860 Network Library that supporst RPCs

Posted by Nypyren on 26 April 2016 - 09:10 PM

For a more compact serialization, you should consider using a series of bytes instead of strings. For more fancy ways to make your network messages smaller, look at the encoding tricks that protobuf and SQLite4 use:

https://developers.google.com/protocol-buffers/docs/encoding
https://sqlite.org/src4/doc/trunk/www/varint.wiki

To handle more complex types, you write functions to read/write those complex types. For example with a list, you write the number of items in the list as a varint (-1 for null if you care about distinguishing between null and empty lists), followed by the serialized form of each element in the list. For a Dictionary<K,V> you send the number of pairs as a varint, then alternate between key,value,key,value.

For polymorphism you make a table of type IDs and send the type ID as a varint immediately before any polymorphic object.

For arbitrary graphs of objects, you traverse the graph, adding each object to a list the first time you see it, until you've seen all reachable objects. Then you write: # of objects, the type IDs for each object in the list, followed by the fields for each object in the list. Any time you encounter a field which is a reference type, you find that object in the list and serialize its index (you can use a Dictionary<object,int> to speed this up). Deserialization creates the object list, news each object by its type id, then fills in their fields and cross-references, then returns the first entry in the list as the "root" of the graph.


If you pass a function name over the network and use reflection to invoke it, it's a security risk because someone could send a network message like [mscorlib.dll:System.IO.Directory.Delete, "C:/Users", true] over the network, and you would *never* want to let your receiving side execute that.

To handle the issue of looking up the functions from method names, I suggest making part of your program startup register all of the possible functions you might want to use. Then, when you want to RPC that function, you look up its index in that table and send just the index over the network. Assuming both ends built the function table the same way, the receiver will know which function to use as well.




PARTNERS