• What are you working on? June 2012
    1,926 replies, posted
  • [QUOTE=Sartek;36333885]@robmaister12 How are you sending your randomly generated maps over? you could just pass the seed and the generation code over and it would probably be smaller in file size and then send over a hash of the map to make sure its the same as the server.[/QUOTE] The server generates a seed. When a client connects, the server sends it a packet that contains the seed. Each packet contains a small 1-byte CRC checksum to make sure the contents of the packet arrive correctly. This is done over UDP, I don't know too much about it, but I'm pretty sure there's a very thin reliability layer set up to make sure the client actually receives the packet. So the seed should arrive, and correctly. Given a seed, world generation is deterministic. If, with testing, we see that sometimes the wrong world gets generated incorrectly, we can have the client send a verification packet back to the server not with a hash of world data (since floating point numbers act slightly differently on different machines), but the next randomly generated number after the generation has been done. If everything went properly, that number should match with the server. I would be more worried about floating-point precision across machines. Different computers (even different compile settings) will produce slightly different values. The server may generate a value of 4.9999999f but the client may generate a value of 5.0000001f, and when I check for < 5f, the server will include the point, but the client won't. This is really just an edge case since terrain values are rarely going to be exactly on a border I'm checking for, and the result is that a client may see one or two vertices in the world differently than on the server, and they'll just glitch around it when the server corrects their position. Buildings and other objects that are treated as entities in the game will not actually be placed as part of the terrain generation process on the client, they'll receive the entities from the server. [editline]14th June 2012[/editline] actually, buildings will be placed on the client anyways, the server will technically just correct their positions when the entity data is sent over.
  • I'm looking into it now, and .NET guarantees IEEE754. Values may still be slightly different on separate CPUs. If it's an issue at all I'll mess around with float.Epsilon to see if there's a way I can statistically minimize the occurrence of discontinuities. If float.Epsilon has the same skewing on different CPUs, I might be able to send the server's bits for float.Epsilon and calculate a delta with the client's float.Epsilon and instead of checking for <= 5.0f, I'd check for <= 5.0f + delta. But I'm getting ahead of myself. There are a lot more important things to get done first... [editline]14th June 2012[/editline] someone on the internet was lying! Only Java uses strictfp, .NET uses the system's FPU...
  • [QUOTE=Richy19;36334219]For example this is how im getting the mesh information, not sure if its correct way of doing it tho.[/QUOTE] You're just reading a file, there's not many ways of doing it. I'm not sure exactly what you're asking but here's my working loader, it's messy code but it does the job. [cpp]bool Model::ReadIQM(const std::string& path) { std::ifstream stream(path.c_str(), std::ios::binary); if (!stream.good()) { return false; } IQM::Header header; stream.read((char*)&header, sizeof(IQM::Header)); if (memcmp(header.magic, IQM::MAGIC, sizeof(IQM::MAGIC)) != 0) { stream.close(); return false; } if (header.version != IQM::VERSION) { stream.close(); return false; } std::vector<Vector3f> vertexPositions(header.num_vertexes); std::vector<Vector2f> vertexTexCoords(header.num_vertexes); std::vector<Vector3f> vertexNormals(header.num_vertexes); std::vector<unsigned char> vertexBoneWeights(header.num_vertexes * 4); std::vector<unsigned char> vertexBoneIndices(header.num_vertexes * 4); for (unsigned int i = 0; i < header.num_vertexarrays; ++i) { IQM::VertexArray vertexArray; stream.seekg(header.ofs_vertexarrays + (i * sizeof(IQM::VertexArray))); stream.read((char*)&vertexArray, sizeof(IQM::VertexArray)); if (vertexArray.type == IQM::ATTRIBUTE_POSITION) { if (vertexArray.format != IQM::VALUE_FLOAT || vertexArray.size != 3) { stream.close(); return false; } stream.seekg(vertexArray.offset); stream.read((char*)&vertexPositions[0], header.num_vertexes * sizeof(Vector3f)); } else if (vertexArray.type == IQM::ATTRIBUTE_TEXCOORD) { if (vertexArray.format != IQM::VALUE_FLOAT || vertexArray.size != 2) { stream.close(); return false; } stream.seekg(vertexArray.offset); stream.read((char*)&vertexTexCoords[0], header.num_vertexes * sizeof(Vector2f)); } else if (vertexArray.type == IQM::ATTRIBUTE_NORMAL) { if (vertexArray.format != IQM::VALUE_FLOAT || vertexArray.size != 3) { stream.close(); return false; } stream.seekg(vertexArray.offset); stream.read((char*)&vertexNormals[0], header.num_vertexes * sizeof(Vector3f)); } else if (vertexArray.type == IQM::ATTRIBUTE_TANGENT) { } else if (vertexArray.type == IQM::ATTRIBUTE_BLENDINDEXES) { if (vertexArray.format != IQM::VALUE_UBYTE || vertexArray.size != 4) { stream.close(); return false; } stream.seekg(vertexArray.offset); stream.read((char*)&vertexBoneIndices[0], header.num_vertexes * (sizeof(unsigned char) * 4)); } else if (vertexArray.type == IQM::ATTRIBUTE_BLENDWEIGHTS) { if (vertexArray.format != IQM::VALUE_UBYTE || vertexArray.size != 4) { stream.close(); return false; } stream.seekg(vertexArray.offset); stream.read((char*)&vertexBoneWeights[0], header.num_vertexes * (sizeof(unsigned char) * 4)); } else if (vertexArray.type == IQM::ATTRIBUTE_COLOR) { } else if (vertexArray.type == IQM::ATTRIBUTE_CUSTOM) { } } char* text = new char[header.num_text]; stream.seekg(header.ofs_text); stream.read(text, header.num_text); std::vector<IQM::Triangle> triangles(header.num_triangles); stream.seekg(header.ofs_triangles); stream.read((char*)&triangles[0], header.num_triangles * sizeof(IQM::Triangle)); stream.seekg(header.ofs_meshes); for (unsigned int i = 0; i < header.num_meshes; ++i) { IQM::Mesh m; stream.read((char*)&m, sizeof(IQM::Mesh)); std::vector<Vertex> vertices; std::vector<IQM::Triangle> meshTriangles; for (unsigned int j = m.first_vertex; j < m.first_vertex + m.num_vertexes; ++j) { Vertex vertex; vertex.position = vertexPositions[j]; vertex.texCoord = Vector2f(vertexTexCoords[j].x, 1.0f - vertexTexCoords[j].y); vertex.normal = vertexNormals[j]; vertex.boneIndices[0] = (unsigned int)vertexBoneIndices[(j * 4) + 0]; vertex.boneIndices[1] = (unsigned int)vertexBoneIndices[(j * 4) + 1]; vertex.boneIndices[2] = (unsigned int)vertexBoneIndices[(j * 4) + 2]; vertex.boneIndices[3] = (unsigned int)vertexBoneIndices[(j * 4) + 3]; vertex.boneWeights[0] = (float)vertexBoneWeights[(j * 4) + 0] / 255.0f; vertex.boneWeights[1] = (float)vertexBoneWeights[(j * 4) + 1] / 255.0f; vertex.boneWeights[2] = (float)vertexBoneWeights[(j * 4) + 2] / 255.0f; vertex.boneWeights[3] = (float)vertexBoneWeights[(j * 4) + 3] / 255.0f; vertices.push_back(vertex); } // this is kind of lame.. Because mesh has their own index/vertex set, have to copy the indices // for this mesh and offset it back to index the vertex data for this mesh for (unsigned int j = 0; j < m.num_triangles; ++j) { meshTriangles.push_back(triangles[m.first_triangle + j]); unsigned int tempIndex1 = meshTriangles[j].vertex[0] - m.first_vertex; unsigned int tempIndex2 = meshTriangles[j].vertex[1] - m.first_vertex; unsigned int tempIndex3 = meshTriangles[j].vertex[2] - m.first_vertex; meshTriangles[j].vertex[0] = tempIndex3; meshTriangles[j].vertex[1] = tempIndex2; meshTriangles[j].vertex[2] = tempIndex1; } int vertexFormat = Mesh::VERTEX_POSITION | Mesh::VERTEX_UV | Mesh::VERTEX_NORMAL | Mesh::VERTEX_WEIGHT; Mesh* mesh = new Mesh(vertexFormat, std::string(&text[m.name])); mesh->SetIndices(&meshTriangles[0].vertex[0], m.num_triangles * sizeof(IQM::Triangle)); mesh->SetVertices(&vertices[0], m.num_vertexes * sizeof(Vertex)); m_meshes.push_back(mesh); } delete []text; stream.seekg(header.ofs_joints); for (unsigned int i = 0; i < header.num_joints; ++i) { IQM::Joint joint; stream.read((char*)&joint, sizeof(IQM::Joint)); Vector3f jointTranslate(joint.translate[0], joint.translate[1], joint.translate[2]); Quaternion jointRotate(joint.rotate[0], joint.rotate[1], joint.rotate[2], joint.rotate[3]); jointRotate.Normalise(); Matrix4x4 b = Matrix4x4::Translation(jointTranslate) * jointRotate.ToMatrix4x4(); Matrix4x4 ib = jointRotate.Conjugate().ToMatrix4x4() * Matrix4x4::Translation(-jointTranslate); if (joint.parent >= 0) { b = m_baseFrame[joint.parent] * b; ib *= m_inverseBaseFrame[joint.parent]; } m_baseFrame.push_back(b); m_inverseBaseFrame.push_back(ib); } stream.close(); return true; }[/cpp] [IMG]https://dl.dropbox.com/u/99765/iqmv339.png[/IMG] I can help with anims if needed.
  • [QUOTE=robmaister12;36336067]The server generates a seed. When a client connects, the server sends it a packet that contains the seed. Each packet contains a small 1-byte CRC checksum to make sure the contents of the packet arrive correctly. This is done over UDP, I don't know too much about it. [/QUOTE] UDP has CRC checksums inside the packets them selfs. If the packet is corrupt them the OS will drop or send back a ICMP message. [url]http://en.m.wikipedia.org/wiki/User_Datagram_Protocol#section_2[/url]
  • [QUOTE=benjojo;36337177]UDP has CRC checksums inside the packets them selfs. If the packet is corrupt them the OS will drop or send back a ICMP message. [url]http://en.m.wikipedia.org/wiki/User_Datagram_Protocol#section_2[/url][/QUOTE] UDP checksums are optional
  • [QUOTE=swift and shift;36337293]UDP checksums are optional[/QUOTE] Are there any major platforms that don't use the CRC? Also, this appears to be changing in IPv6.
  • [QUOTE=benjojo;36337177]UDP has CRC checksums inside the packets them selfs. If the packet is corrupt them the OS will drop or send back a ICMP message. [url]http://en.m.wikipedia.org/wiki/User_Datagram_Protocol#section_2[/url][/QUOTE] Alright, then my friend who's working on networking is probably using that. I don't know exactly how our networking code works, but the beauty of working in a team - I don't have to. Just from what I've heard and seen in the commit logs, there's a checksum somewhere. Whether that's part of UDP or his own checksum and he disabled the built-in checksums, I don't know.
  • Added lexical closures: [img]http://i.imgur.com/jkXwB.png[/img] I was running through the lis.py unit tests, found that I needed closure support in order for lambdas to capture variables and stuff. All seems to be working now, turned out less complex than I'd thought :)
  • I have not made a lot of progress. [img]http://i.imgur.com/kPPni.png[/img]
  • Yeah, finally got time to do some program- What's that? My main hard drive crashed on the day I decided to redo my backup system, but failed and lost all my backups? On topic though, over the past few weeks I've been trying to unit test my code. So far it's going good. Am I the only person who unit tests game code here? Also, I'm thinking of doing command line arguments. Does anybody know of a good library to do this with? I'm thinking of ultragetopt right now. I'd use GNU Getopt but it's GNU/Linux only.
  • [QUOTE=Jookia;36338814]On topic though, over the past few weeks I've been trying to unit test my code. So far it's going good. Am I the only person who unit tests game code here?[/QUOTE] I've found since game code is so context-specific, it's hard to write good unit tests for it. Could you post some examples of how you've done it?
  • [img]http://i.imgur.com/Qw5aq.png[/img] Starting the long, hard road of OS dev.
  • Added region-settings for entities, and regiontrigger things. I also wanna make checkpoints use this region tool. [img]http://i.imgur.com/Q8eRq.gif[/img]
  • [QUOTE=Jookia;36338814]Also, I'm thinking of doing command line arguments. Does anybody know of a good library to do this with? I'm thinking of ultragetopt right now. I'd use GNU Getopt but it's GNU/Linux only.[/QUOTE] [url=http://www.boost.org/doc/libs/1_49_0/doc/html/program_options.html]boost::program_options[/url]?
  • [QUOTE=simie;36338855]I've found since game code is so context-specific, it's hard to write good unit tests for it. Could you post some examples of how you've done it?[/QUOTE] Yes I'll go get that right off my hard drive. [editline]15th June 2012[/editline] [QUOTE=Lexic;36338865][url=http://www.boost.org/doc/libs/1_49_0/doc/html/program_options.html]boost::program_options[/url]?[/QUOTE] I've considered those, but its Unicode support is a little shady (although it may be improving). I've used it and it's actually kind of redundant when it comes to storing the inputs. It creates some kind of configuration and it's just not nice.
  • [QUOTE=Richy19;36334219]Sent you a PM but figured id just ask here, Im trying to do this in C++ but Im not really getting the concept of how to do this? I know its a bit different in C#. I think its mainly because your not parsing the file as such. Could you give any information? For example this is how im getting the mesh information, not sure if its correct way of doing it tho. iqmheader hdr; FILE *f = fopen(filen.c_str(), "rb"); if(!f) { fclose(f); std::cout << "Failed to open: " << filen << std::endl; return false; } if(fread(&hdr, 1, sizeof(hdr), f) != sizeof(hdr) || memcmp(hdr.magic, IQM_MAGIC, sizeof(hdr.magic))) { fclose(f); std::cout << "Failed to open: " << filen << std::endl; return false; } std::cout << fseek(f , hdr.ofs_meshes, SEEK_SET) << std::endl; iqmmesh meshar[hdr.num_meshes]; std::cout << fread(meshar, hdr.num_meshes, sizeof(iqmmesh), f) << std::endl; for(int i = 0; i < hdr.num_meshes; i++) { std::cout << meshar[i].name << " " << meshar[i].material << " " << meshar[i].first_triangle << " " << meshar[i].num_triangles << " " << meshar[i].first_vertex << " " << meshar[i].num_vertexes << " " <<std::endl; } std::cout << std::endl << "File: " << filen << std::endl; std::cout << "Magic: " << hdr.magic[16]<< std::endl; // the string "INTERQUAKEMODEL\0", 0 terminated std::cout << "Version: " << hdr.version<< std::endl; // must be version 2 std::cout << "Filesize: " << hdr.filesize<< std::endl; std::cout << "Flags: " << hdr.flags<< std::endl; std::cout << "Textures: " << hdr.num_text << " : " << hdr.ofs_text<< std::endl; std::cout << "Meshes: " << hdr.num_meshes << " : " << hdr.ofs_meshes<< std::endl; std::cout << "Vertex arrays: " << hdr.num_vertexarrays << " : " << hdr.num_vertexes << " : " << hdr.ofs_vertexarrays<< std::endl; std::cout << "Triangles: " << hdr.num_triangles << " : " << hdr.ofs_triangles << " : " << hdr.ofs_adjacency<< std::endl; std::cout << "Joints: " << hdr.num_joints << " : " << hdr.ofs_joints<< std::endl; std::cout << "Poses: " << hdr.num_poses << " : " << hdr.ofs_poses<< std::endl; std::cout << "Animations: " << hdr.num_anims << " : " << hdr.ofs_anims<< std::endl; std::cout << "Frames: " << hdr.num_frames << " : " << hdr.num_framechannels << " : " << hdr.ofs_frames << " : " << hdr.ofs_bounds<< std::endl; std::cout << "Comment: " << hdr.num_comment << " : " << hdr.ofs_comment<< std::endl; std::cout << "Extensions: " << hdr.num_extensions << " : " << hdr.ofs_extensions<< std::endl; which produces: [code] 0 24 1 6 0 2386 0 1476 15 20 2386 602 1476 385 File: ./mrfixit.iqm Magic: Version: 2 Filesize: 257808 Flags: 0 Textures: 864 : 124 Meshes: 2 : 988 Vertex arrays: 6 : 1861 : 1036 Triangles: 2988 : 105376 : 141232 Joints: 75 : 177088 Poses: 75 : 180688 Animations: 1 : 187288 Frames: 101 : 333 : 187308 : 254576 Comment: 0 : 0 Extensions: 0 : 0 [/code][/QUOTE] Whats wrong with streams? Your code looks disgusting Edit: Quoting/Editing is still broken, since always :/
  • [vid]http://dl.dropbox.com/u/47914747/Embedded/Layer/Editor - change layer.webm[/vid] Working on in-game level editor. Currently you can only change layer of objects. Final design: [img_thumb]http://cloud-2.steampowered.com/ugc/541804448596183624/1E082164D93B25D516A93825482E3C7DF5F9D9F9/[/img_thumb]
  • [QUOTE=Zeh Matt;36339036]Whats wrong with streams? Your code looks disgusting Edit: Quoting/Editing is still broken, since always :/[/QUOTE] I was just following the example in the demo, but your right I should use streams instead. [editline]15th June 2012[/editline] [QUOTE=Jenox;36336800]You're just reading a file, there's not many ways of doing it. I'm not sure exactly what you're asking but here's my working loader, it's messy code but it does the job. I can help with anims if needed.[/QUOTE] Thanks for this, One of the problems I see I had was that I thought I was trying ot read in a vertex(as a struct) where as by the looks of it your meant to read it as the pure data (aka floats and such) BTW do you do any sort of endian safety? Like making sure it gets converted to little endian(with the lilswap function)? And are you using a C++ wrapper for IQM? you seem to ave everything in namespaces. Help with the animation would be verymuch appreciated but for now I just want to get the model loaded and textured and such :), but in the future I might take you up on that
  • I didn't bother to do any endian conversion because I don't need it. If you need it, do it. I just whacked all the IQM structs into an IQM namespace just because I like namespaces.
  • Wanted to learn about DNS to possibly make my own name server. [url]http://tools.ietf.org/html/rfc1035[/url] Holy shit that is long. Can't even use [url]http://pretty-rfc.herokuapp.com/[/url] on it :(.
  • [QUOTE=voodooattack;36325402][ update ] [ render ] [ update ] [ render ] [ update ] [ render ] .... [ display ] [ input ] [ input ] [ display ] [ input ] [ input ] .....[/QUOTE] should be [ input ] [ update ] [ (wait) ] [ input ] [ update ] [ (wait) ] [ input ] [ update ] [ (wait) ] .... [ render ] [ display / wait ] [ render ] [ display / wait ] [ render ] [ display / wait ] .... with the update thread slightly ahead and doing more iterations so the last and next positions/whatever of an object are always available in a buffer. If you do input in the display thread you'll lose accuracy to the lower display rate, and if you render in the update thread there's not much gain because these two are the most expensive steps. The rendering step needs to interpolate between two game states or you will get stuttering when the update count between frames changes. The other option is to have a constant ratio between frame time and update steps, but then you have to change the time step to match the system the game is running on which gives you trouble if you want to record replays that can be validated. Also (and mostly unrelated to voodooattack's post), variable tick rates are perfectly fine for anything that doesn't need to be 100% reproducible or always very accurate, like for example clients for almost every online game: It doesn't matter if a bit of debris bounces slightly differently on one machine than on the other or if it collides at all as long as you can avoid stuttering. This way you can run highly detailed and accurate physics effects on a high-end machine without making something unplayable on a less expensive CPU. There's also no reason not to mix time step sizes, for example by running all important things with a fixed resolution while running particle systems and other purely visual calculations with variable resolution in a second step. There are only two things that you have to remember with a variable vs. a fixed time step: Never accumulate force outside of a simulation step unless the acceleration is constant. Sudden movements should add velocity directly, otherwise jumping and similar action will glitch a lot. Don't do Boolean checks based on position alone. In a fixed time step the result for IsOnGround() is always the same based on distance alone in the frames after beginning a jump. With a variable &#916;t it's possible that the player stays very close to the ground for an additional frame if the computer is fast enough. You have to check for the relative velocity between player and ground to avoid adding velocity twice. Floating point arithmetic itself is deterministic across most systems, the problem is that some things (trigonometry) not in IEEE 754 aren't.
  • [QUOTE=DrLuke;36333069]Then do it[/QUOTE] I do web dev 9-5 M-F so most of my free time is not spent doing more development haha.
  • After the genetic paint thing didn't bear any fruits (only garbage came out :C), I've decided to dip into neural networks that control little tanks over a field which try to sweep mines by driving over them. I'm still a bit undecided on how to merge DNAs and mutate them, but I will figure it out.
  • How long did the paint thing run, and how did more successful mutations spawn the next generation, how many were in the next generation after a successful option was selected?
  • [QUOTE=KmartSqrl;36342504]How long did the paint thing run, and how did more successful mutations spawn the next generation, how many were in the next generation after a successful option was selected?[/QUOTE] Over night, and in the end it was just the same patterns as in the beginnings. I always has 2 codes running at the same time, and the one with a higher score survived, and the looser was replaced by a new random code. Mutation happened after every round by randomly changing 1 to 5 instructions.
  • Would there be issues with making it run 10 at a time and let the most successful 8 of them recieve mutations and the others be replaced by random new programs? I think you might have been limiting how many times something was able to mutate by eliminating them so early. Give things a little more time to change, and maybe drop the number of instructions that can change? Maybe something like one-2 instructions can change, or 1 instruction can be removed or one instruction can be added? With how far the framework for that got it'd be a shame to not get it to bear some kind of fruit :P
  • [QUOTE=Maurice;36338860]Added region-settings for entities, and regiontrigger things. I also wanna make checkpoints use this region tool. [img]http://i.imgur.com/Q8eRq.gif[/img][/QUOTE] Hnng are you just redoing all the features I've added
  • [QUOTE=KmartSqrl;36342631]Would there be issues with making it run 10 at a time and let the most successful 8 of them recieve mutations and the others be replaced by random new programs? I think you might have been limiting how many times something was able to mutate by eliminating them so early. Give things a little more time to change, and maybe drop the number of instructions that can change? Maybe something like one-2 instructions can change, or 1 instruction can be removed or one instruction can be added? With how far the framework for that got it'd be a shame to not get it to bear some kind of fruit :P[/QUOTE] Well, running 2 of them already is pretty slow without limiting the maximum instruction per second to 1000 per code
  • [QUOTE=Jenox;36340010]I didn't bother to do any endian conversion because I don't need it. If you need it, do it. I just whacked all the IQM structs into an IQM namespace just because I like namespaces.[/QUOTE] It bugs the crap out of me when I see C++ projects that simply prepend a 'namespaceable' name to every class possible, instead of actually using a namespace. (Like WxWidgets, or CryEngine 3, or id tech 4). So thank you for not doing that.