Jump to content
  • Advertisement
  • entries
    33
  • comments
    69
  • views
    31813

About this blog

This is a log of my experience on rewriting my graphics engine called HG3D

Entries in this blog

 

Perfect Messaging On C++

Messaging Design Pattern (MDP) Components of a program need to communicate with each other. In messaging design pattern this communication is done via sending and processing messages. A third party component called messenger stands between different components and does the task of passing proper messages to proper components. MDP increases decoupling, encapsulation, and reusability by removing component communication from component functionality. (I am not discussing distributed systems here) Implementation This pattern is often implemented using a message processing interface. Any component that wants to work with the messenger should implement this interface. This interface declares a process_message method that processes a message and returns a message to be sent to other components. Issues This implementation causes the following performance issues: Messages are usually broadcasted to all components. For a component to detect whether a message should be processed or not, it should check either the type of the message or some other member of the message. Implementing interface requires inheritance and increases the function call overhead. These performance issues make MDP unsuitable for communicating small amounts information, which puts a limitation on the ways we use the messaging system to communicate. C++ Implementation In my implementation of MDP I had the following concepts in mind: People use C++ to achieve zero-overhead abstraction (This is not java). Messaging should be so cheap that it should be used as the preferred means of communication. The components are available at compile time (we will utilize this to achieve to the goals above). Solution Find the message destination by message type on compile time (eliminates broadcasting). Passing messages are function calls, try to inline them (eliminate overhead). Since messages have different types, the components' interfaces are different (i.e. each have different process_message methods), there is no need for inheritance. Messenger Since the components are available at the compile time, the messenger can make a tuple of the components it's going to handle. The messenger implements pass_message method as the means of passing messages to internal components from external components. Passing messages includes calling process_message of all components, this is broadcasting, but there is a solution to eliminate redundant process_message calls, described later. One more method get_module is implemented to allow access to components. Note that each component after processing a message returns a tuple of messages to be replied, this also has to be taken care of. //messenger.inl #pragma once #include <cstddef> #include <tuple> namespace Messenger { namespace Privates { //Passes a single message to a tuple of modules. template <std::size_t N, std::size_t M> struct message_passer; //Passes a tuple of messages to a tuple of modules. template <std::size_t N, std::size_t M> struct tuple_message_passer; //Passes a tuple of messages to a tuple of modules. template <typename MTT, typename TT> void pass_tuple_message(const MTT messages_tuple, TT& modules_tuple); template <std::size_t M> struct message_passer <0, M> { template <typename MT, typename TT> static void pass_message(const MT& message, TT& modules_tuple) {} }; template <std::size_t N, std::size_t M> struct message_passer { //Pass a single message to a tuple of modules, the order of modules in the tuple, defines the order of the messages being passed. //The result of processing each message is a tuple of messages to be passed to the modules, which is done using pass_tuple_message. template <typename MT, typename TT> static void pass_message(const MT& message, TT& modules_tuple) { pass_tuple_message((std::get<M - N>(modules_tuple)).process_message(message), modules_tuple); message_passer<N - 1, M>::pass_message(message, modules_tuple); } }; template <std::size_t M> struct tuple_message_passer <0, M> { template <typename MTT, typename TT> static void pass_message(const MTT& messages_tuple, TT& modules_tuple) {} }; template <std::size_t N, std::size_t M> struct tuple_message_passer { //Pass a tuple of messages to a tuple of modules. First the first message on the tuple is passed to all of the modules then the rest of the messages are passed in the same manner. template <typename MTT, typename TT> static void pass_message(const MTT& messages_tuple, TT& modules_tuple) { message_passer<std::tuple_size<TT>::value, std::tuple_size<TT>::value>::pass_message(std::get<M - N>(messages_tuple), modules_tuple); tuple_message_passer<N - 1, M>::pass_message(messages_tuple, modules_tuple); } }; //The helper function that passes a tuple of messages to a tuple of modules. template <typename MTT, typename TT> void pass_tuple_message(const MTT messages_tuple, TT& modules_tuple) { tuple_message_passer<std::tuple_size<MTT>::value, std::tuple_size<MTT>::value>::pass_message(messages_tuple, modules_tuple); } } //The messenger template <typename... Ts > class messenger { //The tuple of modules using module_tuple_type = std::tuple<Ts...>; mutable module_tuple_type modules; public: messenger() {} //pass a message to internal modules template <typename MT> void pass_message(const MT& message) const { Privates::message_passer<sizeof... (Ts), sizeof... (Ts)>::pass_message(message, modules); } //get a certain module template <std::size_t N> typename std::tuple_element<N, module_tuple_type>::type &get_module() { return std::get<N>(modules); } }; } Components Each components is a class that implements the process_message method, that takes a const reference message and returns a tuple of const reference messages. The message type will vary between different message processors, meaning a components may have several overloads of the process_message method with varying return types, and each overload only handles one type of a message. In this method since all the messages are broadcasted to all the components, all components should process all the messages, but the goal is to eliminate redundant message processing, this is done using the following piece of code: //messenger-module.inl #pragma once #include <cstddef> #include <tuple> //Note: Add to the end of the module class to process known messages only, other wise you need to define a message processor for each message type. #ifndef PROCESS_KNOWN_MESSAGES_ONLY # define PROCESS_KNOWN_MESSAGES_ONLY public: template<typename MT> std::tuple<> process_message(const MT& message) {return std::tuple<>();} #endif Adding PROCESS_KNOWN_MESSAGES_ONLY to the end of each component class, defines a default message processor that does nothing and returns an empty tuple of messages. This means if no explicit message processor for a message type is defined in the components class, the message processing is done in using this default message processor which can be optimized away. Hence redundant messages are not passed to modules after optimization. Sample component The following module processes messages of type std::string, rest of the messages are processed by default message processor. //test-module-b.inl #pragma once #include <string> #include <iostream> #include "messenger-module.inl" namespace Test { class module_b { public: std::tuple<> process_message(const std::string& message) { std::cout << message << std::endl; return std::tuple<>(); } PROCESS_KNOWN_MESSAGES_ONLY }; } One more step So far components can pass messages, only if they have received a message. In other words they just can reply to a message, they can't pass a message without processing a message. It would be useful if such utility was present to each component to pass a message before or even while processing a message. For a component to pass a message, the component needs access to the messenger which means that these classes will be codependent, not only each component and messenger would become codependent but all components as well would become codependent to each other. I have addressed this problem with a trick as follows, but I am not satisfied with it. Any solutions are welcome Current solution is to add a function that has access to the messenger of the components, which passes the messages to components using the messenger. //test-module.inl #pragma once namespace Test { template<typename MT> void pass_message(const MT& message); } This is included in each component's file, sample component: //test-module-a.inl #pragma once #include "messenger-module.inl" #include "test-module.inl" #include <string> namespace Test { class module_a { public: std::tuple<> process_message(const int& message) { pass_message(std::to_string(message)); return std::tuple<>(); } PROCESS_KNOWN_MESSAGES_ONLY }; } The declaration of the pass_message is provided in the header which allows the use in the component. The definition however is yet to come, which will follow after the instantiation of the messenger, which is as follows. //test-messenger.inl #pragma once #include "messenger.inl" #include "test-module-a.inl" #include "test-module-b.inl" namespace Test { Messenger::messenger<Test::module_a, Test::module_b> test_messenger; template <typename MT> void pass_message(const MT &message) { test_messenger.pass_message(message); } } This is not an elegant way of doing it, but does the job. Pros and cons Cons The performance relies on the optimizer to do it's job. Pros Zero overhead. Increase of encapsulation and decupling. Easy to implement. Thanks for reading, looking forward to your opinions and suggestions. I'm also looking forward on writing a graphics engine using this design pattern, hopefully I'll make time and post the progress here. Code and VS project on GitHub: https://github.com/IYP-Programer-Yeah/MDP/

IYP

IYP

 

WoA: Too disappointed to participate again?

Yes, that is true, I'm too disappointed at judgment to participate again. This was the 3rd year of our participation and the best of the three years. The first 2 years we did not make time to finish the game, we did not have artists to do the art, and we got bad results, but this year we put our best effort, most of our time and finished the game with almost no bugs. First rant Some judges don't really put the effort, even biased on games, yes the game might be unappealing at first experience, or may at all (there are scores dedicated for these problems), but a judge has agreed to put time, put effort and do his best to look at every single point of the game, becoming a judge is a commitment, it not for fun. Even I as a community reviewer finished some of the games I know judges who didn't. Also as a judge, one should put the effort to fully read the blogs, read other judges' reviews, watch the videos and all that there is to be done, yet the judgment this year was lacking it. Second rant There were supposed to be 4 scores one from each judge, and the best 3 would be averaged, which this chance was take from us, coz one of the judges did not submit, or maybe due to technical problems the submission were not saved, but that in itself shows the lack of commitment, to check your own submissions. The judgment is highly biased on people's opinion, this is completely true, just obvious by look at the judgments and of course understandable, so having more judges widens the opinion variety, and normalizes the bias. The lack of submission just took away this chance. Summery Agreeing to becoming a judge is a commitment, I really appreciate the holding of the event, the sponsors, and the judges, but the other side of the competition, the contestants are valuable as well, and their efforts are even more valuable, and not putting effort to fully understand the game, is just a disrespect to the contestants who put effort on the game. Most of this might be due to lack of time for the judges, but as I said, it's a commitment, you should not commit, if you're not sure you can do it. I have always enjoyed participating, but feeling the disregard against the effort, I don't really think it's any good to participate any more.  

IYP

IYP

 

IYP Bot 2.0

IYP Bot 2.0 The Idea IYP Bot started as a human behavior mimicking bot, and ended up with an unintended idea. The idea of a general bot that could be programed with a few command over the chat. But for this idea to be implemented I needed to refactor all the existing chat bots and make a general purpose bot. A general chat bot first recognizes a string pattern as a command, and generates a response, and/or does a process depending the on the command. For example a chat history logger, recognizes every string as a command and does the process of storing the strings, or maybe some data mining process over that. So we need a way to define a pattern to be recognized and a way to associate a response and/or process with the defined pattern, so on the event of recognizing the pattern the bot does the process and/or responds with proper response. This process is done by categorizing messages, and adding responses to these categories, so in the event of a string fitting in a category (recognizing the pattern) bot responds with a proper random (explained more on Message Categorization and Response Generation->Response Generation) response from that category (responding and/or doing the process). The specifics of defining these categories and responses will be discussed later on section Message Categorization and Response Generation. For the rest of the document I use the term message for patterns defining a category, and response for a response/process associated with each category. Permission System Interaction Permissions Letting every one program a bot is not a good idea, since a lot of mal-intended people exist on the chat, that may harm your system using the program, or abuse the bot to do harmful acts on the server. This brings the need for a permission system. There are four permission levels (the higher the number of the permission the lower its rank):
1- Admin
2- Whitelisted
3- None
4- Blacklisted All of the permission levels applies to roles, member, and channels, the only exception is that channels can't have the admin permission. For any interaction with the bot you will need a permission. Your permission is evaluated as is explained below: 1- If the bot doesn't know any admins, the first person that sends something that the bot can receive will be assigned the first admin, so you're better off first setting the bot up and then adding it to servers. 2- Admins can assign members, roles, and channels permissions as explained above using command: .Permission [Channel/Member/Role] [White/Black/Admin] [Tag] 3- Admins override permission of their roles and the channels they are interacting with the bot in. 4- If a member has a permission other than None assigned to him/her the permission of his roles will not influence his/her permission. 5- If a member is assigned None permission, highest ranked permission of the roles of the member, is used as the member's permission. 6- If member's permission (evaluated from rules 4 and 5) is not Admin the final permission to interact with the bot will be evaluated from the minimum of the member permission and channel permission. 7- If the final evaluated permission from rule 6 is Blacklisted the bot will ignore all of the messages from the member. If the permission is None, member can use commands that are already added to the bot by members. If the permission is Whitelisted the member can use the bot's built in commands to program the bot. Notice: Interaction through bot's DM is possible, this means that permissions assigned to roles and channels will not apply to DM interactions. Test/Final Feature Every new message or response that a member submits to the bot is considered a test feature, meaning it can only be used on channels that are designated test channels, or they should be set as final feature by an Admin permissioned member. Admins can add channels to designated test channel or remove them from designated test channels using the commands: .AddChannelToTest .RemoveChannelFromTest These commands add/remove the channel they are sent in to/from designated test channel list. Admins can set messages and responses to test/final features using the commands: .SetMessageAsTest [Category][Message ID] .SetMessageAsFinal[Category][Message ID] .SetResponseAsTest [Category][Response ID] .SetResponseAsFinal[Category][Response ID] I will explain more on how to acquire Message ID and Response ID on section Bot Elements->Category. Bot Elements Programs A program is a java source code with exactly one public class, the public static functions of the class will be accessible on messages and responses. You can add/remove/list programs using following commands: .NewProgram [Public Class Name][Program] .RemoveProgram [Public Class Name] .ListPrograms The methods you will be calling will have 2 arguments first one of type String[], and the second one of type Message from package: de.btobastian.javacord.entities.message.Message; So you should import this packet on your programs which will allow Discord API integration. The javacord message object passes as second argument will be the message object generated by javacord on even of the receiving the message that triggered the bot. The return value of a method called on messages should be of type Boolean, and of type String on responses. Resources Each resource is a set of strings represented by a name, each string is called an element of the resource. You can manipulate resources using following commands: .NewRes [Resource Name] .AddToRes [Resource Name][Element1][Element2][Element3]...[Elementn] .RemoveRes [Resource Name] .RemoveFromRes [Resource Name][Element] .ListRess .ListResElements [Resource Name] There is a default resource named * and contains every string by default. Category A category contains a set of messages (patterns) and a set of responses represented by a name. You can manipulate categories by following commands: .NewCat [Category Name] .AddMessage [Category Name][Message] .AddResponse [Category Name][Response] .EditMessage[Category Name][Message ID][Message] .EditResponse[Category Name][Response ID][Response] .RemoveCat [Category Name] .RemoveMessage [Category Name][Message] .RemoveResponse [Category Name][Response] .ListCats .ListMessages [Category Name] .ListResponces [Category Name] Following commands list the messages/responses with their ID: .ListMessages [Category Name] .ListResponces [Category Name] Message Categorization and Response Generation A string fits in a category if it matches any of the messages in a category. Messages and responses have the same structure. Message/Response Structure Each message/response is an ordered list of message parts, a message part can be one of the following: Raw Message Part: A raw message part is a raw string. Mention Tag Message Part : A mention tag message part can be the mention tag of the bot, or the mention tag of the sender of the string, each represented by @bot (bot's mention tag) and @sender (sender's mention tag). Sytnax: @sender @bot Resource Message Part: A resource message part is an instance of a resource, with properties of ID, repetition counts, case sensitivity and the name of the corresponding resource the message part is an instance of. Syntax: <Resource Name>{ID}{Count}{Case Sensitivity} ID, Count, and Case Sensitivity are optional, you can leave them empty or not even use them, any of the following are correct resources message parts: <Resource Name> <Resource Name>{ID/Empty} <Resource Name>{ID/Empty}{Count/Empty} <Resource Name>{ID/Empty}{Count/Empty}{Case Sensitivity/Empty} ID allows the resource to be referenced and its value to be used in response generation or as input for programs and its value can be any integer, the default value is -1, any resource with an ID of -1 will be treated as if no ID is assigned to it. Count defines the repetition count and can be any non-negative integer, * or +. * represents any repetition count including 0, + represents any positive repetition count. The default value is 1. Case sensitivity defines the case sensitivity of the message part, it can be true (case sensitive) or false (case insensitive). Program Message Part: A program message part is a function call of a java static function that returns either a Boolean (on messages) or a String (on responses). Syntax: ~ClassName.FunctionName(<ResourceName1>{ID/Empty}, <ResourceName2>{ID/Empty}...,<ResourceNamen>{ID/Empty})~ Any correct resource message part form is acceptable for function arguments. Notice: The * resource can not be used in the argument list w/o being a reference. A typical form of a message/response: <greetings> @sender this is my first response, i <empty/dont>{0} want ~Program.function(<empty/dont>{0})~ program to run. Matching a Message A message matches a string \(S = A_{1}A_{2}A_{3}...A_{n}\) where each \(A_i\) is a sub-string of \(S\) that can have the length \(0\), if and only if for every \(i\) the \(i\)-th message part accepts \(A_i\). Each message part accepts a string as follows: Raw Message Message Part: A raw message part accepts a string that is equal to its raw string, the case sensitivity, is defined by the case sensitivity of the message. Mention Tag Message Part: A mention tag message part accepts the sender's mention tag in the case of @sender and bot's mention tag in the case of @bot. Resource Message Part: A resource message part accepts a string if it's a repetition of the elements of the corresponding resource by the repetition count of the message part, the case sensitivity is defined by the message part's case sensitivity. Program Message Part: A program message part accepts a string if the return value of its function call, is true. The arguments of the function call are resources, if these resources have IDs that are not equal to -1, they are references to resources that are used in the message that match the ID and resource name. To generate the first argument of the function which is an array of strings, if the resource is a reference the accepted sub-string by that resource is associated with the resource, and if the resource is not a reference a random string accepted by the resource is associated with the resource. Each element of the first argument array will be the string associated with each argument resource in order of their appearance, except the last element of the array which will be the sub-string associated with the program. The argument resources that have an integer value for Count that is greater than 1 will be considered several resources with the same ID and name, and will have separate string in the first argument. Notice: There are two kind of messages, interrupting and non-interrupting, if the string is matched with an interrupting message, if will not be matched by any other messages, but if it's matched by a non-interrupting message, it has a chance to be matched by other messages. For each matched message a proper response will be generated. You can use following command to change a message to interrupting and non-interrupting, by default all messages are interrupting. .SetMessageInterrupting [Category Name][Message ID][True/False] Response Generation A response has the same structure of a message, the only difference is that on response generation each message part is substituted by a string as follows: Raw Message Message Part: A raw message part is substituted for the raw string. Mention Tag Message Part: A mention tag message part is substituted for the mention tag of the bot or sender, depending on the it's value being either @bot, or @sender. Resource Message Part: A resource message with and ID that is not -1 will be reference to the resource message part on the message with same name and id, if no such resource is found it will be treated as if it's not a reference. If the resource message part is a reference it will be substituted for the string the referenced resource message part accepted. This can be used to use sub-strings from the message in the generated response. If the resource message part is not a reference it's substituted for a random string that the resource message part accepts. Program Message Part: The program message part is substituted with it's return value. The resource message part arguments, if reference resource message parts, will be associated with the sub-string referenced resource message part has accepted other wise the resource will be associated with a random string that the resource message part accepts. The first argument is an array of strings and each element of the array will be equal to the string associated with each resource message part, in the order of appearance. Notice: On the even of any program returning null, the response generation will be terminated, this can be useful for logging program that don't intend to generate responses. Notice: Some of the messages and responses can generate warnings, to receive or stop receiving warnings use follow commands: .NotifyMeWarnings .Don'tNotifyMeWarnings Notice: On response generation a response is chose that the referenced resource message parts of it, bets fits the available resource message part on the message. If several of the same level of compatibility is found, a random one is chosen. Save/Load/Merge/Reset Database Every thing added to the bot, is considered part of it's current database, you can manipulated the database by following commands: .SaveDatabase [File Name] .LoadDatabase [File Name] .MergerDatabase [File Name] .Reset Thanks for reading the fairly technical doc, if interested in actually using the bot join my test channel on discord: https://discord.gg/nuzz6z6
Also source code available on: https://github.com/IYP-Programer-Yeah/IYP_Bot-2.0 Also I tried to get this out as soon as possible so there are probably a lot of typos, i'll appreciate any suggestions on better phrasing, error fixings and syntax simplification. 

IYP

IYP

 

WoA V Day 7

So today our artists finished the art, we applied it, finished the menu, save and polished out stuff, then did a little optimization and the rest (the last 3 hours that only I was awake of the whole team) I spent on debugging. This year we had 2 artist and I really thank them for the amazing art they did, loved it XD. So no videos for today, if you wanna play it though here is the link:
https://drive.google.com/open?id=0ByRMOIz9rEO0MkU2cUtWSXlfd3M One thing I'm interested in is to see how people solve the puzzles and how fast they can finish the levels, and what their max score are, so any one who plays the game, I'd appreciate if they could take the time to share some feed backs or a video (which will be awesome). Tomorrow I'll post a review on our work on WoA, and few days later, I will send my solutions for the levels XD. Good luck to every one Also we have 3 minor bugs I dare the judges to find

IYP

IYP

 

WoA V Day 6

So lot's of progress today, the game is almost complete, some little work tomorrow and will submit, good luck to ever one XD. Also here's a video of me running around in nearly complete second level, with new dominoes and towers XD, some new HUD as well XD, check it out.
Also today was mostly spent on optimizations, so nothing really much visual to show, except the second levels new look and the character's better cloth simulation https://youtu.be/nw6o2Fm5h8g

IYP

IYP

 

WoA V Day V

So progress was again slow today, the tutorial was completed, and some part of the scoring was implemented, and a few little things were done, I also added some shitty sound effects that are probably going to be replaced later. So we decided to release a second WIP version, we didn't post the first version on blogs, just in chat, so you might not have know about it, but this version is more complete so here it is: https://drive.google.com/open?id=0ByRMOIz9rEO0UDU5S1FNZVlOY2c And the video to be uploaded : https://youtu.be/x43TTps802c Good luck every one, only 2 more days remained

IYP

IYP

 

WoA V Day 4

So more than half of the competition is over, today we made some good progress, are artists made the character and a dice model for the start and end points of the first level, not gonna show that now though, just a picture , a big thanks to them, love their stuff. We almost finished second level as well, some of the menu stuff was done, and newt made a tutorial level, explaining how to play the game. So the todo list from yesterday is almost done :
And here's a video of me jumping around in second level: Good luck to every one

IYP

IYP

 

WoA V Day 3

So today the progress was slow, first we had git problems and then some build problems, but we still had fair amount of progress how ever our todo list just kept getting bigger and bigger . We worked one menus, fixed some bugs, did some optimizations, discussed second level and worked a little on it and our artists made the texture for play ground, but more importantly we finished the game mechanics, meaning you can now jump around rotate dominoes and push them to destroy some castles, and play the first level, which I have in the video below, and the good new is it's already uploaded so no need to wait this time . Hopefully every one is making good progress, good luck

IYP

IYP

 

WoA V Day 2

So we had a good progress today, i implemented the jump helper so the jumps are easier, newt implemented the menus and few other stuff and the artists started working on the character model. So it may not seem much of a progress but it is XD. With the jump helper implemented i feel a huge weight lifted off my shoulders XD, so we have video of me jumping around on dominoes. We also got some of the mechanics implemented, such as pushing the dominoes and rotating them, though the rotation is still buggy. We also thought of a competitive game mode where you play with other players and try to destroy their castles and they can defend their castles by rotating or pushing the dominoes, but that's for later, we may implement it after the basic single player is implemented with several levels. Also with the jump helper being flexible we decided on having 3 levels of difficulty so every one can play it, even the noobs. Here's a video (still uploading so may not be there yet, so check out later XD):
https://youtu.be/p2T3BlBSoo8

IYP

IYP

 

WoA V Day 1

So we decided to go with a 3D runner. The themes we chose were Castles and Chain Reaction. The runner runs on the dominoes trying not to fall and tries to destroy the domino castles. The castles can be destroyed by falling the right dominoes, or if you make the wrong ones fall you may doom yourself, so it's also a puzzle, but with no time, and the rush while trying to stay on the dominoes who can really put the time to find the best path. So it's gonna be a difficult one, so get ready for some frustration. What we got to do today was to make the first level of the game, which is kind of a tutorial, a simple one which I myself can't play  (don't judge me). We also got the physics to work and the runner can actually jump, but it's way too difficult, if you don't believe me watch the video (if it's uploaded), our artists also made a cool domino model, which looks amazing. That's almost all we got to do today, coming up with game play took a lot of our time today, so expect more progress tomorrow. First level with physics: https://youtu.be/1q3YLvABIn8 A little bit of game play: https://youtu.be/-KhUHGM__HI
 

IYP

IYP

 

IYP Bot

So as you know, GDN chat was recently moved to discord, and with discord comes the ability to write a bot. So i thought to write a bot so that users can program it. The main idea behind it was to make a bot that people could contribute to, to teach it language. Though it started that way, but after seeing people's usage of it the idea completely changed to programmable bot. So as the bot got more complicated so did the syntax, and that made a few people to be able to use it. So i thought to introduce my bot with a full documentation.

The idea is to categorize the messages the users in the chat send. All the messages in one category share the same group of responses. So you can say in a category you have an array of messages and an array of responses. If a message is recognized of a category, a random response from that category will be replied by the bot. If the category doesn't contain any responses the category name is replied by the bot.

Syntax to add a message to a category:.add_message [category name] message

Syntax to add a response to a category:.add_response [category name] message

If you use a category name that doesn't exist, it will create that category.

Note: messages and categories are stored in the order the user have added them. That means if a messages if recognized of a specific category the other categories that were added after that category wont be checked. Same is true for messages, if a user sent message is recognized as a message stored in a category, the messages added after that message to the category wont be checked.

Example (on the examples the user name is IYP (which is me :D ) and the bot is IYPBOT): IYP.add_message [greeting_message] hello IYPBOTMessage added IYPhello IYPBOT[greeting_message] IYP.add_response [greeting_message] hey IYPBOTResponse added IYPhello IYPBOThey

Tags: @me, @sender
You can use @me tag as a placeholder to the bot's mention tag.
@sender is a placeholder to user's mention tag.

Example:IYP.add_message [greeting_message] hello @me IYPBOTMessage added IYP.add_response [greeting_message] hey @sender IYPBOTResponse added IYPhello @IYPBOT IYPBOThey @IYP
Resources: A resource is an array of constant strings that can be used instead of a phrase or word in a message, a massage can contain several resource tags. You have to create a resource and then add the strings one by one to the resource.

Syntax to create a resource:.new_res
Syntax to add a resource.add_to_res string
Example:IYP.new_res IYPBOT Resource is added. IYP.add_to_res mat IYPBOTResource added.
Syntax for a resource tag:
Usage example:IYP.new_res IYPBOTResource is added. IYP.add_to_res mat IYPBOTResource added. IYP.add_message [do_i_know] do i know ? IYPBOTMessage added IYP.add_response [do_i_know] yes you do. IYPBOTResponse added IYPdo i know mat? IYPBOTyes you do. IYP.add_to_res iyp IYPBOTResource added. IYPdo i know iyp? IYPBOTyes you do.
What happens is that when the bot finds a resource tag in the message, it looks for the strings in the resource and checks to see if resource tag was replaced by any of these strings, would the message that users have sent fits into the category or not.

You can use the resource that was used in the message by user in the response. To do this you give an ID to each resource, and if the resource tag and ID that was used in message fits the resource tag and ID that is used in response, the user's value is used in the response.

Syntax for a resource tag with id:{resource_id}
Example:IYP.new_res IYPBOTResource is added. IYP.add_to_res mat IYPBOTResource added. IYP.add_message [do_i_know] do i know {0}? IYPBOTMessage added IYP.add_response [do_i_know] yes you do know {0} IYPBOTResponse added IYPdo i know mat? IYPBOTyes you do know mat
If a resource is used in a response but it isn't addressed by an ID (making it unable to use the resource used in message), the bot will use a random resource:

Example:IYP.new_res IYPBOTResource is added. IYP.add_to_res iyp IYPBOTResource added. IYP.add_to_res mat IYPBOTResource added. IYP.add_message [someone_i_know] get some one i know IYPBOTMessage added IYP.add_response [someone_i_know] IYPBOTResponse added IYPget some one i know IYPBOTmat IYPget some one i know IYPBOTiyp




abstract resource: Every string fits in the abstract resource, it can be used with resource ID as well.

Example:IYP.add_message [repeat] repeat: {0} and add this to end of it: {1} IYPBOTMessage added IYP.add_response [repeat] {0} , {0}{1} IYPBOTResponse added IYPrepeat: hello and add this to end of it: iyp IYPBOThello , helloiyp


NOTE: a message containing only will block all the incoming messages



Porgrams:
You can add java programs and use them in the responses. Each program at least should have a public class, you can have several functions in classes, you will call the functions in responses, and the returned string will replace the program tag in response. For a function to be callable in responses, it should have the signiture below:public static String function_name(String[] argument_name)
class name and function name should be of lower case.

Syntax to add a program:.add_program ~file_name~ code

The file name should be the same as the name of the public class in the code.

Example: IYP.add_program ~memory~ public class memory { static String data=""; public static String save (String[] args) { data=args[0]; return "Data saved"; } public static String remember(String[] args) { return data; }} IYPBOTProgram added
To use a program you have to call it in a response, to do that you have to use program tag:

Syntax for program tag~file_name.class_name.function_name({ID}, {ID}, ...)~
you need at least a resource as argument to function

Example:IYP.add_program ~memory~ public class memory { static String data=""; public static String save (String[] args) { data=args[0]; return "Data saved"; } public static String remember(String[] args) { return data; }} IYPBOTProgram added IYP.add_message [save] save: {0} IYPBOTMessage added IYP.add_response [save] ~memory.memory.save({0})~ IYPBOTResponse added IYP.add_message [read] read the data{0} IYPBOTMessage added IYP.add_response [read] ~memory.memory.remember({0})~ IYPBOTResponse added IYPsave: hello IYPBOTData saved IYPread the data IYPBOThello
NOTE: every thing except program body is saved in lower case

To save your work use:
.save db database_name.db

To load use:
.load db database_name.db

You can merge several database with:
.add_db database_name.db
This will add a data base to the currently loaded database



For a quick help you can use ?help

If you wanted to use the bot PM me on the chat so i add you to white list to allow access to commands
If you had any problem understanding any part of this please let me know thanks :D , i'd be glad if anyone wanted to try the bot :D

Source code on github:
https://github.com/iyp-programer-yeah/iyp_bot

IYP

IYP

 

WoA Day 7

And the game was supposed to finish today, but things never go the way you want :D , my friend lost his internet 6h to the end of the competition so he couldn't merge his work with me, several bugs weren't fixed and we couldn't implement cinematics that told the story, we couldn't add the soldiers, and level wasn't completely done, though this whole week was a fulfilling experience, and as my first experience with UE4 engine i learned a lot about it which made this week kinda educational, though i'd love to have an extra day to finish the game, we might still put some time to finish the game and probably will upload it later. So as for the story that wont be in the game (since we couldn't add the cinematics) it's supposed to be a guy who lived a happy life with his family and was killed in an accident and was taken to a lab that did experiments on the dead, one day an earth quake causes the malfunction of the cell-doors and let all the zombies out the main character is supposed to fight the zombies and soldiers and find his way out of the island where the lab is located. Well sadly this story wasn't well implemented in the time givven, but we tried to get as much as possible done. As to problems of we encountered nothing was as bad as internet, and my team mate couldn't put enough time on the project cause of schools and things. The other problem would be the lack of experience on UE4 at least on my side. As to things i liked and would say was perfection, i'd say the terrain was amazingly good, good height map that was made with world machine, amazing material that paint the terrain automatically so there was no need for painting, though it lacked the foilage, which was do to lack of time. I also was satisfied with the AI, soldier and zombie AI, sadly we couldn't add the soldiers, and game crashes 'cause of a bug.
any ways here is the "game" https://drive.google.com/folderview?id=0ByRMOIz9rEO0NFlIUkg1MDlwVkE&usp=sharing , (we didnt even choose a name lol, what about nameless :D yeah that's a good name).

IYP

IYP

 

WoA Day 6

So AI is done, though lots of other things left, the character movements are almost done, here's a video, not time to write much :D , fell asleep before posting this again so tired, here's a video:

IYP

IYP

 

WoA Day 5

So zombie animations and AI is done, and soldier AI is almost finished, we added aiming and gun to the main character, and some other little stuff. hopefully i'll finish AI tomorrow, and i'll get to other stuff, so there are 3 videos to share :D , and here they are: (sorry couldn't get them to youtube, been really busy)



IYP

IYP

 

WoA Day 4

So we added the character and zombies today, and worked on zombie AI which is almost done, and working to some degree, we need to add the little things to the level and the soldier AI, there really is nothing to show, since the zombie animation isn't completely done, so I couldn't implement the AI on zombies, hopefully i'll do it tomorrow. so a little to show today, just the models so here they are (Also decided to move from FPS to third person shooter :D ):

IYP

IYP

 

Woa Day 3

didn't get much done today, mostly problems with GitHub, though we did some adjustments to lighting, I worked on the AI a little, nothing to show, though, so there wont be any thing really new except newt's cinematic scenes that I haven't sent pictures of, so here you go:

IYP

IYP

 

Woa Day2

So I guess the level design is almost over there is only one cinematic left and the lab is almost done, progress was really slow today, hopefully from tomorrow we will work on AI and game play. so these are the pictures:

IYP

IYP

 

Woa Day 1

I gotta say i'm not going to give away the main story of our game :D , but it's gonna be an FPS-Adventure game and zombies in it. But about the progress I gotta say todays was a busy day, we made the menus for the game, 2 main terrains and a back up terrain and a lab with a helipad and helicopter. And added some little stuff to the map that you're gonna see in the pics. Also worked out a cinematic that is supposed to be a part of the main character's lost memory. any ways here are the pics: https://imgur.com/a/ctN8p (sorry nothing on these journals work so I had to put oictures on an imgur album)

IYP

IYP

 

Shadow Mapping Part 5 PCSS

[font=calibri]It's been a while since my last post. I've been busy with the university and stuff and couldn't make much time to work on my journal, though I've tried a lot of new things, so I decided to share some of them with you guys.[/font] [font=calibri]If you don't know what PCSS (Percentage-closer Soft Shadows) is, it's a technique NVidia came up with to generated soft shadows with photorealistic variable penumbra size. [/font][font=calibri]I tried to implement this method a while back but I couldn't get as good results as they got on their paper so I though it was just the paper exaggeration that most often happens on graphics papers. So I Forgot about it, until a few days ago that I was playing GTA V on my friends PC and noticed there is PCSS option on the graphics settings, so that was a motivation to re-implement it, and it actually worked well this time.[/font] [font=calibri]PCSS (Percentage-closer Soft Shadows)[/font]
[font=calibri][/font]
[font=calibri]There are two points about PCSS that makes it convenient. First that it uses normal shadow maps and second that it utilizes PCF (Percentage-closer Filtering, described on previous posts) to filter the shadow map.[/font] [font=calibri]Let's say you want to calculate the value SE the density of the light that the fragment R receives. First you have to calculate PW the width of the penumbra which is done on the blocker search step, and then you have to filter the shadows which is done using variable kernel size PCF.[/font] [font=calibri]So PCSS is consisted of two steps, blocker search and PCF filtering.[/font] [font=calibri]Blocker Search[/font] [font=calibri]On blocker search the algorithm calculates the average distance of the object blocking light from light. This is performed by projecting F to shadow map space. Consider SMC as the shadow map coordinates of R, the algorithm read texels around SMC and if the distance of a texel from light was less than R's distance from the light, RD, that texel is considered a blocker texel. The algorithm calculates the blocker distance from the light, BD, by averaging the blocker texels' distance from the light.[/font] [font=calibri]Using the light's Width LW, BD and RD the penumbra width, PW, is calculated as below:[/font]
[font=calibri][/font]
[font=calibri][/font]
[font=calibri]After calculation of PW, kernel size is calculated for the PCF step, this is done by calculating penumbra width after being projected on the on to shadow map space.[/font] [font=calibri]PCF[/font] [font=calibri]On this step a simple bilinear PCF (described on previous posts) is applied to filter the edges, the kernel size is available from the previous step.[/font] [font=calibri]Implementation[/font] [font=calibri]Shadow map shader[/font][code=:0]//Vertex Shader#version 450in vec3 Vertex;out vec4 Position;uniform mat4 ProjMatrix;uniform mat4 ViewMatrix;uniform mat4 ModelMatrix;void main(){ Position=ViewMatrix*(ModelMatrix*vec4(Vertex,1)); gl_Position=ProjMatrix*Position;}//Fragment Shader#version 450out float Output1;in vec4 Position;void main(){ Output1=length(Position.xyz);}
[font=calibri]Back face is rendered.[/font] [font=calibri]Blocker search function[/font]float AvarageShadowCasterDepth(vec2 ShadowCoord, float Depth, sampler2DArray ShadowMap, int TextID, int R, ivec2 TextureSize)//reciever's coordinate on shadow map, reciever's depth, shadow map sampler, current layer of the shadow map array sampler, radiouse of the blocker search, size of the shadow map texture{ float RetValue=0.0f; float Count=0.0f; const int start=-R/2;; const int end=start+R; for (int IS=0;IS16)//early bail return RetValue/Count; } } return (Count!=0.0f)?RetValue/Count:0.0f;}
Results






Thanks for reading :)

IYP

IYP

 

Shadow Mapping Part 4 Bilinear PCF

So I was off writing for a while but thanks to encouragements of my friend ND I'm back on. On this post I'm going to write about bilinear PCF and usage of Gaussian blur instead of normal PCF. [font=arial]Bilinear PCF[/font] [font=arial]I've been seeing people using D3D getting cool results using normal PCF (as they'd say it's normal), then later I found out about this function in D3D doing a bilinear shadow test, I couldn't find any similar function on OpenGL so I decided to make my own bilinear shadow test function (which I think is quite similar to the one in D3D regarding performance and result).[/font] [font=arial]So the main idea is to do the normal PCF only using the results off the bilinear shadow test instead of normal shadow test. So first thing I did was to make my own bilinear shadow test.[/font] [font=arial][/font]
[font=arial][/font]
[font=arial][/font]
[font=arial][/font] [font=arial]Figure 1-1 The bilinear PCF with 9 samples on a 4096*4096 shadow map[/font] [font=arial][/font]
[font=arial][/font]
[font=arial][/font]
[font=arial][/font] [font=arial]Figure 1-2 Normal PCF with 9 samples one 4096*4096 shadow map[/font]
[font=arial]Bilinear Shadow Test [/font] [font=arial]When you do a simple texture look up, you sample 4 texels and then you apply a bilinear interpolation on the results and that the final color of the texture, so the idea of the bilinear shadow test is to apply the bilinear interpolation after the shadow test and not straightly after sampling the shadow map.[/font]
[font=arial][/font] [font=arial]Figure 2-1 After sampling the 4 texel bilinear interpolation is done regarding the position of the asked point.[/font]
[font=arial]To do the bilinear shadow test we first take 4 sample of 4 texels around a given point, then we perform a shadow test for each texel, then we apply a bilinear interpolation on the results of the shadow tests (shadow test result in either 1 or 0 representing lit and unlit texels).[/font] [font=arial]So for the bilinear shadow text we have 3 steps:[/font] [font=arial]1- Sample 4 texels[/font]
[font=arial]2- Perform shadow test on the samples[/font]
[font=arial]3- Apply bilinear interpolation[/font] [font=arial] Sampling 4 Texels[/font] [font=arial]In my implementation I used textureGather to sample 4 texel, to use textureGather we need to have the coordinates of the texel 1 (in Figure 2-1), to calculate coordinates of this texel we need the size of the texture, and we only have the normalized coordinates of the point that we want it's shadow condition.[/font] [font=arial]The code below shows how the coordinates are calculated and the texels are sampled:[/font]vec2 samples;samples=floor(vec2(TextureSize)*ShadowCoord);vec4 samplesDepth=textureGather(ShadowMap,vec3(samples/vec2(TextureSize),i),0);
[font=arial]samplesDepth contains the values of our samples.[/font]
[font=arial] Performing Shadow Tests[/font] [font=arial]I do a simple shadow test in this part for each of the texels, the code below shows how the test is done:[/font]vec4 SamplesShadowVals; float Offseted_Depth=Depth-0.2f/(MaxRad-1.0f); if (Offseted_Depth>samplesDepth.w)SamplesShadowVals[0]=0.0f;elseSamplesShadowVals[0]=1.0f; if (Offseted_Depth>samplesDepth.z)SamplesShadowVals[1]=0.0f;elseSamplesShadowVals[1]=1.0f; if (Offseted_Depth>samplesDepth.x)SamplesShadowVals[2]=0.0f;elseSamplesShadowVals[2]=1.0f; if (Offseted_Depth>samplesDepth.y)SamplesShadowVals[3]=0.0f;elseSamplesShadowVals[3]=1.0f;
[font=arial]The SamplesShadowVals contain the final test values that we'll use to do the interpolation. Additionally you can use the code below to avoid some artifacts:[/font]
if (samples.x+1.0f>TextureSize.x){SamplesShadowVals[1]=1.0f;SamplesShadowVals[3]=1.0f;}if (samples.y+1.0f>TextureSize.y){SamplesShadowVals[2]=1.0f;SamplesShadowVals[3]=1.0f;}
[font=arial]This part sets the texels that are out of the texture as shadowed.[/font]
[font=arial]Applying Bilinear Interpolation[/font] [font=arial]To apply the bilinear interpolation we need the distance of the asked point from the texel 1 (in the figure 2-1). Then this distance is normalize in a way that the distance of the texel 4 (shown in Figure 2-1) will be (1, 1), this is done with the code below:[/font]vec2 lerpVals=vec2(TextureSize)*ShadowCoord.xy-samples;
[font=arial]lerpvals is the normalized 2D vector from the texel 1 to asked point (shown in Figure 2-1), this vector is used to perform the bilinear interpolation in the code below:[/font]float draw_val=1;draw_val=mix(mix(SamplesShadowVals[0],SamplesShadowVals[1],lerpVals.x),mix(SamplesShadowVals[2],SamplesShadowVals[3],lerpVals.x),lerpVals.y);
[font=arial]The last line does the bilinear interpolation and draw_val is the final value of the test that is between 0 and 1.[/font]
[font=arial]How To Do Bilinear Interpolation[/font] [font=arial]If you already know how to do a bilinear interpolation you don't have to read this par.[/font] [font=arial]So considering samples to be S1, S2, S3 and S4 (representing the texel 1 to texel 4) we do a linear interpolation between S1 and S2 and we call it V1 then we do another linear interpolation between S3 and S4 and we call it V2 and then we simply do another linear interpolation but this time between V1 and V2. The first two of the interpolation is done using the x value of the lerpvals and the last one is done using the y value of the lerpvals. (interpolation is done on the results of the shadow tests of the samples not the samples themselves)[/font] [font=arial]Final Bilinear Test Function [/font]
float BilinearShadowTest(vec2 ShadowCoord, float Depth, sampler2DArray ShadowMap, int i, float MaxRad, ivec2 TextureSize){vec2 samples;samples=floor(vec2(TextureSize)*ShadowCoord);vec4 samplesDepth=textureGather(ShadowMap,vec3(samples/vec2(TextureSize),i),0); vec4 SamplesShadowVals;float Offseted_Depth=Depth-0.2f/(MaxRad-1.0f);if (Offseted_Depth>samplesDepth.w)SamplesShadowVals[0]=0.0f;elseSamplesShadowVals[0]=1.0f; if (Offseted_Depth>samplesDepth.z)SamplesShadowVals[1]=0.0f;elseSamplesShadowVals[1]=1.0f; if (Offseted_Depth>samplesDepth.x)SamplesShadowVals[2]=0.0f;elseSamplesShadowVals[2]=1.0f; if (Offseted_Depth>samplesDepth.y)SamplesShadowVals[3]=0.0f;elseSamplesShadowVals[3]=1.0f; if (samples.x+1.0f>TextureSize.x){SamplesShadowVals[1]=1.0f;SamplesShadowVals[3]=1.0f;} if (samples.y+1.0f>TextureSize.y){SamplesShadowVals[2]=1.0f;SamplesShadowVals[3]=1.0f;}vec2 lerpVals=vec2(TextureSize)*ShadowCoord.xy-samples;float draw_val=1;draw_val=mix(mix(SamplesShadowVals[0],SamplesShadowVals[1],lerpVals.x),mix(SamplesShadowVals[2],SamplesShadowVals[3],lerpVals.x),lerpVals.y);return draw_val;}
[font=arial]This function is written for the 2D texture array, you can simply change it to work on normal textures.[/font]
[font=arial]This function should be as efficient as a single normal texture look up since it's the same numbers of samples and same operation.[/font]
[font=arial] Bilinear PCF[/font] [font=arial]Well now that we have a function for the bilinear shadow test what we do is simply replace the normal shadow test in normal PCF with the bilinear shadow test.[/font] [font=arial]Using Gaussian Blur As Filter [/font] [font=arial]Well it's not like I do a two pass filter, well that could be done but that'd be deferred shadow mapping, so what I do is to take the normal 9 samples that I took for the PCF and then I use the Gaussian function to determine the weights with which the samples will get mixed.[/font] [font=arial]This is simply done by precalculating the weights and sending it to the shader, I used UBOs in my implementation to send both position of the samples and their weights.[/font] [font=arial]This is the Gaussian function:[/font] [font=arial][/font] [font=arial]In my implementation I used the vales below:[/font] [font=arial][/font] [font=arial]The weights are calculated as below:[/font] [font=arial]Considering we have the below numbers as the Gaussian function:[/font] [font=arial][[/font] [font=arial]We have the weights for the texels as:[/font] [font=arial][/font] [font=arial]Here are some results:[/font] [font=arial][/font]
[font=arial][/font]
[font=arial][/font]
[font=arial][/font] [font=arial]Figure 8-1 Result of the bilinear shadow test with Gaussian blur as filter, shadow maps are 4096*4096[/font] [font=arial][/font]
[font=arial][/font]
[font=arial][/font]
[font=arial][/font] [font=arial]Figure 8-2 Results of the normal shadow test with Gaussian blur as filter, shadow maps are 4096*4096[/font] [font=arial]Thanks for reading :) feel free to ask about the post in comments, thanks again[/font]

IYP

IYP

 

CodeMus Team: The Week of Awesome Day 7

Ok WoA is over, am I satisfied? No, since we could do a lot better, some little thing that could get us near to perfection was ignored, well I did my best not to ignore them but it wasn't just me, so it should have been ignored. the game is up now for any player, I'm too tired so it's again going to be short. Try playing the game, it needs a lot of thinking and puzzle solving, finding the hint and understanding their meanings, I dare to say it's not a game every one can play.

https://drive.google.com/file/d/0ByRMOIz9rEO0RjNEUE9EVmlfYWs/view?usp=sharing

IYP

IYP

 

CodeMus Team: The Week of Awesome Day 6

Ok, we're almost done, but I think we haven't implemented the game idea that good, so yeah... but we have some good stuff going on, like digging where ever you want filling where you dig, those kinda cool stuff that actually suit the game play as the game play intends to make things look realistic, but still I don't think we have implemented the puzzle part of the game well enough, as always I'll send some screen shot.








IYP

IYP

 

CodeMus Team: The Week of Awesome Day 5

Ok, it's the 5th day and I'm too buzzy with stuff, so I'm going to be short, so the progress today was mostly on AI and animations, the second world is almost done, we may finish the first level of the game in 3 or 4h from now. So that's it, ill make some screen shots.



IYP

IYP

 

CodeMus Team: The Week of Awesome Day 4

Ok today we made quite a lot of progress, some of the AI is done and the death realm map is almost done, some graphics improvement was also made, so I'm not telling much check the screen shots. Also some unknown creatures were made and are functional, but wait for the game to find out, or will you?




IYP

IYP

 

CodeMus Team: The Week of Awesome Day 3

Today we made a slight change in the game play, it was so slight that the game play changed from funny to scary and mysterious. Even though the change was so "slight" we didn't need to change any thing since no AI was done. now the story is:

You're wife was a biologist, she moved to this forest to study some rare creatures, never seen but talked about. When after 4 months
she stopped contacting, no one dared to help you find her, you were alone but you couldn't give up on her, so you moved to the forest
where she last contacted you. You came across a cemetery, there you felt dizzy and fell and you saw her telling you "dig a grave, bury yourself".

And yes there starts the story, but I'm not going to give it away. We needed to add a new map (not that complex), there is a screen shot down there. The idea of the gameplay is to solve the mystery, so it's more like a puzzle, and also a little real life like, think you're in the same situation, would some one tell you what to do?, no so the game will be built that way, you have a map, sounds, and the visual sense, don't worry intuition will be represented by the music so we don't leave any thing behind, now with theses senses you have to find a way to solve the mystery, use your brain.

You will be quite free but there is not much to do, so we will have a simple AI (and that why we changed the game play in the first place). Now how is the death useful? and what will you do to solve the mystery? well that's a mystery, so wait for the game.


IYP

IYP

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!