GMAD8p\Addon Share{ "description": "Description", "type": "tool", "tags": [] }Author Namelua/autorun/client/cl_addon_share.lua› {:ÕFlua/autorun/server/addon_share.lua‘ ~Rëpif game.SinglePlayer() then return end local categoryIcons = { ["Effects"] = "icon16/image.png", ["Entity"] = "icon16/bomb.png", ["Gamemode"] = "icon16/controller.png", ["map"] = "icon16/map.png", ["model"] = "icon16/sport_soccer.png", ["NPC"] = "icon16/monkey.png", ["tool"] = "icon16/wrench.png", ["Vehicle"] = "icon16/car.png", ["Weapon"] = "icon16/gun.png" } -- Receiving and decompressing list net.Receive("addonshare_addons_table", function() AS_ReceivedData = AS_ReceivedData or "" local msg_len = 0 local last_msg = false last_msg = net.ReadBool() msg_len = net.ReadUInt(16) AS_ReceivedData = AS_ReceivedData .. net.ReadData(msg_len) if last_msg then AS_AddonsTable = util.JSONToTable(util.Decompress(AS_ReceivedData)) table.sort(AS_AddonsTable, function(l, r) return l.title < r.title end) AS_ReceivedData = nil end end) -- Creating list entry local function addToMenu(Parent, Entry, Previous) local panel = vgui.Create("DPanel", Parent) panel:SetHeight(20) panel:SetPaintBackground(false) panel:Dock(TOP) panel:DockMargin(2, 2, 0, 2) if Previous then panel:MoveBelow(Previous) end local iconSubscribed = vgui.Create("DImage", panel) iconSubscribed:SetImage(steamworks.IsSubscribed(Entry.wsid) and "icon16/plugin.png" or "icon16/plugin_disabled.png") iconSubscribed:SetSize(16, 16) iconSubscribed:SetKeepAspect(true) iconSubscribed:Dock(LEFT) iconSubscribed:DockMargin(2, 2, 2, 2) local iconCategory = vgui.Create("DImage", panel) if Entry.wsid == "973145750" then iconCategory:SetImage("icon16/star.png") --star this addon in list else iconCategory:SetImage(categoryIcons[Entry.category] or "icon16/page_white.png") end iconCategory:SetKeepAspect(true) iconCategory:Dock(LEFT) iconCategory:DockMargin(2, 2, 2, 2) iconCategory:SetSize(16, 16) local buttonWorkshop = vgui.Create("DButton", panel) buttonWorkshop:SetText("WS") buttonWorkshop:SizeToContents() buttonWorkshop.DoClickInternal = function() steamworks.ViewFile(Entry.wsid) end buttonWorkshop:Dock(LEFT) buttonWorkshop:DockMargin(2, 2, 4, 2) local label = vgui.Create("DLabel", panel) label:SetText(Entry.title) label:SetDark(true) label:Dock(FILL) return panel end -- Creating list local function addonMenu(Panel) Panel:SetName("Addon Share") if not AS_AddonsTable then Panel:Help("Error occurred while receiving addons data from server. Has server 'Addon Share' installed?") return nil end if #AS_AddonsTable == 0 then Panel:Help("Looks like this server has no Workshop addons installed") return nil end Panel:SetName("List of serverside addons ("..#AS_AddonsTable..")") Panel:Help("Hit [WS] button to open addon's workshop page.\nGreen puzzle icon means you're subscribed for that.\nSubscription status will update on map change.") local prev for _, entry in ipairs(AS_AddonsTable) do prev = addToMenu(Panel, entry, prev) end end -- Adding list to Q-menu hook.Add("PopulateToolMenu", "Addon Share menu", function () spawnmenu.AddToolMenuOption("Utilities", "Addon Share", "Addon_Share", "Addons", "", "", addonMenu) end) -- You may view source code and contribute to this addon on github -- https://github.com/mailz32/gmod-addon-share -- It's licensed under MIT license if game.SinglePlayer() then return end -- Convars CreateConVar("addonshare_auto", "1", {FCVAR_SERVER_CAN_EXECUTE, FCVAR_ARCHIVE}, "Should 'Addon Share' automatically pick addons to download?") CreateConVar("addonshare_send_maps", "0", {FCVAR_SERVER_CAN_EXECUTE, FCVAR_ARCHIVE}, "Should 'Addon Share' send all maps to client's list too? (Just list entries, not actually a files)") -- Concmd concommand.Add("addonshare_manual", function (ply, cmd, args) local id = tonumber(args[1]) if id then resource.AddWorkshop(id) MsgN("[Addon Share] Manually added an addon with ID " .. id) end end, function () end, "Specify extra addon to be downloaded by client. Use 'id' number from Workshop addon page URI (link)", FCVAR_SERVER_CAN_EXECUTE) -- Preparing table local AddonsTable = {} for _, raw_entry in ipairs(engine.GetAddons()) do local entry = {title=raw_entry.title, wsid=raw_entry.wsid} entry.category = raw_entry.tags:match(",(%a+)") -- extracts second tag, which is addon's category if raw_entry.mounted and (entry.category != "map" or GetConVar("addonshare_send_maps"):GetBool()) then table.insert(AddonsTable, entry) end end AS_DataToSend = util.Compress(util.TableToJSON(AddonsTable)) -- Sending compressed table to client (after he spawns) util.AddNetworkString("addonshare_addons_table") hook.Add("PlayerInitialSpawn", "Addon Share send table", function(ply) local startpos = 0 local msg_len = 0 local last_msg = false while not last_msg do msg_len = math.min(65530, #AS_DataToSend - startpos) last_msg = startpos + msg_len == #AS_DataToSend net.Start("addonshare_addons_table") net.WriteBool(last_msg) net.WriteUInt(msg_len, 16) net.WriteData(string.sub(AS_DataToSend, startpos, startpos + msg_len), msg_len) net.Send(ply) startpos = startpos + msg_len end end) -- Require client to cache addons on connect if GetConVar("addonshare_auto"):GetBool() then local counter = 0 for _, entry in ipairs(engine.GetAddons()) do if entry.mounted and entry.models > 0 and not entry.tags:find('map') then resource.AddWorkshop(entry.wsid) counter = counter + 1 end end MsgN("[Addon Share] Auto added "..counter.." addons for clients to download on connect") else MsgN("[Addon Share] Nothing will be auto added to dependencies for clients due to 'addonshare_auto 0'") end ܰ¬à