RAGE Multiplayer 0.3.7 Public Testing

  • RAGE Multiplayer 0.3.7 is another iteration of the 0.4 major features testing backport, this update is to assure the smoothest transition to 0.4.

    0.3.7 introduces backports of major client-side scripting subsystem improvements, such as C# implementation (.NET Core, just like server-side) bringing new possibilities for C# game mode developers, but enhancements for the JavaScript runtime as well.

    C#

    C# scripts are stored in the /client_packages/cs_packages/ folder. Once a player connects, it collects all the scripts, checks its integrity and compiles into an in-memory assembly. Thanks to a number of compilation-time checks, there's no way to allow C# client-side scripts to maliciously hurt users.

    Despite the community concerns, all events and functions are available in C#!

    Here's a quick peek of basic C# stuff:

    EVENTS

    Code
    public class EventsExample : RAGE.Events.Script@@@WCF_PRE_LINEBREAK@@@{@@@WCF_PRE_LINEBREAK@@@  public EventsExample()@@@WCF_PRE_LINEBREAK@@@  {@@@WCF_PRE_LINEBREAK@@@    // this kind of events receives mp.trigger, mp.events.callLocal, but also remote events@@@WCF_PRE_LINEBREAK@@@    RAGE.Events.AddEvent("remote_triggerable_event", SomeEvent);@@@WCF_PRE_LINEBREAK@@@    @@@WCF_PRE_LINEBREAK@@@    RAGE.Events.AddDataHandler("some_data", SomeDataHandler);@@@WCF_PRE_LINEBREAK@@@    @@@WCF_PRE_LINEBREAK@@@    RAGE.Events.Tick += Tick;@@@WCF_PRE_LINEBREAK@@@    RAGE.Events.OnPlayerChat += ChatHandler;@@@WCF_PRE_LINEBREAK@@@    @@@WCF_PRE_LINEBREAK@@@    // trigger a js event@@@WCF_PRE_LINEBREAK@@@    RAGE.Events.CallLocal("eventName", 1, "someString", 1.0f);@@@WCF_PRE_LINEBREAK@@@  }@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@  public void SomeEvent(object[] args)@@@WCF_PRE_LINEBREAK@@@  {@@@WCF_PRE_LINEBREAK@@@  }@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@  public void SomeDataHandler(RAGE.Elements.Entity entity, object value)@@@WCF_PRE_LINEBREAK@@@  {@@@WCF_PRE_LINEBREAK@@@  }@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@  public void ChatHandler(string text, RAGE.Events.CancelEventArgs cancel)@@@WCF_PRE_LINEBREAK@@@  {@@@WCF_PRE_LINEBREAK@@@    if(text == "cancelme")@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@    	cancel.Cancel = true;@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@  }@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@  // known as "render" in JS@@@WCF_PRE_LINEBREAK@@@  public void Tick(System.Collections.Generic.List<RAGE.Events.TickNametagData> nametags)@@@WCF_PRE_LINEBREAK@@@  {@@@WCF_PRE_LINEBREAK@@@  }@@@WCF_PRE_LINEBREAK@@@}

    GAME INTERACTION

    Code
    // trivial game stuff@@@WCF_PRE_LINEBREAK@@@int interior = RAGE.Game.Interior.GetInteriorFromCollision(0.0f, 0.0f, 0.0f);@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@// player interaction@@@WCF_PRE_LINEBREAK@@@RAGE.Elements.Entities.Players.GetAtRemote(1).ClearDecorations();@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@// player interaction using a game entity handle@@@WCF_PRE_LINEBREAK@@@RAGE.Game.Ped.ClearPedDecorations(RAGE.Elements.Player.LocalPlayer.Handle);@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@// ped creation@@@WCF_PRE_LINEBREAK@@@uint freeroamHash = RAGE.Game.Misc.GetHashKey("mp_m_freemode_01");@@@WCF_PRE_LINEBREAK@@@RAGE.Elements.Ped ped = new RAGE.Elements.Ped(freeroamHash, new RAGE.Vector3(0.0f, 0.0f, 0.0f), dimension: 5);

    CEF

    Code
    ...@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@public void OurEventHandler(object[] args)@@@WCF_PRE_LINEBREAK@@@{@@@WCF_PRE_LINEBREAK@@@  RAGE.Chat.Output("Got actually called! {0}", (string)args[0]);@@@WCF_PRE_LINEBREAK@@@}@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@public void TriggerMe()@@@WCF_PRE_LINEBREAK@@@{@@@WCF_PRE_LINEBREAK@@@  	RAGE.Events.Add("eventExample", OurEventHandler);@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@	RAGE.Ui.HtmlWindow wnd = new RAGE.Ui.HtmlWindow("package://index.html");@@@WCF_PRE_LINEBREAK@@@ 	wnd.ExecuteJs("mp.trigger('eventExample', 'yep')");@@@WCF_PRE_LINEBREAK@@@  @@@WCF_PRE_LINEBREAK@@@  	// "mp.gui.execute"@@@WCF_PRE_LINEBREAK@@@  	RAGE.Ui.DefaultWindow.ExecuteJs("test()");@@@WCF_PRE_LINEBREAK@@@}

    BUILT-IN NATIVEUI

    Code
    using System;@@@WCF_PRE_LINEBREAK@@@using System.Collections.Generic;@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@using RAGE.NUI;@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@public class MenuExample@@@WCF_PRE_LINEBREAK@@@        : RAGE.Events.Script@@@WCF_PRE_LINEBREAK@@@{@@@WCF_PRE_LINEBREAK@@@    private bool ketchup = false;@@@WCF_PRE_LINEBREAK@@@    private string dish = "Banana";@@@WCF_PRE_LINEBREAK@@@    private MenuPool _menuPool;@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public void AddMenuKetchup(UIMenu menu)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        var newitem = new UIMenuCheckboxItem("Add ketchup?", ketchup, "Do you wish to add ketchup?");@@@WCF_PRE_LINEBREAK@@@        menu.AddItem(newitem);@@@WCF_PRE_LINEBREAK@@@        menu.OnCheckboxChange += (sender, item, checked_) =>@@@WCF_PRE_LINEBREAK@@@        {@@@WCF_PRE_LINEBREAK@@@            if (item == newitem)@@@WCF_PRE_LINEBREAK@@@            {@@@WCF_PRE_LINEBREAK@@@                ketchup = checked_;@@@WCF_PRE_LINEBREAK@@@                Notify("~r~Ketchup status: ~b~" + ketchup);@@@WCF_PRE_LINEBREAK@@@            }@@@WCF_PRE_LINEBREAK@@@        };@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public void AddMenuFoods(UIMenu menu)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        var foods = new List@@@WCF_PRE_LINEBREAK@@@        {@@@WCF_PRE_LINEBREAK@@@            "Banana",@@@WCF_PRE_LINEBREAK@@@            "Apple",@@@WCF_PRE_LINEBREAK@@@            "Pizza",@@@WCF_PRE_LINEBREAK@@@            "Quartilicious",@@@WCF_PRE_LINEBREAK@@@            0xF00D, // Dynamic!@@@WCF_PRE_LINEBREAK@@@        };@@@WCF_PRE_LINEBREAK@@@        var newitem = new UIMenuListItem("Food", foods, 0);@@@WCF_PRE_LINEBREAK@@@        menu.AddItem(newitem);@@@WCF_PRE_LINEBREAK@@@        menu.OnListChange += (sender, item, index) =>@@@WCF_PRE_LINEBREAK@@@        {@@@WCF_PRE_LINEBREAK@@@            if (item == newitem)@@@WCF_PRE_LINEBREAK@@@            {@@@WCF_PRE_LINEBREAK@@@                dish = item.IndexToItem(index).ToString();@@@WCF_PRE_LINEBREAK@@@                Notify("Preparing ~b~" + dish + "~w~...");@@@WCF_PRE_LINEBREAK@@@            }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@        };@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public void AddMenuCook(UIMenu menu)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        var newitem = new UIMenuItem("Cook!", "Cook the dish with the appropiate ingredients and ketchup.");@@@WCF_PRE_LINEBREAK@@@        newitem.SetLeftBadge(UIMenuItem.BadgeStyle.Star);@@@WCF_PRE_LINEBREAK@@@        newitem.SetRightBadge(UIMenuItem.BadgeStyle.Tick);@@@WCF_PRE_LINEBREAK@@@        menu.AddItem(newitem);@@@WCF_PRE_LINEBREAK@@@        menu.OnItemSelect += (sender, item, index) =>@@@WCF_PRE_LINEBREAK@@@        {@@@WCF_PRE_LINEBREAK@@@            if (item == newitem)@@@WCF_PRE_LINEBREAK@@@            {@@@WCF_PRE_LINEBREAK@@@                string output = ketchup ? "You have ordered ~b~{0}~w~ ~r~with~w~ ketchup." : "You have ordered ~b~{0}~w~ ~r~without~w~ ketchup.";@@@WCF_PRE_LINEBREAK@@@                Notify(String.Format(output, dish));@@@WCF_PRE_LINEBREAK@@@            }@@@WCF_PRE_LINEBREAK@@@        };@@@WCF_PRE_LINEBREAK@@@        menu.OnIndexChange += (sender, index) =>@@@WCF_PRE_LINEBREAK@@@        {@@@WCF_PRE_LINEBREAK@@@            if (sender.MenuItems[index] == newitem)@@@WCF_PRE_LINEBREAK@@@                newitem.SetLeftBadge(UIMenuItem.BadgeStyle.None);@@@WCF_PRE_LINEBREAK@@@        };@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public void AddMenuAnotherMenu(UIMenu menu)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        var submenu = _menuPool.AddSubMenu(menu, "Another Menu");@@@WCF_PRE_LINEBREAK@@@        for (int i = 0; i < 20; i++)@@@WCF_PRE_LINEBREAK@@@            submenu.AddItem(new UIMenuItem("PageFiller", "Sample description that takes more than one line. Moreso, it takes way more than two lines since it's so long. Wow, check out this length!"));@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public void DrawMenu(System.Collections.Generic.List<RAGE.Events.TickNametagData> nametags)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        _menuPool.ProcessMenus();@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public MenuExample()@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        _menuPool = new MenuPool();@@@WCF_PRE_LINEBREAK@@@        var mainMenu = new UIMenu("Native UI", "~b~NATIVEUI SHOWCASE");@@@WCF_PRE_LINEBREAK@@@      @@@WCF_PRE_LINEBREAK@@@      	// original NativeUI replicates GTA V "interaction menu", @@@WCF_PRE_LINEBREAK@@@      	//changing FreezeAllInput to true makes the player completely frozen@@@WCF_PRE_LINEBREAK@@@      	// while the menu is active@@@WCF_PRE_LINEBREAK@@@        mainMenu.FreezeAllInput = true;@@@WCF_PRE_LINEBREAK@@@      @@@WCF_PRE_LINEBREAK@@@        _menuPool.Add(mainMenu);@@@WCF_PRE_LINEBREAK@@@        AddMenuKetchup(mainMenu);@@@WCF_PRE_LINEBREAK@@@        AddMenuFoods(mainMenu);@@@WCF_PRE_LINEBREAK@@@        AddMenuCook(mainMenu);@@@WCF_PRE_LINEBREAK@@@        AddMenuAnotherMenu(mainMenu);@@@WCF_PRE_LINEBREAK@@@        _menuPool.RefreshIndex();@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@        RAGE.Events.Tick += DrawMenu;@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@        mainMenu.Visible = true;@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@@@@WCF_PRE_LINEBREAK@@@    public static void Notify(string text)@@@WCF_PRE_LINEBREAK@@@    {@@@WCF_PRE_LINEBREAK@@@        RAGE.Game.Ui.SetNotificationTextEntry("STRING");@@@WCF_PRE_LINEBREAK@@@        RAGE.Game.Ui.AddTextComponentSubstringPlayerName(text);@@@WCF_PRE_LINEBREAK@@@        RAGE.Game.Ui.DrawNotification(false, false);@@@WCF_PRE_LINEBREAK@@@    }@@@WCF_PRE_LINEBREAK@@@}

    Here's how that NativeUI example looks in the game:

    image.png

    General Changes

    • JS: "entityDataChange" event has been replaced with "mp.events.addDataHandler(key, handler)"
    • JS: added mp.events.callLocal
    • Improvements on initial server loading
    • Fix voice chat not getting cleared properly after setting "voice3d" to false
    • Improve voice chat playback thread synchronization mechanism, so it doesn't affect anything else
    • Fix reported voice chat crashes
    • 0.4's game interaction performance improvements backport
    • Fix in-game UI not saving "latest IP connected to" correctly
    • DataStorage UTF-8 support fixes
    • Added a smoother voice chat packet loss handling
    • Fixed reported voice chat stability issues
    • Fixed some specific remote player tasks stuck after finished
    • Added "experimental web platform features" flag to the in-game CEF
    • Fixed key binding issues with isDown param = false

    Downloads

    You can download the update using the regular RAGE Multiplayer updater. Open config.xml and set your updater branch to 037_testing. Once you restart the client, it will download the update. We don't recommend to use the testing release build to play on 3rd-party servers.

    To reference the client-side C# API you should add "dotnet/rage-sharp.dll" as a reference. You can also reference "dotnet/Newtonsoft.Json.dll" to get JSON functionalities.

    rage.mp/forums/topic/2859-rage…layer-037-public-testing/

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!