#mod_development

1 messages · Page 231 of 1

viscid drum
#

I was trying to modify sodas for multiplayer to give a fatigue reduction, using the sodas have caffeine mod as an example, but it looks like it doesn't work for multiplayer

#

is there a simple way to modify an items stats that works in multiplayer?

verbal yew
# viscid drum is there a simple way to modify an items stats that works in multiplayer?
local function myFunc1(Name, Property, Value)
  local Item = ScriptManager.instance:getItem(Name)
  if Item then
  Item:DoParam(Property.." = "..Value)
  end
end
myFunc1("Base.Ghillie_Top", "FabricType", "Leather");
myFunc1("Base.JacketLong_Santa", "FabricType", "Cotton");
myFunc1("Base.JacketLong_SantaGreen", "FabricType", "Cotton");
myFunc1("Base.Jacket_Fireman", "FabricType", "Denim");
myFunc1("Base.Jacket_Padded", "FabricType", "Leather");
myFunc1("Base.Jacket_PaddedDOWN", "FabricType", "Leather");
#

media/lua/client/

vague bay
#

Could anyone help me compile java? I decombiled the game using beautiful-java and made changes to baseVehicle.java but when I compile it it says packages missing.

verbal yew
#

how to open console log into -debug mode?

#
local last_event = 0;
local secundProshlo = 0;

local function ServerTestHuynya()
    local now = os.time(); 
    if now - last_event > 10 then -- where TEN - it's realtime sec
        print("-- sec");
        print("10 sec");
        print("-- sec");
        secundProshlo = secundProshlo + 10;
        print("Sec past:" .. secundProshlo);
        last_event = now

        -- Start func from here!
    end
end

Events.OnTick.Add(ServerTestHuynya);

by Star

vague bay
#

I don't have the build button anymore

#

ok time to delete all files

#

fresh start

#

which file do I change?

  • BaseVehicle from the sources folder
  • BaseVehicle from the decompiled folder
#

@red tiger Could you please help me ?

#

20-Apr-24 12:26 PM - Build completed with 100 errors and 0 warnings in 5 sec, 632 ms

verbal yew
thick karma
#

I do not fully understand your comment.

verbal yew
#

something like that

#

but for voice

thick karma
#

Ohhhhh

thick karma
#

And if you're going to have to reuse or mod any of their code to do it, it would be good to ask.

verbal yew
thick karma
#

I don't know how they add those tbh... So many character creation panel mods add chunks with no gamepad support that I've come to avoid them all reflexively.

verbal yew
thick karma
#

Yeah I don't mind having a mod here and there on my server that doesn't support us, but the character creation panel and other essential panels gotta be supported or I don't add em

#

Not gonna break support for gamepad on menus that are unavoidable to gamepad players on my server.

#

Not worth what little is gained in doing so to me

#

The vanilla hair menu always worked fine for me lol

#

I ended up just making my own personal mod for my friends and for me that simply adjusts the spacing of elements on that page so that things can actually be read by gamepad players and long entries don't get chopped off on large fonts, and I avoid all other edits to that menu.

red tiger
#

Good morning.

mellow frigate
#

Hello there, for once, I want to create a proper tiles pack. Is there a guide that explains how to set each property for each created tile ? I guess it must be done in the .tiles file, if it is not in the .pack itself.

coarse sinew
vestal gyro
#

would this get the trait its desired recipes or do I have to put into its own function/lua file

coarse sinew
#

https://www.projectzomboid.com/modding/zombie/characters/traits/TraitFactory.html#addTrait(java.lang.String,java.lang.String,int,java.lang.String,boolean)
addTrait returns a TraitFactory.Trait and to that you must add the recipes

require('NPCs/MainCreationMethods' );
local function AddPipebombTrait()
  local chemMunitionsTrait = TraitFactory.addTrait("ChemicalMunitions Proficiency", "Chemical Munitions Proficiency", ...
  
  chemMunitionsTrait:getFreeRecipes():add("Make Aerosol bomb");
  chemMunitionsTrait:getFreeRecipes():add("Make Flame bomb");
  chemMunitionsTrait:getFreeRecipes():add("Make Pipe bomb");
  chemMunitionsTrait:getFreeRecipes():add("Make Noise generator");
  chemMunitionsTrait:getFreeRecipes():add("Make Smoke Bomb");
end
Events.OnGameBoot.Add(AddPipebombTrait);
vestal gyro
#

ah ok thank you

gaunt meteor
#

is it possible to include tile definitions on farm tiles? thinkies

gaunt meteor
#

hm nevermind

red tiger
#

Progress on my software

thick karma
# red tiger

I would love to see a fully dark mode. The big white inputs feel a bit off for me. You generally don't expect black inputs on a light theme app, so perhaps that's a bit of what feels off to me about it. But also it's just such a bright color lol. I could do tan. White is very white. I know it probably seems minor but I would bet you I'm just going to be among the first and far from the only people who comment about this when they first experience it.

red tiger
#

Blocking is a phase in UI design. This is where I'm at currently.

thick karma
#

Heard.

red tiger
#

I'm currently working on the left side nav. It's going to have responsive color changes when interacted.

thick karma
#

That completely makes sense

red tiger
#

The majority of the UI is blocked which is good.

#

The program also has implemented code so it's not just a shell at this point either.

#

The design is to allow for the site to be hosted on GitHub so anyone with a computer browser can access and use it.

red tiger
#

Good afternoon.

thick karma
red tiger
#

No surprise. It likely means that everyone's busy modding.

gaunt meteor
#

I'd ask about growing farm tiles that have properties (for e.g. trees, fences..) but looking into the lua I'm stumped. When I grow stuff, they have no properties. Just plain walk-through tiles rippingskinoff I keep thinking there is a file somewhere in vanilla that is doing that

#

Kinda don't want to add a manual lua check for tile properties either because knowing players they'd build big farms and that'd be a nightmare

muted garnet
#

Say me pls how do I find out the state of the variable for the second player? The server sends commands to two players to create UI windows and then sets Window.instance = true for this player

-- client

Window = ISPanel:derive("Window")
Window.instance = false

function createrequesterwindow()
-- code
Window.instance = true
end

function createrecipientwindow()
-- code
Window.instance = true
end

what do I need to add to this line before creating a context menu to check the player that I clicked for Window.instance?

if not Window.instance then
-- create context menu when click on Isoplayer
end

or do I need to create a table with the data of the first and second players? If this is so, then please tell me how I can implement this?

red tiger
#

@thick karma

thick karma
#

Now we're talkin

red tiger
#

Oh yeah I'm a nerd about dark themes.

thick karma
#

Perfect

red tiger
#

I'll fine-tune the palette last.

#

This is about the look and feel contrast I want.

#

The tool is already 75ish percent working.

thick karma
#
local player = nil
for index = 0, getNumActivePlayers() - 1 do 
   player = getSpecificPlayer(index)
   if player:getOnlineID() == packet.id then break else player = nil end
end```
#

Something like this @muted garnet

red tiger
#

A lot cleaner.

#

Uniform spacing and coloring.

#

=)

#

Oh and this is how the parameter list scales.

#

@thick karma

thick karma
#

Looks very clean. Good shit.

red tiger
#

Thanks.

grizzled fulcrum
#

Hi, I have a small question about TraitFactory.setMutualExclusive(a, b)

#

I thought that this means you cannot have one and the other, only one OR the other. But I did some test and I can have both at the same time and multiple of the same trait

#

to fix this I just did some checks when I add the trait but does anyone know what this actually does since my interpretation was wrong.

thick karma
#

I think that's what it's supposed to do and I have seen mods successfully achieve mutual exclusivity in menu, but I've never been 100% sure how. I always assumed they used that. I imagined you could dynamically add two mutually exclusive traits in theory, but you couldn't choose them in the creation menu.

thick karma
#

Or both?

grizzled fulcrum
#

wait I think I know what you mean, I couldn't add them both in the trait selection menu. but you can still dynamically add them both so I had to do

if data.daysNotBrushed >= sandboxVars.BadTraitCount and player:HasTrait("FoulBrusher") == false then
    if player:HasTrait("GoldenBrusher") then
        traits:remove("GoldenBrusher")
    end

    traits:add("FoulBrusher")
elseif data.daysBrushedToMax >= sandboxVars.GoodTraitCount and player:HasTrait("GoldenBrusher") == false then
    if player:HasTrait("FoulBrusher") then
        traits:remove("FoulBrusher")
    end

    traits:add("GoldenBrusher")
end
#

but you've (indirectly) just brought an unrelated issue to light which is even if you add my trait which costs 2 points, you will still lose it after a day if you don't brush

grizzled fulcrum
#

To anyone who is having the "failed to update workshop item, result=2" issue, try and disable your firewall. I tried a million things and nothing worked but disabling firewall did the trick. Make sure to enable it once you're finished.

verbal yew
#

its mean Mutual Exclusive can be pickable in create menu, but ingame u can get both

grizzled fulcrum
#

I see, thanks

vague bay
#

Good morning.

uneven vessel
#

Anyone know how to add a weapon to the home pistol gun cases?

#

well actually more accurately

#

what the container ID is and how they always manage to pair ammo mag and pistol when they appear together

#

Oh... Nevermind, I see they are all just different cases with the same appearance

verbal yew
#

Case like Distribution

#

CaseLike item for spawn in ProceduralDistribution

uneven vessel
#

so i'd need to make an item that always has items inside it

#

And then add it to the bedroom drop tables?

verbal yew
#

yep, something like that, you create new item same like GunCase (container, or bag), put this item somewhere in ProceduralDistribution, put in this item another items

uneven vessel
#

If something is value 200 it's guaranteed right?

verbal yew
# uneven vessel If something is value 200 it's guaranteed right?

if im not wrong, it's based on ROLLS + loot setting.

for example like:

Rolls = 4

Item1 // SpawnWeight 5
Item2 // SpawnWeight 20
Item3 // SpawnWeight 200
Item4 // SpawnWeight 25
-------------------------
Total weight 250
devides on
0 / 5 / 25 / 225 / 250

Randomize from 0 to 250
from 0 to 5 -- it's first item
from 5 to 25 -- item2
from 25 to 225 -- item3
from 225 to 250 -- item4

Randomizer start 4 times and get item in pool list.
#

200 - it's not spawn chance in total

#

it's weight on pool list

verbal yew
uneven vessel
#

Or does each entry get a roll

verbal yew
#

i think it's roll 3 times and get one items per roll

#

i mean, not 3, i mean roll count

#

like this

#

But you have sandbox option with loot setting

#

more or less

#

it's should upgrade rolls count

uneven vessel
#

I will continue trying tomorrow

#

Confusion

red tiger
#

Good evening.

vague bay
#

Good morning.

verbal yew
#

from 468 lines to 191

muted garnet
# thick karma Need to use the player's display name or online id to find matching player objec...

I don’t quite understand, please explain in more detail how I can get window.instance for a specific player?

server:

    if command == 'GetPlayerID'  then

        args.id = args[1]
        sendServerCommand("nil", "PlayerObjID", args)
    end
client:

function()
    local clickedPlayerID = clickedPlayer:getOnlineID()
    sendClientCommand("123", "GetPlayerID", {clickedPlayerID})
-- code to create context menu when click on player
how can I implement here check to window.instance for clickedplayer?
end

Events.OnServerCommand.Add(function(module, command, args)

if command == 'PlayerObjID' then
  local player = nil
  for index = 0, getNumActivePlayers() - 1 do 
     player = getSpecificPlayer(index)
     if player:getOnlineID() == args.id then 
      if player and Window.instance then
        print("123playerwindow")
      end
      break 
    else player = nil end
  end
end

end)
verbal yew
#

how to test performance difference between two same functionality mods?

thick karma
verbal yew
#

It jumps in both cases, but I'm wondering which code-level mod is easier

bright fog
#

Show us the code diff perhaps ?

thick karma
#

You can just use the same logic to find the right singular player and have them do something when another player triggers the action

verbal yew
#

min

muted garnet
thick karma
thick karma
#

If you don't write the id = part, it'll by default be stored using 1 as the key (it'll store what you're storing as an index of an indexed array)

#

{ foo, bar } => { [1] = foo, [2] = bar }

#

Follow? @muted garnet

muted garnet
# thick karma Follow? <@510938091805081631>

not quite, what do you mean by packet and it`s storing? I don't quite understand how to use this package, like, I can write:

args.playerID = player:getOnlineID()
args.clickedplayerID = clickedplayer:getOnlineID()

and then use this players id for both players?

thick karma
#

And I would in your case use the ID serverside to decide which players to send a response

#

Do you acquire the player objects on client or on server? And have you printed them somehow, to make sure you correctly obtain two different IsoPlayer objects?

muted garnet
#

Players are obtained from the client (playerObj and click Player) and then sent to the server, I have no problems transferring the Ids of players to each other, now I am trying, using your method, to set up the transfer of the value of a specific player Window.instance for all players

thick karma
#

What do you mean by the value of that Window.instance? You can only pass a copy of the Lua-based table data of the instance. The actual instance cannot be passed between computers. You can only pass that kind of thing by reference from one place to another on the same client.

#

You could pass what they need to instantiate their own copy of Window.instance

verbal yew
#

how to ZombRand but something like double in result?

thick karma
#

Also I think I saw the other day that your Window module is a global object; meant to tell you I would discourage that. Not only would it be better to make that local, but if you ARE committed to making it global, "Window" is a terribly unsafe global name for a variable because of the likelihood of reuse by other new modders.

verbal yew
#

ZombRandFloat?

thick karma
#

Or there is some other function that can

thick karma
#

ZombRandFloat(1, 2) => 1.43492429

#

Just worked for me

verbal yew
#

thanks :3

red tiger
#

Good morning.

red tiger
red tiger
#

I personally would rather use a mid-quality PRNG like a Xor128 shift one.

thick karma
bronze yoke
#

it's the opposite...

thick karma
#

Really?

red tiger
thick karma
#

Which is the opposite? Their goal or their choice?

red tiger
#

RJ's reasoning why he wants the high-quality PRNG was for zombie simulation to be better. This is what he told me in the late 2010's.

muted garnet
#

@thick karma

In my context, Window.instance - it`s only test name for variable, that set in true for client player, when a modal window from other player appears on his screen and vice versa:

client side:

Window.instance = false

function Window:OnSentRequest()
      -- creating modal window to player, that sended request
      Window.instance = true -- set this variable in true for client
end

function Window:OnRecieveRequest()
      -- creating modal window to player, that recieved request from player, that sended it
      Window.instance = true -- set this variable in true for client
end

creating context menu to send request to player, that we clicked, client side:

function 123()
if clickedPlayer then
  if not Window.instance then -- here code only checking client variable (Window.instance) and I wanna check clickedPlayer Window.instance (like: if not Window.instance(clickedPlayer))
    creating context menu for client player to send request
  end
end

end

Events.OnFillWorldObjectContextMenu.Add(123)

I asking you about way to get clickedPlayer Window.instance and transfer it to requesterPlayer and vice versa

thick karma
#

Instead of a faster one?

red tiger
#

I released a mod during build 30 and 31 that replaced their PRNG with something even faster than java.awt.Random and it helped people who had dated technology play smoother.

#

ZombRand does have a footprint at the scale it's being used although I can argue that it's less of a footprint in 2024.

red tiger
#

It's good that they're aware of it.

thick karma
#

Well then.

red tiger
#

So not harsh, just engineering thoughts.

#

=)

thick karma
#

Surprise surprise.

#

❤️

red tiger
thick karma
# red tiger

Lookin good. Is this tool aiming to improve upon and replace the online javadoc many of us use? Because I would totally use it.

#

lol it is pretty.

empty rock
#

Shoutout to @bronze yoke for being an absolute brute in this channel - always helpful and quick responses - amazing!

#

Almost anything I search albion has a good answer for it, very helpful! 👌

red tiger
#

I wrote Umbrella. @bronze yoke maintains it.

#

She worked on the events typings originally and we formed a union to provide this platform for documentation to be possible.

thick karma
#

Oh

#

a sledgehammer.

#

Your pfp

red tiger
#

I will always say that modders have so much power right now to improve the quality of life for modding at large thanks to these tools and I'm working right now to make this a reality.

verbal yew
#

@red tiger can you compare the code and tell me which one is better for performance, please? ^^'

verbal yew
red tiger
#

So I plan to personally document the ISUI library. This is the big baddy for most people.

verbal yew
#

same functionality

red tiger
# verbal yew same functionality

Alright so knowing that the functionality is the same I can safely say:

  • You do not repeat the same calls to get inner references which is the biggest gripe I have for all modding code in PZ. Good job.
  • Your code does not index randomly or shift.
  • Your code contains documentation which is rare.

I would document for code that does state switching or resetting just to let the reader know why.

Your code is otherwise trying to follow standards and this should be encouraged at large.

thick karma
# muted garnet <@470376476445900801> In my context, Window.instance - it`s only test name for...

The code you're posting is a bit too pseudo to know whether you are doing things in a way that makes any sense, tbh.

I would not recommend using "Window.instance" as the name of the field that is simply true or false based on whether a player can see the window, because .instance has a precedent in Zomboid as referencing an actual instance, meaning a specific implementation of an object that can have multiple versions (e.g., there is a Zombie-type object class and their are instances of that class, which we tend to call zombie objects or just zombies in practice). Meaning, it doesn't just reference a boolean variable, traditionally, in Project Zomboid vanilla files or in mods.

Your pseudocode example function takes no parameters and suddenly you say you have access to "clickedPlayer" but we have no clue how, so I'm not really sure how far along your code is in terms of correctly accomplishing the "sub-goals" of your overall goal.

It would help if you post actual code so we can see where you're at. What do your client and server files actually look like?

red tiger
#

The most immediate way to improve PZ Lua and Java code is to follow DRY code principle. (Do-not Repeat Yourself)

#

Take the time and make a line to assign variables before going into code.

#

It will make your code so much better.

#

Simple and legible code leads to optimized code practices.

#

Shortcuts only hurts you and the bottom-dollar with performant code.

verbal yew
red tiger
#

More like if you

globalObject.someStaticReference.innerVar:executeFunc(..)

and you call this more than once, store innerVar as a local.

muted garnet
# thick karma The code you're posting is a bit too pseudo to know whether you are doing things...

I'll try to tell you in more detail:

this is client side code, that summon context menu option, when you click on other player, it grabbing clickedPlayer from vanilla ISWorldObjectContextMenu function

ISWorldObjectContextMenu.Window = function(player, context)

    local playerObj = getSpecificPlayer(player)



if clickedPlayer and clickedPlayer ~= playerObj and not clickedPlayer:isAsleep() and isClient() then

    print(playerObj:getModData().Windowing)
    print(clickedPlayer:getModData().Windowing)


    if not WindowUI.instance then
        local option = context:addOption(getText("ContextMenu_Trade1", clickedPlayer:getDisplayName()), context, ISWorldObjectContextMenu.Windowing, playerObj, clickedPlayer) --
        if math.abs(playerObj:getX() - clickedPlayer:getX()) > 2 or math.abs(playerObj:getY() - clickedPlayer:getY()) > 2 then
            local tooltip = ISWorldObjectContextMenu.addToolTip();
            option.notAvailable = true;
            tooltip.description = getText("ContextMenu_GetCloserToTrade1", clickedPlayer:getDisplayName());
            option.toolTip = tooltip;
        end
    end
end

end


ISWorldObjectContextMenu.Windowing = function(context, playerObj, clickedPlayer)

    local playerID = playerObj:getOnlineID()
    local clickedPlayerID = clickedPlayer:getOnlineID()

    if not WindowUI.instance then
        print("12345678")
        sendClientCommand("123", "TradeRequestSent", {playerID, clickedPlayerID})
    end
end

Events.OnFillWorldObjectContextMenu.Add(ISWorldObjectContextMenu.Window)
#

this is server side code to send commands to appear UI windows for clickedPlayer and player

Events.OnClientCommand.Add(function(module, command, player, args)

    if command == "TradeRequestSent" then
        if args[1] and args[2] then

            playerID = args[1]
            playerObj = getPlayerByOnlineID(playerID)
        
            clickedPlayerID = args[2]
            clickedPlayer = getPlayerByOnlineID(clickedPlayerID)


                sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
                sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
                sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
        end
    end
#

and this is client side code to create UI windows for recipient(clickedPlayer) and requester(PlayerObj):

WindowUI = ISPanel:derive("WindowUI");

local modal
local playerObj
local clickedPlayer
WindowUI.instance = false


function WindowUI:onSentWindowRequest()

    modal = ISModalDialog:new(getCore():getScreenWidth() / 2 - 175, getCore():getScreenHeight() / 2 - 75, 350, 150, getText("IGUI_WindowUI_SentWindowRequest to:" .. " " .. clickedPlayer:getDisplayName()), nil, true, WindowUI.onAnswerWindowRequest);
    modal:initialise()
    modal:addToUIManager()
    modal:removeChild(modal.ok);
    modal.moveWithMouse = true; 
    modal.ok = nil;

    local btnWid = 100
    local btnHgt = math.max(25, getTextManager():getFontHeight(UIFont.Small) + 3 * 2)
    local padBottom = 10
    modal.cancel = ISButton:new((modal:getWidth() / 2) - btnWid / 2, modal:getHeight() - padBottom - btnHgt, btnWid, btnHgt, getText("UI_Cancel"), modal, ISModalDialog.onClick);
    modal.cancel.internal = "CANCEL";
    modal.cancel.anchorTop = false;
    modal.cancel.anchorBottom = true;
    modal.cancel:initialise();
    modal.cancel:instantiate();
    modal.cancel.borderColor = {r=1, g=1, b=1, a=0.1};
    modal:addChild(modal.cancel);
    print(WindowUI.instance)

    WindowUI.instance = true
end

function WindowUI:onReceiveWindowRequest()

    modal = ISModalDialog:new(getCore():getScreenWidth() / 2 - 175,getCore():getScreenHeight() / 2 - 75, 350, 150, getText("IGUI_WindowUI_WindowRequest by:" .. " " .. playerObj:getDisplayName()), true, nil, WindowUI.onAnswerWindowRequest);
    modal:initialise()
    modal:addToUIManager()
    modal.requester = playerObj;
    modal.moveWithMouse = true;
    print(WindowUI.instance)

    WindowUI.instance = true
end
#
Events.OnServerCommand.Add(function(module, command, args)

    if command == 'PlayersDataWindowTransfer' then
        playerObj = getPlayerByOnlineID(args[1])
        clickedPlayer = getPlayerByOnlineID(args[2])
    end

        if command == 'TradeRequestSent' then
            WindowUI:onSentWindowRequest()
        end

        if command == 'TradeRequestReceived' then
            WindowUI:onReceiveWindowRequest()
        end
end)
thick karma
#

Where does clickedPlayer come from in ISWorldObjectContextMenu.Window?

muted garnet
#

from vanilla

#

cuz I created function window in ISWorldObjectContextMenu

#

clickedPlayer transfer correctly

#

cuz UI windows appear

thick karma
#

Oh I see.

#

It's a global used in that file

muted garnet
#

but problem is Window.instance work only for client, so if clickedPlayers Window.instance == true, than clickedPlayers context option will not appear, but if other, 3-rd player will click to clickedPlayer, his context option will apear

#

I cant get clickedPlayers Window.instance

#

to make check not only for playerObj, but and for player that we click

#

how can I make to avoid appearing context option on player, that have opened UI window?

thick karma
#

What is the purpose of this? Why send data to all other players on the server about something that can only possibly impact these two?
sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})

#

Ohhhh you want to prevent interruption this way?

#

So players cannot interrupt other people's interactions?

muted garnet
#

I tried to use it with the intention of setting the value for a specific player to prevent the creation of a context menu when clicking on that player if they have a UI window open

#

this is currently used to set the clickedPlayer and playerobj variables

#

in client side creating UI

thick karma
#

You mean you want to expand if not WindowUI.instance then to stop players on any client from interrupting an interaction between two busy clients?

muted garnet
#

YES

#

YES

#

At the moment when the players have a UI window open, I want to make sure that other players do not have the opportunity to send a request to any of them

thick karma
#

Hmmm... I'm not sure you can rely on their OnlineID to remain the same if players log out in the middle of this interaction. I have not tested that. Someone else will have to confirm. I am however much more confident that their display name would remain the same throughout their interaction, so using display names as a key for preventing this behavior might be wise, first of all.

#

You could create a table on every client, which I would not call "instance" but perhaps "busy" would be a good choice, like so:
Window.busy = {}

And then when players successfully call Windowing:

ISWorldObjectContextMenu.Windowing = function(context, playerObj, clickedPlayer)

    local playerID = playerObj:getOnlineID()
    local clickedPlayerID = clickedPlayer:getOnlineID()
    local playerName = playerObj:getDisplayName()
    local clickedPlayerName = clickedPlayer:getDisplayName()

    Window.busy[playerName] = true
    Window.busy[clickedPlayerName] = true

    if not WindowUI.instance then
        print("12345678")
        sendClientCommand("123", "TradeRequestSent", {playerID, clickedPlayerID})
    end
end

And then also follow up with a sync to all other clients that establish those player names as busy the same way.

#

And then if not WindowUI.instance then becomes if not (Window.busy[playerName] or Window.busy[clickedPlayerName]) then

#

And then when they finish the interaction, Window.busy[playerName] = nil.

#

Also, I would strongly recommend throwing local on the variable lines here:

            playerID = args[1]
            playerObj = getPlayerByOnlineID(playerID)
        
            clickedPlayerID = args[2]
            clickedPlayer = getPlayerByOnlineID(clickedPlayerID)
#

Because you're creating those as global variables serverside

#

And especially if Zomboid uses clickedPlayer as a global from time to time, I would be concerned about artificially setting it.

#

If online IDs never change when people log out, feel free to use those instead of names; I would need to test that.

#

Alternatively, and perhaps this would be better than sending the data out to everyone, you could track busy players serverside, and simply display a local message if the server responds that a player is busy when someone tries to activate the function that triggers the window creation on both clients.

#

So you just let them try to activate the interaction, the request goes to server, server checks Window.busy[playerName] and Window.busy[clickedPlayerName], and if server concludes one of them is busy, just respond to the requester with a command that tells them to :Say("That player seems busy...")

#

@muted garnet

#

That would honestly be the more robust way to go than trying to disable the option clientside, because even if you do succeed in sending a command out to everyone to disable that function clientside, there will be a fraction of a second before they receive the command during which they could still fire the command.

muted garnet
# thick karma So you just let them *try* to activate the interaction, the request goes to serv...

like this?

            local playerName = playerObj:getDisplayName()
            local clickedPlayerName = clickedPlayer:getDisplayName()

            if not (WindowUI.busy[playerName] or WindowUI.busy[clickedPlayerName]) then
                sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
                sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
                sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
            end
thick karma
#

Serverside, yes, and, again, then you could do this instead:

muted garnet
#

and do I need to create a table on server side?

thick karma
#

Yes, you would need WindowUI.busy to exist serverside if you plan to use it there. I would recommend something like this:

            if WindowUI.busy[clickedPlayerName] then
                sendServerCommand(playerObj, "123", 'BusyPlayerDetected', {name = clickedPlayerName})
            else
                sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
                sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
            end

And then use normal option disabling to stop players who are themselves busy from sending the command in the first place.

#

Do you see what I'm saying?

#

(I can see English is probably not your first language, so let me know if I am not clear to you.)

#

I.e., use a local WindowUI.busy for determining local players who are busy clientside after they send a command so they can't send two, and use WindowUI.busy serverside for preventing the same player from receiving two commands.

#

Assuming this is a shared object that is available on both sides.

#

Or perhaps use WindowClient.busy and WindowServer.busy to make things more explicit in your code. (Would of course need to create those parent modules (WindowClient and WindowServer).

muted garnet
thick karma
#

By the way, can I assume 123 is a placeholder name for your module? It's an unusually simple choice but I guess most people won't reuse it. Most people would use the name of their most prominent or fundamental module or the ID of their mod as the "module" in those commands.

#

And generally they would also confirm the module in addition to the command for safety.

muted garnet
#

I am getting server error when try to create WindowUI.busy = {}

attempted index of non-table


WindowUI.busy = {}

Events.OnClientCommand.Add(function(module, command, player, args)

    if command == "TradeRequestSent" then
        if args[1] and args[2] then

            playerID = args[1]
            playerObj = getPlayerByOnlineID(playerID)
        
            clickedPlayerID = args[2]
            clickedPlayer = getPlayerByOnlineID(clickedPlayerID)

            local playerName = playerObj:getDisplayName()
            local clickedPlayerName = clickedPlayer:getDisplayName()

            if not (WindowUI.busy[playerName] or WindowUI.busy[clickedPlayerName]) then
                sendServerCommand("123", 'PlayersDataWindowTransfer', {playerID, clickedPlayerID})
                sendServerCommand(clickedPlayer, "123", 'TradeRequestReceived', {playerID})
                sendServerCommand(playerObj, "123", 'TradeRequestSent', {clickedPlayerID})
            end
        end
    end
thick karma
#

Because WindowUI doesn't exist there.

muted garnet
thick karma
#

Let me give you a quick example of how I would personally redo your server file.

#

given module name 123 for testing

muted garnet
#

I need to derive it, like in client? WindowUI = ISPanel:derive("WindowUI");

thick karma
#

That would be weird tbh.

#

Having any access to a UI serverside is a bit wonky.

#

Just a strange concept.

#

Give me a minute.

#

I am offering a concrete example of what serverside could look like.

thick karma
#

@muted garnet

#

This slightly relabels some stuff according to how I would recommend writing some of this; I use lowercase names for functions in alignment with Java standards since Lua has no standards, and I prefer using player for the player object in alignment with getPlayer, OnCreatePlayer, getSpecificPlayer, clickedPlayer, and a million other examples in and outside of your code where player is the object. I use playerIndex for the index. I never refer to playerIndex using player, even if the example on which I am basing my code does that.

#

If you sent it the correct commands and received the correct commands clientside, I believe this would be entirely functional, though I'd have to write the corresponding clientside to be 100% sure through some quick testing that I haven't made any minor mistakes.

#

I use clear labels for data in packets instead of using [1], [2]. [3] etc.

#

I use packet.clickedPlayerID, e.g., not packet[3] or something similarly unclear.

muted garnet
#

maybe you meant {} instead of [] in WindowServer.busy = []? cuz umbrella shows this line like error

thick karma
#

Typo

#

Didn't hold shift there apparently.

#

WindowServer.busy = {} is correct

muted garnet
#

for what this part of code?:

-- Command Processor:

-- The following command would be used to trigger events in this 
-- file (assumes "player" and "clickedPlayer" are IsoPlayer objects):
--
--      local playerID = player:getOnlineID()
--      local playerName = player:getDisplayName()
--      local clickedPlayerID = player:getOnlineID()
--      local clickedPlayerName = player:getDisplayName()

--      local packet = {
--          playerID = playerID,
--          playerName = playerName,
--          clickedPlayerID = clickedPlayerID,
--          clickedPlayerName = clickedPlayerName,
--      }

--      sendClientCommand(player, modID, "createWindowRequest", packet)
thick karma
#

That is how you would interface with the commands I wrote.

#

That's how you would send the command that server would process

#

in determining whether to accept or reject the request to make a window.

#

You would just let anyone who isn't busy send the request, and then the server would go, "Okay, is the target busy?" If so, tell the caller, "No"; but if no one is busy, go ahead and activate the procedure that opens windows on both clients.

#

You would of course need to add another command later for clearing these busy variables. When the players are done with those windows.

#

@muted garnet You would need something like this to catch commands in client:

-- So this doesn't run clientside in Multiplayer, but could 
-- do so if your mod had any purpose in Singleplayer:
if isServer() then return end

-- Filewide Module:

local WindowClient = {}

-- To avoid writing (and later changing) a literal 123 everywhere:

local modID = "123"

-- Commands:

WindowClient.completeRejection = function(packet)
    -- set clientside .busy variable for this player back to nil
end

WindowClient.tradeRequestSent = function(packet)
    -- finish the request for the sender
end

WindowClient.tradeRequestReceived = function(packet)
    -- finish the request for the receiver
end

-- For compatibility with SP and MP:

WindowClient.processServerCommand = function(module, command, packet)
    if not (module == modID and WindowClient[command]) then return end
    WindowClient[command](packet)
end

Events.OnServerCommand.Add(WindowClient.processServerCommand)

return WindowClient

#

I can't show you more right this second because I gotta go work, but try to see if you can make sense of integrating these two files and I'll pop in from time to time if you're still stuck.

muted garnet
#

thank you very much, I don’t fully understand your code yet, but I’ll try to figure it out on my own

thick karma
#

The one with WindowClient goes in some kind of client file, the one with WindowServer goes in some kind of server file, I made them to work if they're both in lua/shared but you can put them in other places with success, too (e.g. you could move the client one to lua/client if you wanted to.

muted garnet
#

could you pls say me difference between shared / server folder? shared folder - it`s folder where can run client and server files both?

vestal gyro
#

for some reason the ui image for the trait refuses to show up

#

I put the image in media/ui/Traits

#

but it just doesn't show up

verbal yew
#

trait_

#

without "

vestal gyro
#

I looked at other mods and it seemed to me that the variable name was corresponding to that, alright I'll try it like that

verbal yew
#

local yourname = TraitFactory it's for work with trait in future, like:

#

for add recipes, xp boost, etc

slow graniteBOT
#
_keburulla_ has been warned

Reason: Bad word usage

vestal gyro
#

yeah

#

alright it works now thanks

thick karma
slow graniteBOT
#
roach12172 has been warned

Reason: Bad word usage

thick karma
#

@muted garnet Added example of completeRejection command above.

#

Any progress?

muted garnet
thick karma
#

No worries. I'm sure it'll take you some time to get everything implemented and make sense of what I sent. Feel free to reach out if you feel you've made as much sense as you can and you're still stuck.

muted garnet
#

thank you very much for your help and support!

thick karma
#

As far as I know, client literally runs on the client in multiplayer -- that is literally where that code is executed. Client code, as far as I know, is not executed on the server. Is that what you mean when regarding where things get executed?

#

But asking that question is not annoying at all; I welcome anyone to correct anything I say wrong. I've surely said a lot of things wrong. Someone (e.g., albion) tends to chime in when I do (lol). The experienced ones usually only let me talk when I'm not wrong.

#

😅

#

This part of your link is correct:
"Simply putting code into server doesn't mean it is executed on the server and not on the client."

#

But it doesn't work the other way

#

Simply putting code in client DOES mean it is executed on client and not on server.

#

That can be very confusing to many people but it's a recurring issue here.

#

(If you're playing in MP.)

#

I'm sure albion will chime in asap if I'm wrong... lmao

#

You can easily test this by creating a global variable in client and trying to send it to yourself from server in a packet.

#

Should come out nil.

#

This is also correct from your link, FYI: In single player for example all code is executed no matter in which folder you placed it.

#

This is not entirely correct:
"These folder's purpose is code organisation."

#

The folders also impact load order.

#

shared does load before client.

#

I do believe server files run whenever a function in server files is called in SP but tbh I have not explicitly tested that. I would assume that is accurate.

#

That may be a bit misleading in the Konijima link I offered.

thick karma
#

I only put distribution files and such in server; stuff that explicitly extends server files.

verbal yew
#

Who know how to set up clothes to zombies like variables between two items?
for example, i have 4 helmets, but i want use only one, like random between this helmets

thick karma
#

If you see them in here or the other server I might ask them.

#

But that white background... Oof... 😉

verbal yew
thick karma
#

lol f.r. I like color but white is entirely too much color for me.

#

Yeah that's very fair. The server files run but the server in other senses does not exist (e.g., you cannot call sendServerCommand afaik), and in other senses you are arguably your own server.

#

But you are sort of the client of the server that sort of does not exist in SP (in a sense) because you can call sendClientCommand

#

It's all very confusing, certainly lol

muted garnet
#

@thick karma It seems like I figured out the code a little and was able to display UI windows for players, parameters and values, this is an interesting and useful experience for me, very thank you, but please explain in more detail what these lines are used for:

if isClient() then return end -- This is used to check that this is a server file, right?

how this function works?:

WindowServer.directClients = function(module, command, packet, player)
    if not isClient() and not isServer() then
        triggerEvent("OnServerCommand", module, command, packet)        -- Singleplayer
    else
        if player then
            sendServerCommand(player, module, command, packet)        -- Multiplayer -> One Client
        else
            sendServerCommand(module, command, packet)                -- Multiplayer -> All Clients
        end
    end
end

Why do need to return the table value? return WindowServer

thick karma
muted garnet
#

and why in this line uses clickedPlayerID = packet.clickedPlayerID? clickedPlayerID = packet.clickedPlayerID

WindowServer.directClients(modID, 'tradeRequestSent', { clickedPlayerID = packet.clickedPlayerID }, player)

can`t I use instead it? WindowServer.directClients(modID, 'tradeRequestSent', {packet.clickedPlayerID}, player)

thick karma
muted garnet
#

that is, it updates the value of packet.clickedplayer?

thick karma
#

If you use the latter example, you'll access the packet data by saying packet[1] instead of packet.clickedPlayerID. I prefer the latter (packet.clickedPlayerID) because it's descriptive.

#

I avoid using indexes without explicitly described meanings unless the list is an arbitrary indexed list of same-type same-purpose objects, rather than a set of specific data where each entry has a specific purpose.

thick karma
muted garnet
thick karma
#

But honestly, at second glance, for your purposes, it may be more convenient to change that

#

And forward the whole packet.

#

    WindowServer.directClients(modID, 'tradeRequestSent', packet, player)
    WindowServer.directClients(modID, 'tradeRequestReceived', packet, clickedPlayer)

should be plenty fast enough and also you may want easy access to the name variables, or the online id of the other player.

thick karma
#

Where client = the player's Zomboid app on their computer, not exactly the player (just to be super clear).

muted garnet
#

What's the Difference?

thick karma
#

I mean that even though we write the command as sendServerCommand(player, module, command, packet), the client code has to guarantee that the right player on that client receives the command. The server only guarantees it'll get to the right PC. If you're sending a command to a client that has 2 players playing in splitscreen, you'll need to use player online ID or name to find the right player object.

#

sendServerCommand(player etc. does not actually guarantee that player receives that command, despite how it may appear.

#

See what I'm saying?

muted garnet
#

I got it, thanks

thick karma
# muted garnet I got it, thanks

Side thought not directly related to the other stuff we're discussing: I'm guessing this mod you're making currently has no singleplayer purpose, but if it ever acquires one, you may need to consider what to do about commands involving OnlineIDs, as they may fail to provide the results needed for your code to work. Just FYI.

#

I have not tested the results of trying to request or use online IDs for anything in SP.

#

I would expect some nil return values somewhere along the way.

muted garnet
#

no, this mod will only be for multiplayer, cuz all the functionality is interaction between two players

thick karma
#

Makes sense.

#

Then for what it's worth directClients is overkill; I just teach people to use it because it's convenient when working with mods that function in SP and MP.

#

You could skip that function and directly use the sendServerCommands to reply to the server.

#

Just for your own knowledge.

#

I think it's good to know long-term because it can make the transition between SP and MP functionality very easy.

muted garnet
#

do you mean this checks for the client and the player?

    if not isClient() and not isServer() then
        triggerEvent("OnServerCommand", module, command, packet)        -- Singleplayer
    else
        if player then
            sendServerCommand(player, module, command, packet)        -- Multiplayer -> One Client
        else
            sendServerCommand(module, command, packet)                -- Multiplayer -> All Clients
        end
    end
thick karma
red tiger
#

It's a simple hack to use the same messaging setup as multiplayer basically.

thick karma
#

sendServerCommand(module, command, packet) triggers OnServerCommand on every client with the provided module, command, packet. And that's also exactly what triggerEvent("OnServerCommand", module, command, packet) does, but triggerEvent only works clientside and in this case it's just simulating a server response by calling what the server would have called.

thick karma
#

Meanwhile, sendServerCommand(player, module, command, packet) uses player to pick one client, and then it effectively activates triggerEvent("OnServerCommand", module, command, packet) on that client.

#

The value of directClients above (for me) is that it moves the optional player parameter to the end of the function parameters.

#

So 1 function can trigger all 3 needed outcomes.

#

however sendServerCommand is already designed to handle 2 of them in MP mode.

#

So you don't need directClients for using 1 command in multiple ways if you aren't making a mod that has any value in SP.

red tiger
#

Events are stupid simple. All you'll need to do is keep track of your game's states.

#

That part's on you.

#

Otherwise it's just another event to fire / trigger.

#

When designing LuaNet commands, be sure not to overload them.

thick karma
red tiger
#

It says 'Server' In it but it doesn't mean you can't use it in singleplayer, etc.

thick karma
#

For sure. It's 100% not meant to be complicated.

#

triggerEvent(eventName, arg, arg, arg, arg) makes an event with given name call some functions that [hopefully] expect those args. The end.

thick karma
#

lol but yeah let's not worry about that for now... Haha poor M.

#

Every new Zomboid programmer: "How do I make a UI?"
is handed a book

red tiger
#

If not for that programming wouldn't be fun.

#

Also yeah I focus heavily on UI here as part of my niche.

#

Before multi-tab chat and discord intrgration was official I had my mod. =)

thick karma
#

That's lit

red tiger
#

I wrote my own UI library ontop of ISUI and ISChat.

thick karma
#

I got sucked into the UI stuff early by my compulsion to hide it and the lack of a quick gamepad shortcut for doing so.

#

And then it spiraled completely out of control.

#

Not as out of control as you, of course.

#

lol

red tiger
thick karma
#

Hahaaaaa classic.

red tiger
#

Also before the JVM flag java.awt.headless=true was passed to the game on launch by default, you used to be able to open urls from the lua side. I wrote a discord invite button into a server at one point.

#

Was nifty.

#

You have no idea how insanely convenient that was.

#

People could join the discord server from inside the game itself.

#

If you set that flag to false on launch and use openUrl('..') in Lua, it'll work.

bronze yoke
#

openUrl still works to my knowledge

rich reef
#

Hello all. I have a question regarding the "Mod Options" mod, and trying to get settings to update on user change/apply. It does not seem that I can get the game to recognize anything has changed, which is frustrating. Even copying other code from working mods does not function the same. Does anyone have any insight into this? It seems so straightforward, yet isn't working for me. Thanks in advance.

bright fog
rich reef
bright fog
#

the game can't recognize anything has changed as in your options ?

#

Like whenever you change an option it doesn't update ?

rich reef
#

Yes. I have a basic checkbox, and a simple "print" command to show the data of that checkbox. And upon checking/unchecking the box, and hitting Apply>Accept, it still does not show any change, or reflect it anywhere else trying to use that variable.

bright fog
#

Depends how you access that data tbf

rich reef
#

I was wondering if its just an issue with accessing it correctly, but I've litereally copy>pasted other code and it doesnt function for me.

bright fog
#

We'll need more info on that one to be able to help you ngl

#

Like how you use that option, how you access its status, when, what's the code you're doing, where the code is located in your folders if it's in shared or client

#

Make sure ModOption is loaded before if your code runs from shared

rich reef
#

All the code is in the same .lua under Client. Basically just trying to print out the variable data gathered from the settings:getModOptions table.

bright fog
#

Send the code

verbal yew
# bright fog Send the code
    item zReV2_1ECO_fullhelmet1A
    {
        Type = Clothing,
        DisplayName = zReV2_1ECO_fullhelmet1A,
        ClothingItem = zReV2_1ECO_fullhelmet1A,
        ClothingItemExtra = zReV2_1ECO_fullhelmet1,
        ClothingItemExtraOption = zReECOWear_1,
        BodyLocation = MaskEyes,
        Icon = zReV2_1ECO_fullhelmet1A,
        CanHaveHoles = false,
        BloodLocation = FullHelmet,
            BiteDefense = 100,
            ScratchDefense = 100,            
            BulletDefense = 0,
        Insulation = 0.55,
        WindResistance = 0.40,
        WaterResistance = 1.0,
        Weight = 0.4,
        Tags = GasMask,
        WorldStaticModel = zReV2_1ECO_fullhelmet1_Ground,
    }

    item zReV2_1ECO_fullhelmet2A
    {
        Type = Clothing,
        DisplayName = zReV2_1ECO_fullhelmet2A,
        ClothingItem = zReV2_1ECO_fullhelmet2A,
        ClothingItemExtra = zReV2_1ECO_fullhelmet2,
        ClothingItemExtraOption = zReECOWear_1,
        clothingExtraSubmenu = Wear,
        BodyLocation = MaskEyes,
        Icon = zReV2_1ECO_fullhelmet2A,
        CanHaveHoles = false,
        BloodLocation = FullHelmet,
            BiteDefense = 100,
            ScratchDefense = 100,            
            BulletDefense = 100,
        Insulation = 0.55,
        WindResistance = 0.40,
        WaterResistance = 1.0,
        Weight = 1,
        Tags = GasMask,
        WorldStaticModel = zReV2_1ECO_fullhelmet2_Ground,
    }
verbal yew
#

can u add this two item in suspec overhaul

bright fog
#

Oh sure

#

Filters?

verbal yew
#

GasMask

bright fog
#

Yeah filters

#

Alright doing that

#

I was about to push an update soon anyway ;)

#

Revamping the hotkeys to require Mod Options instead of Eggon's Hotkeys

bright fog
verbal yew
bright fog
#

👌

rich reef
#
local alertVolume = 0.05

local nsEnabled = true

if ModOptions and ModOptions.getInstance then

    local function onModOptionsApply(optionValues)
        local value = optionValues.settings.options_data.enable_notify_sound
        nsEnabled = value

        if nsEnabled ~= nil then
            nsEnabled:onModOptionsApply(value)
        end

        print("checkbox1 = ", SETTINGS.options_data.enable_notify_sound)

    end

    local SETTINGS = {

        options_data = {
            enable_notify_sound = {
                name = "Enable Notification Sound",
                default = true,
                OnApplyMainMenu = onModOptionsApply,
                OnApplyInGame = onModOptionsApply,
            }
        },

        mod_id = 'GN84-UCN-IM',
        mod_shortname = 'UCN + IM Sound',
        mod_fullname = 'UCN + IM Sound',
    }

    local optionsInstance = ModOptions:getInstance(SETTINGS)    

    ModOptions:loadFile()
    
    Events.OnGameStart.Add(function()
        print("checkbox1 = ", settings.options_data.enable_notify_sound)
      end)
end
bright fog
rich reef
#

I can get the options to show up correctly in Mod Options > Mod Settings page, and don't get any errors thrown, but it won't show anything updates when I check the box. Even a simple boolean true>false doesn't show any change.

bright fog
#

And you sure the original mod you copied it from worked ?

rich reef
#

Yes, absolutely. I tried a couple mods, with very simple implementation, similar to the actual examples on the guide page.

#

I feel like I am just missing something really simple, or something is out of place causing the issue. I just cant pinpoint it

bright fog
#

I'm not familiar enough with Mod Options to debug that properly ngl

rich reef
#

I appreciate your help Sir Doggy.

bright fog
#

👌

#

@verbal yew Testing your new helmets, they clip through caps

#

cool shit tho

thick karma
# rich reef I appreciate your help Sir Doggy.

I'm not familiar with the functions you're trying to override or your style of updating Mod Options, but I can provide you with a working example that you can refactor to your heart's content.

rich reef
thick karma
#

This is an example from Wookiee Gamepad Support.

#

I give you this one because it has several different kinds of options going on

#

Including some mutually exclusive ones.

#

It relies on the existence of WGS = {} elsewhere in the code, and a handful of variables declared outside this chunk

#

But hopefully it can give you a feel for what needs to happen.

#

If you want a super easy one I have easy ones too. The options for Better Automatic Movement are pretty easy.

rich reef
#

Thank you Burryaga, that is great. I'll dig at it for a few and see if I can get a basic version working for my mod. Is there anything specific you know that needs to be set up with Events, or a way to access when settings get updated?

thick karma
#

You can refactor any of my Mod Options mods on the workshop freely.

#

Most of my mods use it.

#

You can also borrow a copy of Exterminator

#

Which you'll learn you'll want

rich reef
#

I honestly only need a boolean On/Off checkbox, and a Dropdown with 3 Items, of High, Medium, Low for a sound volume setting. Super simple.

thick karma
#

It's a tiny module in all my Mod Options mods

#

It looks like this:

#
-- Protects Against a Known Options Bug
if not Exterminator then
    Exterminator = {}

    Exterminator.onEnterFromGame = MainScreen.onEnterFromGame

    function MainScreen:onEnterFromGame()
        Exterminator.onEnterFromGame(self)

        -- Guarantee that when you ENTER the options menu, the game does not think you've already changed your damn options.

        MainOptions.instance.gameOptions.changed = false
    end
end
#

Just copy-paste as is and you'll be fine.

rich reef
#

At the top of the code, prior to Mod Options Implementation and Other Code?

thick karma
#

Anywhere in any file

#

This code is standalone and will kick in when it needs to

red tiger
#

Passed the heck out lol

#

Gonna work on my tool now. =)

rich reef
#

Okay. Thank you very much. That's an awesome and simple little bug fix!

thick karma
#

Is that if you store it all in YourModule.mod

verbal yew
thick karma
#

You end up accessing your mod option states by typing YourModule.mod.options.theOptionYouWant

#

Which I like for legibility

#

Obviously if you plan to use it in a loop, store it in a function-local variable (to avoid repeated table accesses if performance is a concern)

rich reef
#

I've seen this version in other mods, which seems more intuitive, but wasn't sure if I was getting the state from the loaded .lua file, or from actual saved settings.

thick karma
#

And the functions available return it

bright fog
thick karma
rich reef
#

LoL

thick karma
#

Also

rich reef
#

Would you be so kind as to send me a very simplified version, using the same concept? I'm looking over the one you sent, and its very detailed, but would be interested in seeing a barebones version in play if possible.

thick karma
#

Just to show you

#

I am pretty serious

#

About my Mod Options.

rich reef
#

Well you are definitely the person to talk to! haha

red tiger
#

I'm serious about documenting code.

#

SO serious that I document other people's code.

#

And then I sleep.

thick karma
#
local Trash = {}

Trash.mod = {

    options = {
        volubility = 2,
        range = 6,
        mouthy = false,
        sneaky = false,
        context = false
    },
    
    names = {
        volubility = "Volubility",
        range = "Sound Range",
        mouthy = "Feeling Mouthy",
        sneaky = "Sneaky Insults",
        context = "Context Option"
    },

    mod_id = "TrashTalker",

    mod_shortname = "Trash Talker"

}

local none = 0

Trash.whisperShortcut = {
    key = none,
    name = "WhisperTrash"
}

Trash.talkShortcut = {
    key = none,
    name = "TalkTrash"
}

Trash.shoutShortcut = {
    key = none,
    name = "ShoutTrash"
}

Trash.category = "[Trash Talker]"
#
if ModOptions and ModOptions.getInstance then

    ModOptions:AddKeyBinding(Trash.category, Trash.talkShortcut)
    ModOptions:AddKeyBinding(Trash.category, Trash.whisperShortcut)
    ModOptions:AddKeyBinding(Trash.category, Trash.shoutShortcut)

    Trash.modSettings = ModOptions:getInstance(Trash.mod)

    local volubility = Trash.modSettings:getData("volubility")
    local range = Trash.modSettings:getData("range")
    local mouthy = Trash.modSettings:getData("mouthy")
    local sneaky = Trash.modSettings:getData("sneaky")
    local context = Trash.modSettings:getData("context")
    
    volubility.tooltip = "How much trash should a trash talker talk? By default, trash talkers will say something snarky after 20% of killing blows."
    range.tooltip = "How loud is your voice? By default (unless modified by your host), trash talking must make some noise. The default range of this scale is 2 to 20 squares, but this can be modified by the host. Notice that 10 is not the maximum. The number goes to eleven. Is it any louder? Well, it's one louder, isn't it? It's not ten. If you need that extra push over the cliff, you know what you do? Exactly. Put it up to eleven."
    mouthy.tooltip = "If this is enabled, your character will talk trash even if they do NOT have the trait. This setting does nothing if your character is already a trash talker by nature (i.e., if they already have the trait)."
    sneaky.tooltip = "If this is enabled, you will whisper trash talk when you are sneaking."
    context.tooltip = "If this is enabled, a Trash menu will be added to your world object context menu (the right-click menu) allowing you to mutter, talk, or battlecry random trash. Whispers will have half of the range (or volume) that is set above; shouts will have double the normal range."
#

    for index = 1, 11 do
        volubility[index] = getText("IGUI_TrashTalker_Volubility_" .. tostring(index))
        range[index] = getText("IGUI_TrashTalker_Range_" .. tostring(index))
    end
    
    function volubility:OnApply(value)
        Trash.volubilify(value)
    end
    
    function range:OnApply(value)
        Trash.rangify(value)
    end
end
#

Kind of Important: I cut the function of Trash.volubilify and Trash.rangify for simplicity.

#

Also this mod is old and doesn't use translations.

red tiger
#

When you upload text as a file it keeps it short on the message history. Helpful for future code-pasting here. =)

#

Not mad or anything just helpful tips that goes a long way.

thick karma
#

For sure, I was just trying to let them skim it in-app with color

red tiger
#

Ah true.

#

files are not mobile-supported.

#

Wish they were honestly.

rich reef
#

I greatly appreciate the help. I am sure this will help someone else as well with the same issue.

thick karma
#

(Ignore the backticks)

red tiger
#

Hehe

#

I'm going to aim for making my software ready to use minimally by tonights' end.

thick karma
#

Right on

rich reef
#

Thank you very much @thick karma . I will tinker with it for a few and let you know if I have any other questions. 🙂

thick karma
#

Good luck!

rich reef
# thick karma Good luck!

Well, I've got it working now. Thanks a million! Now just to figure out how to implement a dropdown menu option in the style that you sent me last.

rancid barn
#

is there a dedicated tutorial anywhere for making a VHS? i have the perfect idea for one

verbal yew
#

one min

#

i went for an energy drink

rancid barn
#

nice

verbal yew
#

seventh... I hope this shiet doesn't kill me...

red tiger
#

As a newly diagnosed type-1 diabetic, coffee is now more of a lifeline than ever.

#

haha

#

I had a sugar-free Redbull though and that was a nice switch-up.

verbal yew
red tiger
verbal yew
#

shiet...

red tiger
#

They found the rock in my brain because I came in from symptoms of both that and the t1d.

#

=)

verbal yew
verbal yew
#

you want create VHS like in total pool list

red tiger
#

I actually feel so much better btw if wondering. Amazing recovery so far.

#

<3

verbal yew
#

or something like drop generated in zombie?

rancid barn
verbal yew
#

okay, it's easy

#

you should create lua file here:
\media\lua\shared\RecordedMedia\

rancid barn
#

gotcha

#

empty lua file made and ready

verbal yew
#

structure like:

RecMedia = RecMedia or {}

-- Media: VHS: Unknown Tape [id: 18271e35-d316-47c1-ae05-eda2fa515b01]
RecMedia["GUID"] = {
    itemDisplayName = "RM_GUID2",  -- VHS: Неизвестная кассета
    title = "RM_GUID3",      -- Неизвестная кассета
    subtitle = "RM_GUID4",   -- «Лекарство реально!» (с) Доктор Керхус
    author = nil,
    extra = nil,
    spawning = 1,  -- nil not spawn
    category = "Retail-VHS",    -- Retail - profi Tape, "Home-VHS" - homemade tape                            -- 
    lines = {
        { text = "RM_GUID5", r = 0.79, g = 0.79, b = 0.79, codes = "BOR-1" },
        { text = "RM_GUID6", r = 1.00, g = 0.82, b = 0.27, codes = "BOR-1" },
        etc
    },
};
#

wheres GUID# - it's generated guid

rancid barn
#

just any random GUID would do?

verbal yew
red tiger
#

As long as it doesn't collide with others you should be fine.

thick karma
#

I often use a middleman variable that gets updated onApply to handle dropdown variables in Mod Options

verbal yew
# rancid barn just any random GUID would do?

Recorded_Media_EN.txt translate file
structure looks like:

/* zRe VAC2 */
RM_GUID1 = "VHS: Unknown tape",
RM_GUID2 = "Unknown tape",
RM_GUID3 = "«Cure is real!» (с) Dr. Kerhus",

/* Ambient */
RM_GUID4 = "*Something falls in the background...* <fzzt>",

/* kERHUS */
RM_GUID5 = "-It works?! The record is going? Fine...",
RM_GUID6 = "Greetings, survivor... You may have found this tape at one of my assistants...",
RM_GUID7 = "Or at what left of him... *sigh*...",

etc

#

this is just guid, without translate, without RM_

thick karma
verbal yew
#

this is stuff change stats, get xp, or recipes, for example:

"RCP=RecipeID" -- get recipe
rich reef
rich reef
rancid barn
verbal yew
verbal yew
#

thath all

#

guide end

rancid barn
#

the game just knows the dialogue im adding corresponds to the VHS im adding?

thick karma
#

No event

#

Right after declaring it.

rich reef
#

Also, the volubility function wasn't supplied in your code, otherwise I'd take a look at it to see how you did a dropdown index.

thick karma
#

I am on mobile or I'd send the whole file

#

Volubility is in there and the process used for initializing mod options is safe

rich reef
#

I will take a look, thanks!

rich reef
thick karma
#

Yeah that's it

#

Doesn't need to be end of file in yours

#

That was probably context dependent decision

rich reef
#

Awesome, thats helpful.

thick karma
#

I often put all the mod options shit together near the top

#

Trash Talker has a hybrid options loading process sort of

rich reef
#

Now I didnt see any dropdown list in your code to pull from. I'll have to dig at the guides again to figure out how to make a 3 option table to use and pull from.

thick karma
#

Volubility is a dropdown list

#

Iirc

#

I'm pretty sure it corresponds to percent chances

#

What is the default value of volubility in Trash.mod?

#

Remind me just in case I'm wrong

#

In Trash.mod.options

rich reef
#

It looks like it's set to 2

#

I'm trying to find the index though down in the code, where you have that detailed.

thick karma
thick karma
#

Look for the word index

rich reef
#

Yes, it seems that its the selection on the dropdown. Just can't find where it initializes that list in the mod options menu.

#

Yes, I can see that now. It looks like its referencing a translation file for it, which I don't have implemented in mine yet.

red tiger
#

Got that mad scientist energy going..

#

Got some vscode open and some honey-mustard pretzel twists and a plan.

#

Also hello, Notepad++ community.

#

x)

rich reef
red tiger
rich reef
#

Thats good to know. I was looking up how to do a switch case setup, and it just said to use table indexes.

red tiger
#

Lua (when compared to other languages), is stupid simple. It doesn't mean one is dumb not to know it but rather that it's easy to get a grasp of what you work with.

rich reef
#

Ive always strayed away from stacked If statements though, ever since the old basic days, and not having the usage of { } to outline functions kind of murders my organization attempts lol

red tiger
#

It's good that a focus for you is organization.

rich reef
#

Yeah, Ive actually been enjoying how simple it is though.

red tiger
#

Find small projects to push yourself and use that knowledge to further your work.

rich reef
#

I think after reading my old code on projects and not being able to read it, I went out of my way to organize and comment and refactor stuff as much as possible to make it workable at later dates.

rich reef
red tiger
#

I miss working on PZ servers.

#

I'm a Java developer first.

#

I wrote an entire framework that gave rise to Java JAR plugins that hooked into events, commands, and layered API for things in the PZ engine for servers. This also is the framework that my anti-cheat sits on.

rich reef
#

Thats awesome

#

I'm much more comfortable with the C/Java styling of syntax, so going back to stuff like Lua isn't difficult, but makes me double think what I'm doing and whether the same things work or not.

red tiger
#

I still use semi-colons. That's not going away.

rich reef
#

Yeah, I do too. Does that affect Lua code, or is it treated the same way?

red tiger
#

Did you know that you can make unsigned integers from tables in Lua and make them function?

red tiger
rich reef
#

I didnt know that, good to know though

red tiger
#

I recently wrote bitwise emulation in the PZ Lua environment to implement AES support.

rich reef
red tiger
#

Now I can encrypt LuaNet commands.

red tiger
rich reef
#

Very cool

red tiger
#

I code in several languages so I don't want to screw up in another.

rich reef
#

I can't imagine needing to encrypt anything in a PZ mod, but cool that its possible.

red tiger
#

Also because it's all in lua that means it's jumbled in the dynamo VM that Lua is in PZ.

#

Less vulnerable to interception.

rich reef
#

Thats a good point. I can see that need case.

red tiger
#

Also you can make tables 'read-only' in Lua natively using metatables so more protection even.

rich reef
#

What sort of Anit-Cheat do you have going? Better than the vanilla stuff? Or any specific use cases?

red tiger
#

You can write code that checks to see if a table is altered before and after passing it to an event.

red tiger
rancid barn
#

I HAVE SO MANY GUIDS TO GENERATE

red tiger
#

I wrote a separate, more specific and aggressive alternative that runs in Lua through bootloaders and encryption.

rich reef
#

you dont have to go into great detail about it, but just curious of the basics of your anti-cheat

rancid barn
#

BUT THIS MOD IS GUNNA RULE

grizzled fulcrum
red tiger
rancid barn
#

OMG I THOUGHT I LOST THE DIALOGUE

red tiger
#

You can't affect memory beyond conventional assignment.

rancid barn
#

I JUST DIDNT SAVE THE NOTEPAD FILE

grizzled fulcrum
rich reef
red tiger
#

There's a variety of ways to check for the explicit presence of cheats for all except one specific one.

rich reef
#

I'm not well versed in the available cheats for PZ, so I'm curious to what is out there, and how it would affect my server. Having to wipe and reset is miserable, and I don't want to have to whitelist anything unless absolutely necessary.

red tiger
#

I'm also deploying a bootloader approach where the workshop provides a skeleton bootloader function that then loads, encrypts, and distributes local code on the server machine. This allows for servers to customize and improve on the encryption and payload of their own anti-cheat, making it harder for cheaters to write blanket code that bypasses the anti-cheat.

#

It's going to make cheaters' lives pain

rich reef
#

lol

red tiger
#

Diverse security is best.

grizzled fulcrum
#

I'd imagine you could just not choose to load the anticheat's client lua (unless it's done on serverside only)

red tiger
#

Allowing the server to modify or replace the payload and encrypt it is the best approach we have.

red tiger
grizzled fulcrum
#

fair

rich reef
#

Do you get a lot of false positives with your anti-cheat?

red tiger
#

This is an aggressive model where the one people use now that I made is a passive network monitor.

red tiger
#

It's going to be the same with the vanilla one.

#

The difference between the vanilla one and mine is I use YAML for the config and literally everything is configurable, even down to the logs and their nature.

rich reef
#

Thats cool. I wonder if your work will help improve the ability for mod data to be secured simply by running data through your functions, and then to the server. Then you'd be able to increase the effectiveness of the vanilla anti-cheat without having to turn down the sensitivity of them to allow mods to function correctly.

red tiger
red tiger
#

So everything in this 2nd anti-cheat is modular.

#

You can use the bitwise ops library independently, the AES one independently, and even the packet encryption service API.

rich reef
#

Very cool man, very cool.

#

So, when will this masterpeice be released? haha 😉

red tiger
#

When I get it done. The model is already proven and working.

#

Just getting it feature complete which is the easiest step.

#
# Client >
LOG  : General     , 1700627414926> 4,358,007> sendClientCommand(module='mod_loader', command='_', args='{"options":{"encryption":{"keyLength":16,"mode":3,"priority":1}},"_":"?C──:?╦╡?|9┼─r┘\u0003-V±\u0011▒'εxS|s\u0002#;)▓i?)c╪5\u0003╩\u0017Ñ≤\u001b▓?F?4`?\u0000?dª\u0017\u001fK═?;╠*?╥»ñ'V?*╦└▌?╖hN└╜"}'
)

# Server >
2023-11-21 22:30:15.692> General     > ### {"_":{"id":"greet_message","data":{"message":"Hello, Server!"}}}
2023-11-21 22:30:15.693> General     > onReceivePacket(module='mod_loader', player='zombie.characters.IsoPlayer@4efb13f1', id='greet_message', data=table 0x1610818058)
sendServerCommand(
        module='mod_loader',
        command='_',
        args='{"options":{"encryption":{"keyLength":16,"mode":3,"priority":1}},"_":"cj6φ:?╦╡?|9┼─r┘\u0003-V±\u0011▒'εxS|s\u0002#;)▓i?)c╪5\u0003╩\u0017Ñ≤\u001b▓?F?4`?\u0000?dª\u0017\u000fB╓?0╩*?╥»┬└Θ?┌\u0005a√}H▀▒Θ▒"}'
);

# Client >
LOG  : General     , 1700627417288> 4,360,370> ### {"_":{"id":"greet_message","data":{"message":"Hello, Client!"}}}
LOG  : General     , 1700627417317> 4,360,398> onReceivePacket(module='mod_loader', player='nil', id='greet_message', data=table 0x889524107)
#

This is showing it working.

rich reef
#

Fancy fancy

#

Well if you need any guinea pigs for testing on live servers, let me know, and I'd be glad to help.

red tiger
#

Thanks. DM me if you want a link to my personal server where my stuff lives.

#

=)

#

The anticheat is under the name 'Crafthammer' but that's the framework name. I built the anti-cheat into the framework.

rich reef
#

Sure thing. I appreciate the help and advice from anyone willing to share

red tiger
#

Awesome.

#

One day we'll all be writing in Lua modules.

#

Oh and if you know TypeScript, it's possible to mod PZ using it.

#

Just a head's up.

#

The project is dated and I need to upgrade it but yeah that's an option.

rich reef
#

Cool

rancid barn
#

the GUIDs are all done

#

gonna check out what that happy gilmore mod did to add its VHS. this shit is going to the workshop babyyy

#

holy shit this is nothing

#

its that easy!

rich reef
thick karma
#

Tell 4x font users to grab MORE on the Workshop! Haha

rich reef
#

lol

#

I'm testing out your mod now, see how it works.

thick karma
#

If you turn on 4x font and check the page with and without it you'll really see

#

Esp if you enable a few Mod Options mods

#

It's p messy at large font

#

Without MORE

rich reef
#

Hey @thick karma , do you know how to implement Sandbox options in mods as well for server side changes? That's my next learning goal.

thick karma
bronze yoke
rich reef
rich reef
vestal gyro
#

@bronze yoke @verbal yew thank you guys even tho it was nothing major I got 2 more mods published today

verbal yew
vestal gyro
#

It was some traits stuff

thick karma
grizzled fulcrum
#

I am trying to create my first item and have put my item and model in items.txt and models_items.txt but they aren't showing up in the debug items list. I think I may be doing something wrong but I am not sure. I will share a snippet.

items.txt

module AoqiaToothbrushMod {
    item ToothbrushRed {
        DisplayCategory = Junk,
        Type = Normal,
        DisplayName = Toothbrush,
        Icon = ToothbrushRed,
        Weight = 0.1,
        WorldStaticModel = ToothbrushRed,
    }
}

models_items.txt

module AoqiaToothbrushMod {
    model ToothbrushRed {
        mesh = WorldItems/Toothbrush,
        texture = WorldItems/ToothbrushRed,
        scale = 0.6,
    }
}
uneven vessel
#

Where can I find a list of places to insert Items/ProceduralDistributions

#

Like if I wanted to add a pistol to the police locker drop table for example

verbal yew
grizzled fulcrum
#

ohhh ty

#

do I have to prefix stuff with Base too or not

uneven vessel
#

ty my broder

bronze yoke
#

you only need to import base if you reference something from base

#

which you aren't doing

#

are your files in media/scripts/?

#

if so, your files aren't being loaded whatsoever, because otherwise they would overwrite the vanilla files and you would see half the items go missing 😅

#

the actual scripts seem good to me so the issue is probably something to do with where the file is placed causing it to not be read

grizzled fulcrum
#

but I reference the base game Tootbhrush mesh and also the displayname in the item.txt

bronze yoke
#

neither of those things are tied to modules

#

modules only exist within the scope of script objects, the mesh is just a file path

#

displaynames are actually basically an unused feature at this point (but you *do* have to specify one or the translation system fails 🙃) but they were never tied to modules either, they're just a string

grizzled fulcrum
#

so the mesh for the toothbrushred model would be valid without importing base?

uneven vessel
grizzled fulcrum
#

It's just that I have no way to test my item in game because the debug itemlist only shows vanilla items

#

so I'm going to just add it to loot table

bronze yoke
#

if it's not in the debug item list, your mod is not working

grizzled fulcrum
#

o

uneven vessel
#

I've created a new case item

#

my plan is to add the pistol mag and ammo box to its loot table

#

and then force insert this new case into bedroom drawers etc

#

should work

bronze yoke
#

you only need to import a module if you're using script objects from that module - it's mostly useful for things like recipes where you're going to reference items from other modules, the only reason i can think you would need it for an item definition is if you used a vanilla Model script (your scripts create a new model script in your own module that uses the same mesh)

grizzled fulcrum
#

I see

#

also would you happen to know where some docs are on loot tables/procedural loot generation? I have never d one this kind of thing. If not, I will have a look at some mods

bronze yoke
#

it's just a bunch of nested tables, you can see them in server/Items/ProceduralDistributions.lua

grizzled fulcrum
#

I should've known to look at the game's lua, ty

bronze yoke
#

the format is item, chance repeating, so you find the loot table you want to add the item to and do ```lua
local lootTable = ProceduralDistributions.list.Antiques.items

table.insert(lootTable, "MyModule.MyItem")
table.insert(lootTable, 5) -- set this to whatever spawn chance seems appropriate

uneven vessel
#

okay i got it

verbal yew
# grizzled fulcrum I should've known to look at the game's lua, ty
require 'Items/ItemPicker'    
require 'Items/Distributions'
require 'Items/ProceduralDistributions'

local zReProcedural_InstrumentalDistr = {
        'ArmySurplusTools',
        'ArmyHangarTools',
        'GarageCarpentry',
        'GarageMetalwork',
        'GarageMechanics',
        'GarageTools',
    }
for _, v in pairs(zReProcedural_InstrumentalDistr) do
    table.insert(ProceduralDistributions["list"][v].items, "MyModule.MyItem1")
    table.insert(ProceduralDistributions["list"][v].items, 0.5)
    table.insert(ProceduralDistributions["list"][v].items, "MyModule.MyItem2")
    table.insert(ProceduralDistributions["list"][v].items, 1)
end
grizzled fulcrum
#

ty!

verbal yew
uneven vessel
#

how do I make the game read this?

verbal yew
uneven vessel
#

there was something at the bottom of the distritibutions file but

verbal yew
#
require 'Items/ItemPicker'    
require 'Items/Distributions'
table.insert(Distributions.PistolCaseMakarov.rolls, 1);
table.insert(Distributions.PistolCaseMakarov.items, "yourModule.yourItemId");
table.insert(Distributions.PistolCaseMakarov.items, 1);
uneven vessel
#

Uhhhuhhh Okay

#

Sorry I'm really new to all this

red tiger
#

=)

#

Will be back later do to fun things.

#

<3

uneven vessel
#

require 'Items/ItemPicker'
require 'Items/Distributions'
table.insert(Distributions.PistolCaseMakarov.rolls, 1);
table.insert(Distributions.PistolCaseMakarov.items, "yourModule.yourItemId");
table.insert(Distributions.PistolCaseMakarov.items, 1);

PistolCaseMakarov = {
    rolls = 1,
    items = {
        "Base.Makarov", 200,
        "Base.9x18mmClip", 200,
        "Base.9x18mmClip", 10,
        "Base.Bullets9x18mmBox", 200,
    },
    junk = {
        rolls = 1,
        items = {
            
        }
    }
},
#

idk how to copy paste code in a brick like that

verbal yew
bronze yoke
#

if you're adding a new loot table you don't need to use table.insert, just do```lua
Distributions.PistolCaseMakarov = {
rolls = 1,
items = {
"Base.Makarov", 200,
"Base.9x18mmClip", 200,
"Base.9x18mmClip", 10,
"Base.Bullets9x18mmBox", 200,
},
junk = {
rolls = 1,
items = {

    }
}

}

#

(and in fact table.insert will not work)

uneven vessel
#

hmm okay that's what I wanted

#

Now I've got to use
require 'Items/ProceduralDistributions'

table.insert

#

to insert this thing

#

into like closets and drawers right?

bronze yoke
#

yeah, although keep in mind that requiring vanilla files does literally nothing

thick karma
bronze yoke
#

there's value in that

thick karma
#

❤️

uneven vessel
#

praying this works

#

test time

#

nooo

#

Okay it works

#

But there's nothing inside

#

oh... nvm

rancid barn
#

so im looking at the happy gilmore vhs mod as a reference and omg

#

i dont need this guid stuff

#

i can just use the text str8 in the file

#

hell all i need is just a RecordedMedia file for the tape dialogue

rich reef
#

Quick question: Is their an easy to follow list of distribution tables, available areas, etc?

coarse sinew
#

Does anyone know why some textures are centered, and others are not, for example here upper containers are on top in Tiled, but when drawn in game (with drawTextureScaledAspect) in BrushTools they are centered. Corner counters also, in the Tiled they are the bottom of the tile and drawn in game centered.

mellow frigate
verbal yew
#

i want to ask about Jump mod:

Why is the OnPlayerUpdate check used?
Isn't it possible to use environment check around player at the moment of key clicking?
Or is it all that complicated...
Question regarding event performance...

#

or key pressing, btw

#

when the key is held down - check the terrain - release - jump, if all is well

mellow frigate
#

This is for historical reasons. I initially used 'e' key with specific tempo that needed a constant callback.

untold orbit
#

Does anyone know how I can intercept servermsg ?
Like if I do "servermsg testing" in console, how can I check what was sent via lua, if possible?

red tiger
#

@untold orbit Server-side handles this all in Java space.

#

Only client-side is exposed via ISChat.lua

#

You can figure out how to send this via servercommand to the server and then handle it via file I/O with a separate process to pick up or use a Java mod for the server like one of mine to intercept it in Java and handle it however needed.

untold orbit
#

I want to intercept even if noone is online so client wouldn't work 🤔
Wouldn't java mod mean others can't play unless they install that updated file?

red tiger
#

No. If you modify the server's Java you should be fine as long as it doesn't crash or error.

untold orbit
#

ooooh really?! So if I decompile, edit and compile classes directly on the server it will work? 👀

red tiger
#

Yes.

#

You're speaking to the most experienced modder for this specific niche.

#

I've spent thousands of hours in this code doing this for servers in the past.

#

I answered you because that. =P

untold orbit
#

You have any guides? I've done java before 👍

red tiger
#

I had this very specific need for server msgs.

#

So uhh IntelliJ IDEA is your friend.

#

Build an environment where you zip up the class files for PZ and then make it a jar file. Reference this. Reference the other libraries in the game's folder.

#

Double-click on the class you want to modify, copy its contents then paste it in an identical package in the src of your project.

#

Clean it up, modify it, compile it, inject it back into your files and test.

#

Then you're set.

#

Nothing much to a guide other than the nuances of decompilers and how to understand issues with them when they show up.

untold orbit
#

I've got IntelliJ setup already, what do you mean exactly with zip up the class files for pz and make a jar file?

red tiger
#

Jars are zip files with the .jar extension.

#

Zip up the .class files from PZ and then rename the ext to .jar.

#

It will work as a dependency.

verbal yew
#

or ms

red tiger
#

There's an official API call for it. ZombMillis or something

#

It's there.

#

I don't know it by memory but I know it's there because I needed it a lot.

untold orbit
#

So I get the files under the java folder of PZ and zip that?

red tiger
#

Exactly.

untold orbit
red tiger
#
@LuaMethod(name = "getTimeInMillis", global = true)
    public static long getTimeInMillis() {
      return System.currentTimeMillis();
    }
untold orbit
#

Ok I have those files zipped, IntelliJ started with the startup screen, now what 😄

red tiger
#
local timeMS = getTimeInMillis();
red tiger
verbal yew
#

getGameTime():getUnmoddedMultiplier()

red tiger
#

You know that double-clicking a .class file in IDEA will prompt fernflower to decompile it.

#

Set this in the same package in your source control folder.

verbal yew
#

thanks, i found something too :з

red tiger
#

You modify and then compile it against the game's Jar file you made.

#

Take your compile and paste it in and run the server.

#

Then it's development and production my friend.

#

If you develop in Kotlin it's doable with PZ modding too.

untold orbit
#

Are there any plugins to speed things up or should I just create an empty project?

red tiger
#

I personally use Gradle but a maven project is pretty quick.

#

Here's a gradle project that uses a folder lib/* that has the pz Jar in it locally.

#

If you're comfortable with that, you could use something like this to compile and take your compiled files and patch a copy of the server.

#

I have more advanced tools for jar injections for patches for the server but since there's no demand for that here I don't push it out.

untold orbit
#

What decompiler do you recommend?

red tiger
#

I stick with fernflower. I've used it since 2011.

#

So IDEA packages and provides fernflower.

#

Just double-click on a .class file in your IDE and it'll prompt you a agreement to sign and then it'll work.

untold orbit
#

If I just want this for personal use, for my own server, is it really necessary to use Maven/Gradle? I assume a normal IntelliJ build would do when I create the project?

red tiger
#

No. I use it because I'm simply comfortable with it.

#

I linked it as an option for you if that's something you're interested in.

untold orbit
#

Ok so I edit and then click build then upload the file I edited to the server, restart server and enjoy the crash? 😄

red tiger
#

Pretty much.

#

You build against the original compiled files.

#

It's a remarkably simple process for how drummed up the act of modding the core of the game is.

#

The complexity comes from the knowledge of the codebase and what's being done.

untold orbit
#

Yeah I get that. Got to start somewhere tho!

red tiger
#

Well I guess I've made another java modder here lol

#

Good stuff.

verbal yew
#

looool

#

what's...

#

hmmm

#

why here exist two stuff to transmit Rust on vehicle

#

like transmitRust()

#

or sendClientCommand(getSpecificPlayer(getPlayer():getPlayerNum()), "vehicle", "setRust", { vehicle = vehicle:getId(), rust = NUMBER })

#

whats better to use for mp?

#
local zReRust = 1.0

sendClientCommand(getSpecificPlayer(getPlayer():getPlayerNum()), "vehicle", "setRust", { vehicle = vehicle:getId(), rust = zReRust })
or
vehicle:setRust(zReRust)
vehicle:transmitRust()
???
#

hmm... better to throw lua on serverside and
vehicle:setRust(zReRust)
vehicle:transmitRust()
i apologize...

bitter shale
#

hi is there any guide on how to start modding? never modded a game before but i would like to learn ^^ i know java if it helps

thick karma
#

I pinged them into the resource thread.

untold orbit
#

java.lang.UnsupportedClassVersionError: zombie/chat/ChatBase has been compiled by a more recent version of the Java Runtime (class file version 63.0), this version of the Java Runtime only recognizes class file versions up to 61.0

What version does PZ use?

#

I use sdk 19?

red tiger
untold orbit
#

Used same JDK as the server and it works now. Cool. Time to dive into the Java code...

red tiger
#

If you have non-intensive questions I may answer.

#

Been busy myself here in TypeScript.

untold orbit
#

Thanks! I need time to test and if I get stuck I'll shoot a message in here.

echo fiber
#

how do i make comments in a text doc

#

was it <-- Text --> or * \Text /*

woeful mulch
#

Is the author of the Dogs mod here? I was wondering if it's compatible with superb survivors, or if it's just compatible with superb survivors continued

echo fiber
#

i still go back to it quite often if i get stuck or need help but dont want to bother anyone here lol

bitter shale
#

im also new at project zomboid in general xd

echo fiber
# bitter shale ty! i dont have any specific idea atm but would like to learn and this will help...

a good place to start might be looking at adding every day items, i started at making a vape mod got way over my head and ended up learning so much but in the end it came together very nicely! a big thing that can help is download a mod from the workshop, and look at its files and see how its structured and how they built it, and if you find out what you wanna do find mods that are like what your trying to do and see if you can get any hits from what they did

#

but never copy and paste code if you dont know what it does

bitter shale
#

and ty ill try it xd

echo fiber
#

also text documents lol

#

on the left is the text document part and the right is lua, small ss of what iv worked on but didnt really ever have to touch java

#

im sure there are mods that do touch the java code but i wouldnt know, as iv only ever done small mods

bitter shale
echo fiber
#

im actually working on a model as we speak, its a mil crate, needs a texture and a model for this item, it will sit on the ground for players and they will be able to use crafting to trigger its random roll at loot

#

also you dont have to go as detailed as i am, for the most part the less there is the better for fps reasons, but i dont post my mods they are for me to have fun with so i go alittle over kill

bitter shale
echo fiber
#

i actually just finished it XD, but after learning all of this you dont really forget it, i took a break for over a year then just started doing this one

bitter shale
# echo fiber

thanks hehe , what software is used for making these? blender? (the only software that i know of lol)

echo fiber
bitter shale
echo fiber
snow hedge
#

Got a question! Without manually scaling an item, in this case a rifle, down in blender what else can I do to make a model fit in game without significant texture problems?

echo fiber
#

one sec i have something for that

snow hedge
#

iirc it is something that goes in to the module section like:
mesh
texture
scale

I can't seem to get the scaling to work, however.

#

Hence the blender method that... doesn't work all that wonderfully.

echo fiber
#

i used it about a min ago and it worked for me

snow hedge
# echo fiber

Yup, I have something like that but I'm having issues with it. I'm certain it's a me issue though. I'm new to this so I'm bound to have done something dumb.

#

Once I can get back home I'll show what I got.

echo fiber
#

can yo usend me a ss of it

#

okay sounds good you can ping me when you send it 😄

snow hedge
#

👍

echo fiber
subtle sparrow
#

Hi All! Was hoping for a little direction on where to look. I want to start with something super simple (or should be) that bugs me to get into PZ modding. The saved dropdown on the trait selection screen, I want to sort that. I'm trying to figure out where to start honestly, to modify that specific sropdown, if anyone has a good idea! Thanks!

verbal yew
#

I've played enough

brittle dock
# subtle sparrow Hi All! Was hoping for a little direction on where to look. I want to start wi...

https://youtu.be/N6tZujOPnDw This video shows how to add a custom drink object to the game, and should give you a good introduction to a lot of different elements of the development process-- all under 1 hour.

This tutorial video will show you how to make a simple drink in Project Zomboid. I will go into the steps involved from start to finish. Read more below.

0:00 intro
1:08 Start
1:44 Searching for images to use in game
2:30 Searching for the Sound Effect
3:06 Creating mod file directory
3:54 Creating mod.info
4:34 Creating a poster
7:14 Adding ne...

▶ Play video
subtle sparrow
#

Thanks!

brittle dock
echo fiber
#

apparently iv been gone to long, im trying to get the items to load in game but its acting like the mods not installed at all, i checked it is am i doing something wrong if anyone can point it out in this photo

clever palm
#

im

brittle dock
#

Hey all, in the procedural lists for the distributions, what does "weight chance" refer to?

For example

            procedural = true,
            procList = {
                {name="ClothingStoresDress", min=0, max=99, weightChance=100},
                {name="ClothingStoresShirts", min=0, max=99, weightChance=100},```

From what I gather, ```ClothingStoresDress = {
        rolls = 4,
        items = {
            "DressKnees_Straps", 10,
            "Dress_Knees", 10,``` indicates that containers labelled "ClothingStoresDress" make 4 separate checks, each time giving the listed items their respectively numbered chance of spawning.

I would imagine the "weightChance" of the procedural lists determines which of the lists is chosen? ie. if ClothingStoresShirts had a weight chance of 50, a clothing rack container would be twice as likely to contain dresses instead of shirts?
coarse sinew
#

You're right, "weightChance" influences the probability of a specific item list (or procedural list) being chosen when the game decides what items to spawn in a particular container. Each procedural item list in a container can have a different "weightChance", which acts as a weighting factor for the likelihood of that list being selected during the item generation process.

Here’s how it generally works:

  • when the game needs to populate a container with items, it considers all procedural lists available for that container.
  • it calculates the total weight by adding up the "weightChance" values of all procedural lists assigned to that container.
  • each list's chance of being selected is proportional to its "weightChance" relative to the total weight. For example, if you have two lists — one with a "weightChance" of 100 and another with a "weightChance" of 50 — the first list has a 2:1 likelihood of being chosen compared to the second.
#

So in your example, if "ClothingStoresDress" and "ClothingStoresShirts" both have a "weightChance" of 100, they have equal probability of being chosen. If "ClothingStoresShirts" had a "weightChance" of 50, then "ClothingStoresDress" would indeed be twice as likely to be chosen

echo fiber
#

i keep getting this bug log when i try to place my object on the ground

late girder
#

I'm trying to override (clamp) the java getWidth return value to try to fix the issue with too wide UI panels for ultrawide+ resos, anyone messed around with that?

#

trying to find where the magic factor is located that's used for stretching the panels based on screen resolution

echo fiber
#

So my keycard model shows up and works in game, but the crate does not they are both set up the same to the T i over looked everything and there are no mistakes please someone help

#

anyone able to voice chat to help me figure this out, i see nothing wrong with it and yet still cant place it

brittle dock
#

Doublecheck the distribution .lua for your mod, give it a 100% spawn rate somewhere and see if it spawns, as a means of isolating the issue

echo fiber
#

i go to place it and the game yells at me saying that error i put in chat

#

all the models are exported as FBX but my models that have more going on they wont load in the game at all

reef nebula
#

Hi there everyone....Im new to PZ MODDING but have been doing some DayZ mods for some time now .....any tips for where to start learning....or maybe a few YouTube links

brittle dock
bright fog
#

If you need any info about zombies I can help you with that 👌

verbal yew
#

?!

verbal yew
bright fog
#

yeah just letting you know since I've worked a lot on zombies in the past months ;)

verbal yew
#

At least I don't have any ideas yet...

#

I'm smearing myself with mods. I'm looking at someone else's code. rewriting some... polishing my build modpack etc

reef nebula
brittle dock
# reef nebula Okay so now I'm looking for pickle Rick thank you lol

This tutorial video will show you how to make a simple drink in Project Zomboid. I will go into the steps involved from start to finish. Read more below.

0:00 intro
1:08 Start
1:44 Searching for images to use in game
2:30 Searching for the Sound Effect
3:06 Creating mod file directory
3:54 Creating mod.info
4:34 Creating a poster
7:14 Adding ne...

▶ Play video
mellow frigate
#

@TIS devs or whoever modder : When I change many squares in one session (from a mod), When I quit the game, it does not quit successfully. last log (in debug) is 'EXITDEBUG: IngameState.exit 7'. This also occures if I limit down to 5 the number of changes per OnPlayerUpdate. I did not test lower frequency. Has anyone any hints on what I could do to prevent this problem ? Note that it seems the save went OK as I can reload the game and my changes being saved I'll never get the problem with that same save.

slow graniteBOT
#
just.creed has been warned

Reason: Bad word usage

mellow frigate
#

another note: with 5 max, I never get that kind of warning: 'IsoRegion Warning -> xx squares have been changed in one tick.' Still the game quit fails.

red tiger
#

Good afternoon.

violet shell
#

Hi, I'm thinking about to make a mod that brings a window to players 5 minutes before the scheduled restart of the dedicated server. Do you think it's doable ?

robust briar
#

and give warnings at 10 min, 5 min, 1 min, 30 sec

violet shell
#

you use /servemsg with RCON scheduled tasks ? or a mod can send those messages ?

robust briar
#

We have a little program which checks for mod updates, then send those over RCON

#

then issues the quit command

gleaming sorrel
#

I found out how to make the character in PZ actually look sick when they get bitten
By using the bite texture but editing
Other parts of the body like the face

For my Boiling Rash mod they bleed a black substance from the eyes and mouth

#

Definitely a fun thing to mess around with

bright fog
#

👀

#

That's amazing

#

Too bad this kind of thing is very small, so you could make a character visually sick from basic infections like having red eyes and shit like that but would probably be impossible to see

#

In your case it fits pretty well

gleaming sorrel
#

The symptoms are of course easier to see in game as the image compression pixelates the texture

#

Just really hard to get a good picture of

bright fog
#

yeah

gleaming sorrel
#

This is the colors I used for the Knox plague version

They are visible
Though the brighter it is the better you can see the color

drifting ore
#

anyone know the name of the maintenance skill in the game files?

violet shell
#

I'm looking for someone who could help me using sandboxvars options for my mod, anyone ?

bright fog
bright fog
#

I'm not familiar with the art style of the original creation of your zombie stuff

#

But like your lore text you coul put it into a cool image alongside your custom zombies put in scene

#

(if they come from other games or else at least)

gleaming sorrel
gleaming sorrel
bright fog
#

👌

bright fog
#

For the sizes at least

gleaming sorrel
#

I often like to use a very government informative style

And I was trying earlier to figure out how to make a little vector image human
So I can make some symptoms charts
To put of the modpage
Kinda like the ones the movie cargo used

gleaming sorrel
bright fog
gleaming sorrel
#

Thank you very much