Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
RplDocs.c
Go to the documentation of this file.
1
11
805
905
1663
1801
1802#ifdef DOXYGEN
1803
1805class EntityWithRplProp : GenericEntity
1806{
1807 [RplProp()]
1808 bool bFlag;
1809
1810 [RplProp(onRplName: "OnIValueChanged")]
1811 int iValue;
1812
1813 [RplProp(condition: RplCondition.NoOwner)]
1814 float fValue;
1815
1816 [RplProp(customConditionName: "MyCondition")]
1817 float fCustomCondition;
1818
1819 void OnIValueChanged()
1820 {
1821 // ...
1822 }
1823
1824 // The custom condition can be helpful in places where you know that
1825 // certain values are no longer needed on the other side. This can't be
1826 // utilized to filter out connections!
1827 bool MyCondition()
1828 {
1829 return bFlag;
1830 }
1831}
1833
1834class EntityWithRplPropClass: GenericEntityClass {}
1835EntityWithRplPropClass g_EntityWithRplPropClassInst;
1836
1838class EntityWithRplRpc : GenericEntity
1839{
1840 // This is a RPC. It has to be annotated with the RplRpc attribute.
1841 [RplRpc(RplChannel.Reliable, RplRcver.Owner)]
1842 void OwnerRpc(int a)
1843 {
1844 // ...
1845 }
1846
1847 override void EOnFrame(IEntity owner, float timeSlice)
1848 {
1849 // The RPC has to be call via special method on the entity/component.
1850 Rpc(OwnerRpc, 7);
1851 // This call can have multiple outcomes depending on the role and
1852 // ownership of this instance.
1853 // 1. If this instance is the owner the RPC will be called directly
1854 // (the same way a method would be).
1855 // 2. This instance is the server and some client owns the instance.
1856 // Then the RPC would be called on the owning client.
1857 // 3. This is the client that does not own the instance. Then this
1858 // RPC would be dropped.
1859 //
1860 // You can avoid a lot of branches if you design your code around these rules.
1861
1862
1863 // You can still use the RPC method the same way as you would use any other method.
1864 // In many cases this would be the best way how to unify your logic.
1865 OwnerRpc(7);
1866 }
1867}
1869
1870class EntityWithRplRpcClass: GenericEntityClass {}
1871EntityWithRplRpcClass g_EntityWithRplRpcClassInst;
1872
1874class ComplexType
1875{
1876 bool m_Bool;
1877 int m_Int;
1878 string m_String;
1879 float m_Float;
1880 WorldTimestamp m_Timestamp;
1881 vector m_Vector;
1882
1883 // ## Extract/Inject
1884 // Extracting data from instance into snapshot, and injecting data from snapshot to instance.
1885 // Snapshot is meant to be fast to work with, so values are left uncompressed
1886 // to avoid extra work when accessing these values.
1887
1888 // ## Encode/Decode
1889 // Encoding snapshot into a packet and decoding snapshot from a packet.
1890 // Packets need to be as small as possible, so this process tries to reduce the
1891 // size as much as it can. Knowing what range of values can certain variable have and
1892 // encoding that range in minimum number of bits required is key. If you have
1893 // to assume full range of values is needed, you can use helpers for different
1894 // types that already implement those.
1895
1896 // ## EncodeDelta/DecodeDelta
1897 // Optional functions for implementing encoding of snapshot differences
1898 // (delta encoding). Encoding reads from old and new snapshots and writes differences
1899 // between them into delta-encoded packet. Decoding then reads from old snapshot
1900 // and delta-encoded packet and writes new packet based on them.
1901
1902 static bool Extract(ComplexType instance, ScriptCtx ctx, SSnapSerializerBase snapshot)
1903 {
1904 // Fill a snapshot with values from an instance.
1905 snapshot.SerializeBool(instance.m_Bool);
1906 snapshot.SerializeInt(instance.m_Int);
1907 snapshot.SerializeString(instance.m_String);
1908 snapshot.SerializeFloat(instance.m_Float);
1909 snapshot.SerializeBytes(instance.m_Timestamp, 8);
1910 snapshot.SerializeVector(instance.m_Vector);
1911 return true;
1912 }
1913
1914 static bool Inject(SSnapSerializerBase snapshot, ScriptCtx ctx, ComplexType instance)
1915 {
1916 // Fill an instance with values from snapshot.
1917 snapshot.SerializeBool(instance.m_Bool);
1918 snapshot.SerializeInt(instance.m_Int);
1919 snapshot.SerializeString(instance.m_String);
1920 snapshot.SerializeFloat(instance.m_Float);
1921 snapshot.SerializeBytes(instance.m_Timestamp, 8);
1922 snapshot.SerializeVector(instance.m_Vector);
1923 return true;
1924 }
1925
1926 static void Encode(SSnapSerializerBase snapshot, ScriptCtx ctx, ScriptBitSerializer packet)
1927 {
1928 // Read values from snapshot, encode them into smaller representation, then
1929 // write them into packet.
1930 snapshot.EncodeBool(packet); // m_Bool
1931 snapshot.EncodeInt(packet); // m_Int
1932 snapshot.EncodeString(packet); // m_String
1933 snapshot.EncodeFloat(packet); // m_Float
1934 snapshot.Serialize(packet, 8); // m_Timestamp
1935 snapshot.EncodeVector(packet); // m_Vector
1936 }
1937
1938 static bool Decode(ScriptBitSerializer packet, ScriptCtx ctx, SSnapSerializerBase snapshot)
1939 {
1940 // Read values from packet, decode them into their original representation,
1941 // then write them into snapshot.
1942 snapshot.DecodeBool(packet); // m_Bool
1943 snapshot.DecodeInt(packet); // m_Int
1944 snapshot.DecodeString(packet); // m_String
1945 snapshot.DecodeFloat(packet); // m_Float
1946 snapshot.Serialize(packet, 8); // m_Timestamp
1947 snapshot.DecodeVector(packet); // m_Vector
1948 return true;
1949 }
1950
1951 static bool SnapCompare(SSnapSerializerBase lhs, SSnapSerializerBase rhs , ScriptCtx ctx)
1952 {
1953 // Compare two snapshots and determine whether they are the same.
1954 // We have to compare properties one-by-one, but for properties with known
1955 // length (such as primitive types bool, int, float and vector), we do multiple
1956 // comparisons in single call. However, because we do not know length of string,
1957 // we use provided function which will determine number of bytes that need
1958 // to be compared from serialized data.
1959 return lhs.CompareSnapshots(rhs, 4+4) // m_Bool, m_Int
1960 && lhs.CompareStringSnapshots(rhs) // m_String
1961 && lhs.CompareSnapshots(rhs, 4+8+12); // m_Float, m_Timestamp, m_Vector
1962 }
1963
1964 static bool PropCompare(ComplexType instance, SSnapSerializerBase snapshot, ScriptCtx ctx)
1965 {
1966 // Determine whether current values in instance are sufficiently different from
1967 // an existing snapshot that it's worth creating new one.
1968 // For float or vector values, you could use some threshold to avoid creating too
1969 // many snapshots due to tiny changes in these values.
1970 return snapshot.CompareBool(instance.m_Bool)
1971 && snapshot.CompareInt(instance.m_Int)
1972 && snapshot.CompareString(instance.m_String)
1973 && snapshot.CompareFloat(instance.m_Float)
1974 && snapshot.Compare(instance.m_Timestamp, 8)
1975 && snapshot.CompareVector(instance.m_Vector);
1976 }
1977
1978 static void EncodeDelta(SSnapSerializerBase oldSnapshot, SSnapSerializerBase newSnapshot, ScriptCtx ctx, ScriptBitSerializer packet)
1979 {
1980 // Generate packet that allows other side to produce new snapshot when it already
1981 // has old one available.
1982
1983 // We write new value of bool directly, as there is no way to reduce it below one bit.
1984 // We still need to read old value from old snapshot to correctly access following
1985 // properties.
1986 bool oldBool;
1987 oldSnapshot.SerializeBool(oldBool);
1988 bool newBool;
1989 newSnapshot.SerializeBool(newBool);
1990 packet.Serialize(newBool, 1);
1991
1992 // We encode difference between old and new value of the int, and rely on the fact
1993 // that difference of these values requires fewer bits to encode than value itself.
1994 int oldInt;
1995 oldSnapshot.SerializeInt(oldInt);
1996 int newInt;
1997 newSnapshot.SerializeInt(newInt);
1998 int deltaInt = newInt - oldInt;
1999 packet.SerializeInt(deltaInt);
2000
2001 // For remaining values (string, float, vector), we use single bit to signal whether
2002 // value has changed and we only encode their new value if it is different from
2003 // the old one.
2004 string oldString;
2005 oldSnapshot.SerializeString(oldString);
2006 string newString;
2007 newSnapshot.SerializeString(newString);
2008 bool stringChanged = newString != oldString;
2009 packet.Serialize(stringChanged, 1);
2010 if (stringChanged)
2011 packet.SerializeString(newString);
2012
2013 float oldFloat;
2014 oldSnapshot.SerializeFloat(oldFloat);
2015 float newFloat;
2016 newSnapshot.SerializeFloat(newFloat);
2017 bool floatChanged = newFloat != oldFloat;
2018 packet.Serialize(floatChanged, 1);
2019 if (floatChanged)
2020 packet.Serialize(newFloat, 32);
2021
2022 // For timestamps, rounding of float will occur when absolute value of
2023 // difference is more than 16777216ms (~4.5 hours). Also, past certain
2024 // point, differences cannot be represented as 32-bit int either. Therefore,
2025 // once difference cannot be sent accurately, we instead send full value.
2026 // This might not be the best solution for extreme uses, but it demonstrates
2027 // how one can implement delta-encoding using domain-specific knowledge.
2028 WorldTimestamp oldTimestamp;
2029 oldSnapshot.SerializeBytes(oldTimestamp, 8);
2030 WorldTimestamp newTimestamp;
2031 newSnapshot.SerializeBytes(newTimestamp, 8);
2032 float deltaMs = newTimestamp.DiffMilliseconds(oldTimestamp);
2033 bool isUsingDelta = -16777216.0 <= deltaMs && deltaMs <= 16777216.0;
2034 packet.Serialize(isUsingDelta, 1);
2035 if (isUsingDelta)
2036 {
2037 int deltaMsInt = deltaMs;
2038 packet.SerializeInt(deltaMsInt);
2039 }
2040 else
2041 {
2042 packet.Serialize(newTimestamp, 64);
2043 }
2044
2045 vector oldVector;
2046 oldSnapshot.SerializeVector(oldVector);
2047 vector newVector;
2048 newSnapshot.SerializeVector(newVector);
2049 bool vectorChanged = newVector != oldVector;
2050 packet.Serialize(vectorChanged, 1);
2051 if (vectorChanged)
2052 packet.Serialize(newVector, 96);
2053
2054 // More techniques are possible when modification patterns are known. For example,
2055 // when multiple properties are always modified together, they could share single
2056 // bit that would say whether there were any changes (and new values were encoded)
2057 // or not.
2058 }
2059
2060 static void DecodeDelta(ScriptBitSerializer packet, ScriptCtx ctx, SSnapSerializerBase oldSnapshot, SSnapSerializerBase newSnapshot)
2061 {
2062 // Generate new snapshot using data from old snapshot and packet.
2063 // Note that even when value from old snapshot is not used because packet carries
2064 // new value, we always read it to make sure we correctly access following
2065 // properties that may not have changed.
2066
2067 // Bool value is read directly from packet, just as it was written.
2068 bool oldBool;
2069 oldSnapshot.SerializeBool(oldBool);
2070 bool newBool;
2071 packet.Serialize(newBool, 1);
2072 newSnapshot.SerializeBool(newBool);
2073
2074 // New value of int is reconstructed by applying delta to old value.
2075 int oldInt;
2076 oldSnapshot.SerializeInt(oldInt);
2077 int deltaInt;
2078 packet.SerializeInt(deltaInt);
2079 int newInt = oldInt + deltaInt;
2080 newSnapshot.SerializeInt(newInt);
2081
2082 // Remaining properties are reconstructed by checking whether they changed
2083 // and either reading new value from packet, or copying old value.
2084 string oldString;
2085 oldSnapshot.SerializeString(oldString);
2086 bool stringChanged;
2087 packet.Serialize(stringChanged, 1);
2088 string newString;
2089 if (stringChanged)
2090 packet.SerializeString(newString);
2091 else
2092 newString = oldString;
2093 newSnapshot.SerializeString(newString);
2094
2095 float oldFloat;
2096 oldSnapshot.SerializeFloat(oldFloat);
2097 bool floatChanged;
2098 packet.Serialize(floatChanged, 1);
2099 float newFloat;
2100 if (floatChanged)
2101 packet.Serialize(newFloat, 32);
2102 else
2103 newFloat = oldFloat;
2104 newSnapshot.SerializeFloat(newFloat);
2105
2106 WorldTimestamp oldTimestamp;
2107 oldSnapshot.SerializeBytes(oldTimestamp, 8);
2108 bool isUsingDelta;
2109 packet.Serialize(isUsingDelta, 1);
2110 WorldTimestamp newTimestamp;
2111 if (isUsingDelta)
2112 {
2113 int deltaMsInt;
2114 packet.SerializeInt(deltaMsInt);
2115 float deltaMs = deltaMsInt;
2116 newTimestamp = oldTimestamp.PlusMilliseconds(deltaMs);
2117 }
2118 else
2119 {
2120 packet.Serialize(newTimestamp, 64);
2121 }
2122 newSnapshot.SerializeBytes(newTimestamp, 8);
2123
2124 vector oldVector;
2125 oldSnapshot.SerializeVector(oldVector);
2126 bool vectorChanged;
2127 packet.Serialize(vectorChanged, 1);
2128 vector newVector;
2129 if (vectorChanged)
2130 packet.Serialize(newVector, 96);
2131 else
2132 newVector = oldVector;
2133 newSnapshot.SerializeVector(newVector);
2134 }
2135}
2137
2139class RplExampleDebugShapeClass: GenericEntityClass {}
2140RplExampleDebugShapeClass g_RplExampleDebugShapeClassInst;
2141
2142class RplExampleDebugShape : GenericEntity
2143{
2144 static const int COLOR_COUNT = 4;
2145 static const int COLORS[] = {
2146 Color.BLACK,
2147 Color.RED,
2148 Color.GREEN,
2149 Color.BLUE,
2150 };
2151
2152 private int m_Color;
2153
2154 void RplExampleDebugShape(IEntitySource src, IEntity parent)
2155 {
2156 this.SetEventMask(EntityEvent.FRAME);
2157 }
2158
2159 override void EOnFrame(IEntity owner, float timeSlice)
2160 {
2161 vector worldTransform[4];
2162 this.GetWorldTransform(worldTransform);
2163 Shape.CreateSphere(m_Color, ShapeFlags.ONCE, worldTransform[3], 0.5);
2164 }
2165
2166 bool SetColorByIdx(int colorIdx)
2167 {
2168 if (colorIdx < 0 || colorIdx >= COLOR_COUNT)
2169 return false;
2170
2171 m_Color = COLORS[colorIdx];
2172 return true;
2173 }
2174}
2176
2178class RplExample1ComponentColorAnimClass : ScriptComponentClass { }
2179RplExample1ComponentColorAnimClass g_RplExample1ComponentColorAnimClass;
2180
2181class RplExample1ComponentColorAnim : ScriptComponent
2182{
2183 // Constant specifying how often (in seconds) to change the color index. For
2184 // example, setting this to 5 will change the color index every 5 seconds.
2185 private static const float COLOR_CHANGE_PERIOD_S = 5.0;
2186
2187 // Helper variable for accumulating time (in seconds) every frame and to calculate
2188 // color index changes.
2189 private float m_TimeAccumulator_s;
2190
2191 // Color index currently used for drawing the sphere.
2192 private int m_ColorIdx;
2193
2194 override void OnPostInit(IEntity owner)
2195 {
2196 // We check whether this component is attached to entity of correct type and
2197 // report a problem if not. Once this test passes during initialization, we
2198 // do not need to worry about owner entity being wrong type anymore.
2199 auto shapeEnt = RplExampleDebugShape.Cast(owner);
2200 if (!shapeEnt)
2201 {
2202 Print("This example requires that the entity is of type `RplExampleDebugShape`.", LogLevel.WARNING);
2203 return;
2204 }
2205
2206 // We initialize shape entity to correct color.
2207 shapeEnt.SetColorByIdx(m_ColorIdx);
2208
2209 // We subscribe to "frame" events, so that we can run our logic in `EOnFrame`
2210 // event handler.
2211 SetEventMask(owner, EntityEvent.FRAME);
2212 }
2213
2214 override void EOnFrame(IEntity owner, float timeSlice)
2215 {
2216 // We calculate change of color index based on time (and configured color
2217 // change period), then apply the change in color.
2218 int colorIdxDelta = CalculateColorIdxDelta(timeSlice);
2219 ApplyColorIdxDelta(owner, colorIdxDelta);
2220 }
2221
2222 private int CalculateColorIdxDelta(float timeSlice)
2223 {
2224 // We first accumulate time and then calculate how many color change periods
2225 // have occurred, giving us number of colors we've cycled through.
2226 m_TimeAccumulator_s += timeSlice;
2227 int colorIdxDelta = m_TimeAccumulator_s / COLOR_CHANGE_PERIOD_S;
2228
2229 // We remove full periods from the accumulator, only carrying over how much
2230 // time from current period has elapsed.
2231 m_TimeAccumulator_s -= colorIdxDelta * COLOR_CHANGE_PERIOD_S;
2232
2233 return colorIdxDelta;
2234 }
2235
2236 private void ApplyColorIdxDelta(IEntity owner, int colorIdxDelta)
2237 {
2238 // If there is no change to color index, we do nothing.
2239 if (colorIdxDelta == 0)
2240 return;
2241
2242 // We calculate new color index.
2243 int newColorIdx = (m_ColorIdx + colorIdxDelta) % RplExampleDebugShape.COLOR_COUNT;
2244
2245 // We check also new color index, since shorter periods and lower frame-rate
2246 // may result in new and old color index values being the same.
2247 if (newColorIdx == m_ColorIdx)
2248 return;
2249
2250 // Now we can update the color index ...
2251 m_ColorIdx = newColorIdx;
2252
2253 // ... and set new color based on new color index value.
2254 RplExampleDebugShape.Cast(owner).SetColorByIdx(m_ColorIdx);
2255 }
2256}
2258
2260class RplExample2ComponentColorAnimClass : ScriptComponentClass { }
2261RplExample2ComponentColorAnimClass g_RplExample2ComponentColorAnimClass;
2262
2263class RplExample2ComponentColorAnim : ScriptComponent
2264{
2265 private static const float COLOR_CHANGE_PERIOD_S = 5.0;
2266
2267 private float m_TimeAccumulator_s;
2268
2270 // We mark color index as replicated property using RplProp attribute, making
2271 // it part of replicated state. We also say we want OnColorIdxChanged function
2272 // to be invoked whenever replication updates value of color index.
2273 [RplProp(onRplName: "OnColorIdxChanged")]
2274 private int m_ColorIdx;
2276
2278 override void OnPostInit(IEntity owner)
2279 {
2280 auto shapeEnt = RplExampleDebugShape.Cast(owner);
2281 if (!shapeEnt)
2282 {
2283 Print("This example requires that the entity is of type `RplExampleDebugShape`.", LogLevel.WARNING);
2284 return;
2285 }
2286
2287 shapeEnt.SetColorByIdx(m_ColorIdx);
2288
2289 // We must belong to some RplComponent in order for replication to work.
2290 // We search for it and warn user when we can't find it.
2291 auto rplComponent = BaseRplComponent.Cast(shapeEnt.FindComponent(BaseRplComponent));
2292 if (!rplComponent)
2293 {
2294 Print("This example requires that the entity has an RplComponent.", LogLevel.WARNING);
2295 return;
2296 }
2297
2298 // We only perform simulation on the authority instance, while all proxy
2299 // instances just show result of the simulation. Therefore, we only have to
2300 // subscribe to "frame" events on authority, leaving proxy instances as
2301 // passive components that do something only when necessary.
2302 if (rplComponent.Role() == RplRole.Authority)
2303 {
2304 SetEventMask(owner, EntityEvent.FRAME);
2305 }
2306 }
2308
2309 override void EOnFrame(IEntity owner, float timeSlice)
2310 {
2311 int colorIdxDelta = CalculateColorIdxDelta(timeSlice);
2312 ApplyColorIdxDelta(owner, colorIdxDelta);
2313 }
2314
2315 private int CalculateColorIdxDelta(float timeSlice)
2316 {
2317 m_TimeAccumulator_s += timeSlice;
2318 int colorIdxDelta = m_TimeAccumulator_s / COLOR_CHANGE_PERIOD_S;
2319 m_TimeAccumulator_s -= colorIdxDelta * COLOR_CHANGE_PERIOD_S;
2320 return colorIdxDelta;
2321 }
2322
2324 private void ApplyColorIdxDelta(IEntity owner, int colorIdxDelta)
2325 {
2326 if (colorIdxDelta == 0)
2327 return;
2328
2329 int newColorIdx = (m_ColorIdx + colorIdxDelta) % RplExampleDebugShape.COLOR_COUNT;
2330 if (newColorIdx == m_ColorIdx)
2331 return;
2332
2333 // Update replicated state with results from the simulation.
2334 m_ColorIdx = newColorIdx;
2335
2336 // After we have written new value of color index, we let replication know
2337 // that there are changes in our state that need to be replicated to proxies.
2338 // Without this call, even if we change our color index, new value would not
2339 // be replicated to proxies.
2340 Replication.BumpMe();
2341
2342 // Presentation of replicated state on authority.
2343 RplExampleDebugShape.Cast(owner).SetColorByIdx(m_ColorIdx);
2344 }
2345
2346 // Presentation of replicated state on proxy.
2347 private void OnColorIdxChanged()
2348 {
2349 RplExampleDebugShape.Cast(GetOwner()).SetColorByIdx(m_ColorIdx);
2350 }
2352}
2354
2356class RplExample3ComponentColorAnimClass : ScriptComponentClass { }
2357RplExample3ComponentColorAnimClass g_RplExample3ComponentColorAnimClass;
2358
2359class RplExample3ComponentColorAnim : ScriptComponent
2360{
2361 [RplProp(onRplName: "OnColorIdxChanged")]
2362 private int m_ColorIdx;
2363
2364 override void OnPostInit(IEntity owner)
2365 {
2366 auto shapeEnt = RplExampleDebugShape.Cast(owner);
2367 if (!shapeEnt)
2368 {
2369 Print("This example requires that the entity is of type `RplExampleDebugShape`.", LogLevel.WARNING);
2370 return;
2371 }
2372
2373 shapeEnt.SetColorByIdx(m_ColorIdx);
2374
2375 auto rplComponent = BaseRplComponent.Cast(shapeEnt.FindComponent(BaseRplComponent));
2376 if (!rplComponent)
2377 {
2378 Print("This example requires that the entity has an RplComponent.", LogLevel.WARNING);
2379 return;
2380 }
2381 }
2382
2383 void NextColor()
2384 {
2385 m_ColorIdx = (m_ColorIdx + 1) % RplExampleDebugShape.COLOR_COUNT;
2386 Replication.BumpMe();
2387 RplExampleDebugShape.Cast(GetOwner()).SetColorByIdx(m_ColorIdx);
2388 }
2389
2390 private void OnColorIdxChanged()
2391 {
2392 RplExampleDebugShape.Cast(GetOwner()).SetColorByIdx(m_ColorIdx);
2393 }
2394}
2395
2396class RplExample3SystemClass : ScriptComponentClass { }
2397RplExample3SystemClass g_RplExample3SystemClassInst;
2398
2399class RplExample3System : ScriptComponent
2400{
2401 static const ResourceName s_ControllerPrefab = "{65B426E2CD4049C3}kroslakmar/RplExampleController.et";
2402 static const ResourceName s_SpherePrefab = "{1AD0012447ACCE3F}kroslakmar/RplExampleShape.et";
2403
2404 ref RplExample3SessionListener m_SessionListener = new RplExample3SessionListener(this);
2405 ref map<RplIdentity, RplExample3Controller> m_Controllers = new map<RplIdentity, RplExample3Controller>();
2406
2407 ref array<RplExample3ComponentColorAnim> m_Spheres = new array<RplExample3ComponentColorAnim>();
2408
2409 override void OnPostInit(IEntity owner)
2410 {
2411 if (g_Game.InPlayMode())
2412 SetEventMask(owner, EntityEvent.INIT);
2413 }
2414
2415 override void EOnInit(IEntity owner)
2416 {
2417 RplMode mode = RplSession.Mode();
2418 if (mode != RplMode.Client)
2419 {
2420 RplSession.RegisterCallbacks(m_SessionListener);
2421 }
2422
2423 if (mode == RplMode.None || mode == RplMode.Listen)
2424 {
2425 RplExample3Controller controller = NewController(RplIdentity.Local());
2426 controller.RplGiven(null);
2427 }
2428
2429 Resource prefab = Resource.Load(s_SpherePrefab);
2430 EntitySpawnParams spawnParams = new EntitySpawnParams();
2431 spawnParams.TransformMode = ETransformMode.WORLD;
2432 owner.GetWorldTransform(spawnParams.Transform);
2433 float xBase = spawnParams.Transform[3][0];
2434 float yBase = spawnParams.Transform[3][1] + 2.0;
2435 for (int y = -1; y <= 1; y++)
2436 for (int x = -1; x <= 1; x++)
2437 {
2438 spawnParams.Transform[3][0] = xBase + x;
2439 spawnParams.Transform[3][1] = yBase + y;
2440 IEntity ent = g_Game.SpawnEntityPrefab(prefab, owner.GetWorld(), spawnParams);
2441 m_Spheres.Insert(RplExample3ComponentColorAnim.Cast(
2442 ent.FindComponent(RplExample3ComponentColorAnim)
2443 ));
2444 }
2445 }
2446
2447 RplExample3Controller NewController(RplIdentity identity)
2448 {
2449 ref Resource controllerPrefab = Resource.Load(s_ControllerPrefab);
2450 auto controller = RplExample3Controller.Cast(
2451 g_Game.SpawnEntityPrefab(controllerPrefab, GetOwner().GetWorld(), null)
2452 );
2453 controller.m_System = this;
2454 m_Controllers.Set(identity, controller);
2455
2456 return controller;
2457 }
2458
2459 void DeleteController(RplIdentity identity)
2460 {
2461 auto controller = m_Controllers.Get(identity);
2462 delete controller;
2463 m_Controllers.Remove(identity);
2464 }
2465
2466 void ChangeColor(int idx)
2467 {
2468 m_Spheres[idx].NextColor();
2469 }
2470}
2471
2472class RplExample3SessionListener: RplSessionCallbacks
2473{
2474 RplExample3System m_System;
2475
2476 void RplExample3SessionListener(RplExample3System system)
2477 {
2478 m_System = system;
2479 }
2480
2481 override void EOnConnected(RplIdentity identity)
2482 {
2483 RplExample3Controller controller = m_System.NewController(identity);
2484 auto rplComponent = BaseRplComponent.Cast(controller.FindComponent(BaseRplComponent));
2485 rplComponent.Give(identity);
2486 }
2487
2488 override void EOnDisconnected(RplIdentity identity)
2489 {
2490 m_System.DeleteController(identity);
2491 }
2492};
2493
2494class RplExample3ControllerClass : GenericEntityClass {}
2495RplExample3ControllerClass g_RplExample3ControllerClassInst;
2496
2497class RplExample3Controller : GenericEntity
2498{
2499 static const KeyCode s_KeyMap[] = {
2500 KeyCode.KC_NUMPAD1,
2501 KeyCode.KC_NUMPAD2,
2502 KeyCode.KC_NUMPAD3,
2503 KeyCode.KC_NUMPAD4,
2504 KeyCode.KC_NUMPAD5,
2505 KeyCode.KC_NUMPAD6,
2506 KeyCode.KC_NUMPAD7,
2507 KeyCode.KC_NUMPAD8,
2508 KeyCode.KC_NUMPAD9,
2509 };
2510
2511 RplExample3System m_System;
2512 int m_IsDownMask = 0;
2513
2514 bool RplGiven(ScriptBitReader reader)
2515 {
2516 if (false)
2517 {
2519 }
2520 else
2521 {
2522 SetEventMask(EntityEvent.FIXEDFRAME);
2523 }
2524 return true;
2525 }
2526
2527 override void EOnFrame(IEntity owner, float timeSlice)
2528 {
2529 foreach (int idx, KeyCode kc : s_KeyMap)
2530 {
2531 int keyBit = 1 << idx;
2532 bool isDown = Debug.KeyState(kc);
2533 bool wasDown = (m_IsDownMask & keyBit);
2534 if (isDown && !wasDown)
2535 Rpc(Rpc_ChangeColor_S, idx);
2536
2537 if (isDown)
2538 m_IsDownMask |= keyBit;
2539 else
2540 m_IsDownMask &= ~keyBit;
2541 }
2542 }
2543
2544 [RplRpc(RplChannel.Reliable, RplRcver.Server)]
2545 void Rpc_ChangeColor_S(int idx)
2546 {
2547 if (idx < 0 || idx >= 9)
2548 return;
2549
2550 m_System.ChangeColor(idx);
2551 }
2552
2553 override void EOnFixedFrame(IEntity owner, float timeSlice)
2554 {
2555 int isDownMask = 0;
2556 int keyBit = 1;
2557 foreach (KeyCode kc : s_KeyMap)
2558 {
2559 if (Debug.KeyState(kc))
2560 isDownMask |= keyBit;
2561
2562 keyBit <<= 1;
2563 }
2564 Rpc(Rpc_OwnerInputs_S, isDownMask);
2565 }
2566
2567 [RplRpc(RplChannel.Unreliable, RplRcver.Server)]
2568 void Rpc_OwnerInputs_S(int isDownMask)
2569 {
2570 int inputsChanged = m_IsDownMask ^ isDownMask;
2571 if (!inputsChanged)
2572 return;
2573
2574 for (int idx = 0; idx < 9; idx++)
2575 {
2576 int keyBit = 1 << idx;
2577 bool isDown = isDownMask & keyBit;
2578 bool wasDown = m_IsDownMask & keyBit;
2579 if (isDown && !wasDown)
2580 m_System.ChangeColor(idx);
2581 }
2582
2583 m_IsDownMask = isDownMask;
2584 }
2585};
2587
2588#endif
RplMode
Mode of replication.
Definition RplMode.c:9
SCR_CacheNoteComponentClass ScriptComponentClass RplProp()] protected ref array< string > m_aLines
ref Color m_Color
enum EVehicleType IEntity
proto external IEntity SpawnEntityPrefab(notnull Resource templateResource, BaseWorld world=null, EntitySpawnParams params=null)
proto external bool InPlayMode()
proto external int SetEventMask(notnull IEntity owner, int mask)
void Rpc(func method, void p0=NULL, void p1=NULL, void p2=NULL, void p3=NULL, void p4=NULL, void p5=NULL, void p6=NULL, void p7=NULL)
proto external EntityEvent SetEventMask(EntityEvent e)
proto external Managed FindComponent(typename typeName)
void EOnFrame(IEntity owner, float timeSlice)
proto external void GetWorldTransform(out vector mat[])
See IEntity::GetTransform.
proto external BaseWorld GetWorld()
void EOnFixedFrame(IEntity owner, float timeSlice)
void EOnFrame(IEntity owner, float timeSlice)
proto external GenericEntity GetOwner()
Get owner entity.
void EOnInit(IEntity owner)
void EntitySpawnParams()
Definition gameLib.c:130
Game g_Game
Game singleton instance.
Definition gameLib.c:13
IEntity GetOwner()
Owner entity of the fuel tank.
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
EntityEvent
Various entity events.
Definition EntityEvent.c:14
KeyCode
Definition KeyCode.c:13
RplRole
Role of replicated node (and all items in it) within the replication system.
Definition RplRole.c:14
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
Definition EnNetwork.c:95
RplCondition
Conditional replication rule. Fine grained selection of receivers.
int RplIdentity
Definition EnNetwork.c:32
RplRcver
Definition RplRcver.c:59
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.
Definition RplChannel.c:14
void Debug()
Definition Types.c:327