Display Name : the name that will be displayed in Custom Build Rules. ex) PIDL Rule
File Name : create a rule filename. ex) PIDL_Custom_Build_Rule
Directory : specify the location where the rule file will be saved. ex) C:\XXX\YYY
After completing the settings, a PIDL Build Rule is added to Custom Build Rules as follows.
Check out the newly created rules file, and when you create a file in your project, you will see that the Build Tool is automatically selected as PIDL.
Using the Command Prompt (Command Prompt, cmd.exe)
If the PIDL AddOn or Custom Build Rules are not available, you can compile via command prompt on the Windows OS.
For cs, you can compile it with the PIDL -cs command.
PIDL.exe is located in the util folder of the ProudNet installation path.
Both Debug and Release must be set to the same.
Do not follow the example with the path to PIDL.exe, but set the path to PIDL in the path where ProudNet is installed.
In this example, we created a Common - PIDL folder and specified it as the PIDL compile path (outdir).
Please do not follow that include path, but specify the required path for your project.
Check out the generated source files and include them in the Visual Studio project properties window to use them when you build.
Three files are required: .props, .targets, and .xml. Unable to use the .rules file created from a previous version
Two ways to use customizations
1. After creating a project in Visual Studio 2005 or 2008, create a .rules file and set it to use, then convert the project to version 2010 or later.
: When converting a project, .rules files are automatically converted to .props, .targets, and .xml.
2.Create .props, .targets, and .xml files and write your own XML code to use them
Visual Studio 2005, 2008 and Visual Studio 2010 and later versions have different defined macros.
If the rule is not written in the macros used in the Visual Studio 2010 and later versions, you must convert to the macros used in the Visual Studio 2010 and later versions or modify .props, .targets, and .xml respectively after the conversion.
Using include or import in PIDL content
As you develop your programs, you may want to include or import statements in your .pidl file.
To do this, use the following inside your PIDL content.
#include"a/b/c.h"// for C++ language. semicolon is mandatory!
importcom.mycompany.mygame; // for Java
Marshaling
Using custom class types for RMI
// MyType.hnamespace Proud{ // The contents of the called RMI function are converted into a string and output. // Useful for creating logs.voidAppendTextOut(String&a,constMyType&b); // Reads the contents of a custom type from the message buffer.CMessage&operator>>(CMessage&a,MyType&b); // Put the contents of the custom type into the message buffer.CMessage&operator<<(CMessage&a,constMyType&b);}
Inside ProudNet's RMI functionality, we are using Proud.CMessage. And RMI parameters are marshaled through overloading of the above functions. Proud.CMessage is being used here.
Proud.CMessage holds the message data used by ProudNet to turn RMIs into messages or read parameters from messages, and is used as a stream object.
An example of implementing a stream in a marshaling function is as follows.
namespace Proud{CMessage&operator>>(CMessage&a,MyType&b) { a>>b.x,b.y>>b.z>>b.w;return a; }CMessage&operator<<(CMessage&a,constMyType&b) { // Do not use a.UseInternalBuffer()! a<<b.x,b.y<<b.z<<b.w;return a; }voidAppendTextOut(String&a,constMyType&b) { String f;f.Format(L"{x=%f,y=%f,z=%f,w=%f}",b.x,b.y,b.z,b.w); a+=f; }}
Finally, before including the proxy and stub files created by the PIDL compiler, the header file where the above overloaded methods are declared must be included first.
See <installed folder>/sample/CustomTypeMarshal or <Sample/CasualGame/GCServer/FarmCommon.h> for examples of actual implementations. To verify that your own marshalling functionality works, use Proud.TestMarshal().
After serialising and sending the CFastArray on the C++ side, the C# side receives it, deserializes it, and uses it to proceed to step 5.
In this example, the C# client calls an RMI called Ping, and the C++ server that receives it calls the StudentList RMI, passing a CFastArray in C++ to the C# side, which receives it as a List.
Marshalling in C# is developed by inheriting, implementing, and replacing Nettention.Proud.Marshaler and works with the keyword marshaler(cs)=CsClient.MyMarshaler declared in the PIDL without having to do it yourself.
namespaceCsClient{classMyMarshaler:Nettention.Proud.Marshaler {publicstaticboolRead(Message msg,outList<Student> students) { students =null;if (!msg.ReadScalar(outvar size)) {returnfalse; } students =newList<Student>();for (int i =0; i < size; ++i) {Student s =newStudent();if (!msg.Read(outs.Name)) { returnfalse; }if (!msg.Read(outs.ID)) {returnfalse; }if (!msg.Read(outs.Kor)) {returnfalse; }if (!msg.Read(outs.Eng)) {returnfalse; }if (!msg.Read(outs.Mat)) {returnfalse; }students.Add(s); }returntrue; }publicstaticvoidWrite(Message msg,List<Student> students) {msg.WriteScalar(students.Count);for (int i =0; i <students.Count; ++i) {msg.Write(students[i].Name);msg.Write(students[i].ID);msg.Write(students[i].Kor);msg.Write(students[i].Eng);msg.Write(students[i].Mat); } } }}
g_S2CStub.StudentList= (remote, rmiContext, students) =>{lock(g_lock) { // You can do this by using students passed into List<Student>.foreach (var s in students) {Console.WriteLine(s.ToString()); } }returntrue;};
- Marshaling methods based on conditions
There are ways to marshal information about a character in an RMI parameter where each field is valid or invalid for different types of characters. You can implement different marshalling using switch/case statements or polymorphism of objects.
Below is an example of using swich/case.
➡️ example of using swich/case
namespace Proud{enumUnitType { Zergling, // StarCraft's juggling (ground type normal attack unit) Queen, // Queen of Starcraft (Flying Special Technology Use Unit) Broodling // A short-lived attack unit created by Queen using her brudling skills };structUnit { UnitType m_type; // Unit's type Vector2D m_position; // Unit;s locationint m_energy; // Unit's energy (or mana) reserves -> Queen onlyfloat m_lifeTime; // Unit's survival time limit -> Bridling onlyint m_attackPower; // Unit's attack power->Juggling only };CMessage&operator<<(CMessage& msg,constUnit& unit) { msg<<unit.m_type<<unit.m_position;switch(unit.m_type) {case Zergling: msg<<unit.m_attackPower;break;case Queen: msg<<unit.m_energy;break;case Broodling: msg<<unit.m_lifeTime;break; }return msg; }CMessage&operator>>(CMessage& msg,Unit& unit) { msg>>unit.m_type>>unit.m_position;switch(unit.m_type) {case Zergling: msg>>unit.m_attackPower;break;case Queen: msg>>unit.m_energy;break;case Broodling: msg>>unit.m_lifeTime;break; }return msg; }}
- Marshaling data in Bit
You can marshal data in bits to reduce the amount of data stored in the message.
Proud.CMessage has the following methods of storing data in bit units.
Method
Description
Proud.CMessage.ReadBits
Read in bit
Proud.CMessage.WriteBits
Write in bit
➡️ Example
namespace Proud{structMyType {int x,y,z; };CMessage&operator>>(CMessage&a,MyType&b) { // Use 6 bits to read the x valuea.ReadBits(b.x,6); // Use 3 bits to read the y valuea.ReadBits(b.y,3); // Use 15 bits to read the z value, i.e. sum 6+3+15=24 bits (3 bytes)a.ReadBits(b.z,15);return a; }CMessage&operator<<(CMessage&a,constMyType&b) {a.WriteBits(b.x,6); // Store x,y,z values in bits.a.WriteBits(b.y,3);a.WriteBits(b.z,15);return a; }}
Precautions of marshaling data in bits
The number of bits you want to record should not be outside the range of the actual value you want to record.
For example, if you want to write an int and the value inside is actually negative, the first bit is 1. In this case, if you try to write 31 bits or less to reduce the amount of bits, the value of the first bit will be missing, so be aware of this when doing reading/writing in bits.
- Marshaling enum types
To marshal an enum type, implement a function like the one below.
enumtype { ... } ;namespace Proud{inlineCMessage&operator<<(CMessage& a,type b) { a<<(int)b;return a; }inlineCMessage&operator>>(CMessage& a,type& b) {int x; a>>x; b=(type)x;return a; }inlinevoidAppendTextOut(String&a,type b) { String txt;txt.Format(L"%d",(int)b); a+=txt; }}
By using the macro PROUDNET_SERIALIZE_ENUM already defined in ProudNet, the above implementation can be easily accomplished as follows.
PROUDNET_SERIALIZE_ENUM(type)
- Marshaling collections (arrays, etc.)
ProudNet provides you with the ability to marshal a few basic types of collections (e.g., arrays). It supports Proud.CFastArray, Proud.CFastMap, std.vector and CAtlArray.
See the operator>>, operator<< override in marshaler.h for more details.
// Below is an example of declaring an array type in PIDL.
Foo([in] Proud::CFastArray<MyType> a, [in] std::vector<MyType> b);
If you need marshaling of collection types other than those defined by default in marshaler.h, you will need to implement an override corresponding to the collection you need. For this, see Using custom class types for RMI or marshaler.h for examples already implemented.
If you want to marshal for a collection type that ProudNet does not yet support, use the following routine as a guide to write it.
➡️ example of marshaling std.vector
namespace Proud{ // Serialisation functions available in vector // for output to streamtemplate<typenameelem>inlineCMessage&operator>>(CMessage&a, std::vector<elem> &b) { // Get the size.int size; a >> size; // Raise an exception if the size is unacceptable. // You may suspect that it has been hacked.if (size<0||size >= CNetConfig::MessageMaxLength)ThrowExceptionOnReadArray(size); // To reduce memory fragmentsb.reserve(size);b.resize(0); // Reads the array items one by one. elem e;for (int i =0;i < size;i++) { a >> e;b.push_back(e); }return a; } // Serialisation functions available // for unary item elem such as vector, list, etc. // for input from streamtemplate<typenameelem>inlineCMessage&operator<<(CMessage&a,const std::vector<elem> &b) { // Record the size of the array.int size = (int)b.size(); a << size; // Record each array argument.for (std::vector<elem>::const_iterator i =b.begin();i !=b.end();i++) { a << (*i); }return a; }template<typenameelem>inlinevoidAppendTextOut(String&a, std::vector<elem> &b) { a +=L"<vector>"; }}
List of keywords
Name of keyword
Description
Example usage
access
[C#] Setting access to proxy and stub classes in a namespace
[access=Permission name]
byval
Passing parameters as value
P2PChat([in] Proud::String a, [in, byval] int b);
global
Namespace access (currently global only)
global Simple 2000 { ... }
in
Enter parameters
Chat([in] string txt);
include
[C#] Insert include into the result
#include "a/b/c.cpp"
marshaler
[C#] Marshaller for custom types
[marshaler(cs) = SimpleCSharp.CMyMarshaler]
mutable
Passing a parameter as mutable (:=reference)
Chat([in, mutable] string txt);
private
[C#] Setting access to proxy and stub classes in a namespace
[access=private]
protected
[C#] Setting access to proxy and stub classes in a namespace
[access=protected]
public
[C#] Setting access to proxy and stub classes in a namespace
[access=public]
rename
Compiling with substitutions in a specific environment
rename cs(Proud::String, System.String);
using
[C#] Insert the using keyword in the result
using(cs) System.XXX;
[C#] To pass a variety of parameters
global Namespace name 2000
{
Function name([in] int Variable name);
}
When PIDL is written as above, the PIDL compiler compiles it as follows for the base type.
namespace Namespace name
{
class Proxy : public ::Proud::IRmiProxy
{
public:
virtual bool Function name( ::Proud::HostID remote, ::Proud::RmiContext& rmiContext , const int& Variable name) PN_SEALED;
...
}
}
If you want to change the delivery method, use the keywords below.