diff --git a/[admin]/admin2/admin_definitions.lua b/[admin]/admin2/admin_definitions.lua
index 9e7951eb5..67dfbecad 100644
--- a/[admin]/admin2/admin_definitions.lua
+++ b/[admin]/admin2/admin_definitions.lua
@@ -46,7 +46,8 @@ enum(
"EVENT_SCREEN_SHOT",
"EVENT_RESOURCE_START",
"EVENT_RESOURCE_STOP",
- "EVENT_PLAYER_JOIN"
+ "EVENT_PLAYER_JOIN",
+ "EVENT_MUTE"
},
"ae"
)
@@ -64,7 +65,8 @@ enum(
"SYNC_SERVER",
"SYNC_BAN",
"SYNC_BANS",
- "SYNC_MESSAGES"
+ "SYNC_MESSAGES",
+ "SYNC_MUTES"
},
"as"
)
diff --git a/[admin]/admin2/client/main/admin.lua b/[admin]/admin2/client/main/admin.lua
index 142aeffb0..4755892ff 100644
--- a/[admin]/admin2/client/main/admin.lua
+++ b/[admin]/admin2/client/main/admin.lua
@@ -18,6 +18,7 @@ aAdminMain = {
BlockedTabsBySensitiveData = {
['bans'] = true,
['adminchat'] = true,
+ ['mutes'] = true
}
}
@@ -53,6 +54,7 @@ function aAdminMain.Open()
aAdminMain.AddTab("Resources", aResourcesTab, "resources")
aAdminMain.AddTab("Server", aServerTab, "server")
aAdminMain.AddTab("Bans", aBansTab, "bans")
+ aAdminMain.AddTab("Mutes", aMuteTab, "mutes")
aAdminMain.AddTab("Admin Chat", aChatTab, "adminchat")
aAdminMain.AddTab("Rights", aAclTab, "acl")
-- aAdminMain.AddTab("Network", aNetworkTab)
diff --git a/[admin]/admin2/client/main/admin_mute.lua b/[admin]/admin2/client/main/admin_mute.lua
new file mode 100644
index 000000000..00bfa7ccb
--- /dev/null
+++ b/[admin]/admin2/client/main/admin_mute.lua
@@ -0,0 +1,109 @@
+--[[**********************************
+*
+* Multi Theft Auto - Admin Panel
+*
+* client\main\admin_mute.lua
+*
+* Original File by omar-o22
+*
+**************************************]]
+aMuteTab = {
+ List = {}
+}
+
+function aMuteTab.Create(tab)
+ aMuteTab.Tab = tab
+
+ aMuteTab.MuteListSearch = guiCreateEdit(0.01, 0.02, 0.3, 0.04, "", true, aMuteTab.Tab)
+ guiCreateInnerImage("client\\images\\search.png", aMuteTab.MuteListSearch)
+ guiHandleInput(aMuteTab.MuteListSearch)
+ aMuteTab.MuteList = guiCreateGridList(0.01, 0.07, 0.80, 0.91, true, aMuteTab.Tab)
+ guiGridListAddColumn(aMuteTab.MuteList, "Name", 0.22)
+ guiGridListAddColumn(aMuteTab.MuteList, "Serial", 0.25)
+ guiGridListAddColumn(aMuteTab.MuteList, "Duration", 0.17)
+ guiGridListAddColumn(aMuteTab.MuteList, "Muted by", 0.22)
+ aMuteTab.Details = guiCreateButton(0.82, 0.07, 0.17, 0.04, "Details", true, aMuteTab.Tab)
+ aMuteTab.Unmute = guiCreateButton(0.82, 0.12, 0.17, 0.04, "Unmute", true, aMuteTab.Tab, "unmute")
+ aMuteTab.MuteRefresh = guiCreateButton(0.82, 0.94, 0.17, 0.04, "Refresh", true, aMuteTab.Tab, "listmute")
+
+ addEventHandler("onClientGUIChanged", aMuteTab.MuteListSearch, aMuteTab.onMuteListSearch)
+ addEventHandler("onClientGUIClick", aMuteTab.Tab, aMuteTab.onClientClick)
+ addEventHandler(EVENT_SYNC, root, aMuteTab.onClientSync)
+
+ guiGridListClear(aMuteTab.MuteList)
+ sync(SYNC_MUTES)
+end
+
+function aMuteTab.onClientClick(button)
+ if (button == "left") then
+ if (source == aMuteTab.Details) then
+ local row = guiGridListGetSelectedItem(aMuteTab.MuteList)
+ if (row == -1) then
+ messageBox("No mute selected!", MB_ERROR, MB_OK)
+ return
+ end
+
+ local serial = guiGridListGetItemText(aMuteTab.MuteList, row, 2)
+ aMuteDetails.Show(serial)
+ elseif (source == aMuteTab.Unmute) then
+ local row = guiGridListGetSelectedItem(aMuteTab.MuteList)
+ if (row == -1) then
+ messageBox("No mute selected!", MB_ERROR, MB_OK)
+ return
+ end
+
+ local serial = guiGridListGetItemText(aMuteTab.MuteList, row, 2)
+ aMuteDetails.Show(serial, true)
+ elseif (source == aMuteTab.MuteRefresh) then
+ guiGridListClear(aMuteTab.MuteList)
+ sync(SYNC_MUTES)
+ end
+ end
+end
+
+function aMuteTab.onMuteListSearch()
+ guiGridListClear(aMuteTab.MuteList)
+ local text = string.upper(guiGetText(source))
+ if (text == "") then
+ aMuteTab.Refresh()
+ else
+ for serial, mute in pairs(aMuteTab.List) do
+ if
+ ((mute.name and string.find(string.upper(mute.name), text)) or
+ (serial and string.find(string.upper(serial), text)) or
+ (mute.admin and string.find(string.upper(mute.admin), text)))
+ then
+ aMuteTab.AddRow(serial, mute)
+ end
+ end
+ end
+end
+
+function aMuteTab.onClientSync(type, data)
+ if (type == SYNC_MUTES) then
+ aMuteTab.List = data
+ aMuteTab.Refresh()
+ end
+end
+
+function aMuteTab.Refresh()
+ guiGridListClear(aMuteTab.MuteList)
+ for id, ban in pairs(aMuteTab.List) do
+ aMuteTab.AddRow(id, ban)
+ end
+end
+
+function aMuteTab.AddRow(serial, data)
+ local list = aMuteTab.MuteList
+ local row = guiGridListAddRow(list)
+ local time
+ if time == 0 then
+ time = "Permanent"
+ else
+ time = secondsToTimeDesc(data.time / 1000)
+ end
+ guiGridListSetItemText(list, row, 1, data.name or "Unknown", false, false)
+ guiGridListSetItemText(list, row, 2, serial or "", false, false)
+ guiGridListSetItemText(list, row, 3, time, false, false)
+ guiGridListSetItemText(list, row, 4, data.admin or "Console", false, false)
+end
\ No newline at end of file
diff --git a/[admin]/admin2/client/main/admin_players.lua b/[admin]/admin2/client/main/admin_players.lua
index 3aeb0afe0..e222cb97f 100644
--- a/[admin]/admin2/client/main/admin_players.lua
+++ b/[admin]/admin2/client/main/admin_players.lua
@@ -174,12 +174,11 @@ function aPlayersTab.onClientClick(button)
guiComboBoxGetItemText(aPlayersTab.SlapOptions, guiComboBoxGetSelected(aPlayersTab.SlapOptions))
)
elseif (source == aPlayersTab.Mute) then
- triggerServerEvent(
- "aPlayer",
- localPlayer,
- player,
- iif(aPlayers[player].mute, "unmute", "mute")
- )
+ if aPlayers[player].mute then
+ triggerServerEvent(EVENT_MUTE, localPlayer, "unmute", {serial = getPlayerSerial(player)})
+ else
+ aMute.Show(player)
+ end
elseif (source == aPlayersTab.Freeze) then
triggerServerEvent(
"aPlayer",
diff --git a/[admin]/admin2/client/widgets/admin_mute.lua b/[admin]/admin2/client/widgets/admin_mute.lua
new file mode 100644
index 000000000..baf01d0d3
--- /dev/null
+++ b/[admin]/admin2/client/widgets/admin_mute.lua
@@ -0,0 +1,253 @@
+--[[**********************************
+*
+* Multi Theft Auto - Admin Panel
+*
+* client\widgets\admin_mute.lua
+*
+* Original File by omar-o22
+*
+**************************************]]
+aMute = {
+ defaultDurations = {
+ {"1 minute", 1},
+ {"1 hour", 60},
+ {"1 day", 60 * 24},
+ {"1 week", 60 * 24 * 7},
+ {"Permanent", 0}, -- HARDCODED AS SECOND LAST IN THIS TABLE, DO NOT MOVE
+ {"Custom", 0} -- HARDCODED AS LAST LAST IN THIS TABLE, DO NOT MOVE
+ },
+ durationType = "minutes",
+ Form = nil,
+ playerName = nil
+}
+
+function aMute.Show(player)
+ if (not aMute.Form) then
+ aMute.Create()
+ end
+
+ if (player) then
+ aMute.playerName = getPlayerName(player)
+ guiSetText(aMute.Form, "Mute player "..aMute.playerName)
+ end
+
+ addEventHandler("onClientGUIClick", aMute.Form, aMute.onClick)
+ addEventHandler("onClientGUIFocus", aMute.Form, aMute.onFocus)
+ addEventHandler("onClientGUIBlur", aMute.Form, aMute.onBlur)
+ guiSetVisible(aMute.Form, true)
+ guiBringToFront(aMute.Form)
+end
+
+function aMute.Close(destroy)
+ if (destroy) then
+ destroyElement(aMute.Form)
+ aMute.Form = nil
+ else
+ removeEventHandler("onClientGUIClick", aMute.Form, aMute.onClick)
+ guiSetVisible(aMute.Form, false)
+ aMute.Reset()
+ end
+ aMute.playerName = nil
+end
+
+function aMute.Create()
+ local sx, sy = guiGetScreenSize()
+
+ aMute.Form = guiCreateWindow((sx - 350) / 2, (sy - 290) / 2, 350, 290, "Mute player", false)
+ aMute.ReasonLabel = guiCreateLabel(25, 40, 300, 20, "Mute reason (required):", false, aMute.Form)
+ aMute.ReasonEditBox = guiCreateEdit(25, 65, 300, 30, "Enter mute reason...", false, aMute.Form)
+ aMute.ReasonEditBoxRecievedInput = false
+ aMute.DurationLabel = guiCreateLabel(25, 110, 300, 20, "Mute duration (required):", false, aMute.Form)
+ aMute.DurationComboBox = guiCreateComboBox(25, 140, 300, 100, "Select mute duration...", false, aMute.Form)
+ for i=1, #aMute.defaultDurations do
+ guiComboBoxAddItem(aMute.DurationComboBox, aMute.defaultDurations[i][1])
+ end
+ aMute.DurationEditBox = guiCreateEdit(25, 175, 300, 30, "Duration...", false, aMute.Form)
+ aMute.DurationEditBoxRecievedInput = false
+ guiSetEnabled(aMute.DurationEditBox, false)
+
+ aMute.RadioSeconds = guiCreateRadioButton(35, 216, 70, 20, "Seconds", false, aMute.Form)
+ guiSetEnabled(aMute.RadioSeconds, false)
+ aMute.RadioMinutes = guiCreateRadioButton(112, 216, 70, 20, "Minutes", false, aMute.Form)
+ guiSetEnabled(aMute.RadioMinutes, false)
+ aMute.RadioHours = guiCreateRadioButton(189, 216, 70, 20, "Hours", false, aMute.Form)
+ guiSetEnabled(aMute.RadioHours, false)
+ aMute.RadioDays = guiCreateRadioButton(256, 216, 70, 20, "Days", false, aMute.Form)
+ guiSetEnabled(aMute.RadioDays, false)
+
+ aMute.SubmitButton = guiCreateButton(70, 246, 100, 30, "Submit", false, aMute.Form)
+ aMute.CancelButton = guiCreateButton(180, 246, 100, 30, "Cancel", false, aMute.Form)
+ aRegister("mute", aMute.Form, aMute.Show, aMute.Close)
+end
+
+function aMute.Reset()
+ guiSetText(aMute.Form, "Mute player")
+ guiSetText(aMute.ReasonEditBox, "Enter mute reason...")
+ aMute.ReasonEditBoxRecievedInput = false
+ guiComboBoxSetSelected(aMute.DurationComboBox, -1)
+ guiSetText(aMute.DurationEditBox, "Duration...")
+ guiSetEnabled(aMute.DurationEditBox, false)
+ aMute.DurationEditBoxRecievedInput = false
+ guiSetEnabled(aMute.RadioSeconds, false)
+ guiSetEnabled(aMute.RadioMinutes, false)
+ guiSetEnabled(aMute.RadioHours, false)
+ guiSetEnabled(aMute.RadioDays, false)
+ aMute.durationType = "minutes"
+end
+
+function aMute.onClick(button, state)
+ if not (button == "left" and state == "up") then
+ return
+ end
+
+ -- Handle cancel button first
+ if source == aMute.CancelButton then
+ aMute.Close()
+ return
+ end
+
+ -- Autofill and enable/disable duration editbox based on choice
+ if source == aMute.DurationComboBox then
+ local selected = guiComboBoxGetSelected(aMute.DurationComboBox)
+ if selected == -1 then
+ return
+ elseif selected == #aMute.defaultDurations - 2 then
+ -- Second-last option is permanent duration - clear and disable edit box
+ guiSetText(aMute.DurationEditBox, "0")
+ guiSetEnabled(aMute.DurationEditBox, false)
+ aMute.durationType = "minutes"
+ elseif selected == #aMute.defaultDurations - 1 then
+ -- Last option (should) be custom duration - enable duration edit box
+ guiSetText(aMute.DurationEditBox, "Duration...")
+ guiSetEnabled(aMute.DurationEditBox, true)
+ guiSetEnabled(aMute.RadioSeconds, true)
+ guiSetEnabled(aMute.RadioMinutes, true)
+ guiSetEnabled(aMute.RadioHours, true)
+ guiSetEnabled(aMute.RadioDays, true)
+ guiRadioButtonSetSelected(aMute.RadioMinutes, true)
+ aMute.durationType = "minutes"
+ aMute.DurationEditBoxRecievedInput = false
+ else
+ guiSetText(aMute.DurationEditBox, aMute.defaultDurations[selected + 1][2])
+ guiSetEnabled(aMute.DurationEditBox, false)
+ guiSetEnabled(aMute.RadioSeconds, false)
+ guiSetEnabled(aMute.RadioMinutes, false)
+ guiSetEnabled(aMute.RadioHours, false)
+ guiSetEnabled(aMute.RadioDays, false)
+ aMute.durationType = "minutes"
+ end
+ return
+ end
+
+ if source == aMute.RadioSeconds or source == aMute.RadioMinutes or source == aMute.RadioHours or source == aMute.RadioDays then
+ if guiRadioButtonGetSelected(aMute.RadioSeconds) then
+ aMute.durationType = "seconds"
+ return
+ elseif guiRadioButtonGetSelected(aMute.RadioMinutes) then
+ aMute.durationType = "minutes"
+ return
+ elseif guiRadioButtonGetSelected(aMute.RadioHours) then
+ aMute.durationType = "hours"
+ return
+ elseif guiRadioButtonGetSelected(aMute.RadioDays) then
+ aMute.durationType = "days"
+ return
+ end
+ end
+
+ -- Handle submit button
+ if source == aMute.SubmitButton then
+ aMute.verifyForm()
+ return
+ end
+end
+
+function aMute.onFocus()
+ -- Clear reason/duration edit boxes on first click
+ if source == aMute.ReasonEditBox then
+ if not aMute.ReasonEditBoxRecievedInput then
+ guiSetText(aMute.ReasonEditBox, "")
+ aMute.ReasonEditBoxRecievedInput = true
+ end
+ elseif source == aMute.DurationEditBox then
+ if not aMute.DurationEditBoxRecievedInput then
+ guiSetText(aMute.DurationEditBox, "")
+ aMute.DurationEditBoxRecievedInput = true
+ end
+ end
+end
+
+function aMute.onBlur()
+ -- Reset default text of reason/duration edit boxes if they lose focus with no input
+ if source == aMute.ReasonEditBox then
+ if guiGetText(source) == "" then
+ guiSetText(aMute.ReasonEditBox, "Enter mute reason...")
+ aMute.ReasonEditBoxRecievedInput = false
+ end
+ elseif source == aMute.DurationEditBox then
+ if guiGetText(source) == "" and (guiComboBoxGetSelected(aMute.DurationComboBox) == #aMute.defaultDurations - 1) then
+ guiSetText(aMute.DurationEditBox, "Duration (seconds)...")
+ aMute.DurationEditBoxRecievedInput = false
+ end
+ end
+end
+
+function aMute.verifyForm()
+ -- Verify mute reason
+ local muteReason = guiGetText(aMute.ReasonEditBox)
+ if muteReason == "" or (not aMute.ReasonEditBoxRecievedInput) then
+ messageBox("No mute reason provided.", MB_ERROR, MB_OK)
+ return
+ end
+
+ -- Verify mute duration
+ local muteDuration
+ local durationSelection = guiComboBoxGetSelected(aMute.DurationComboBox)
+ if durationSelection == -1 then
+ messageBox("No mute duration provided.", MB_ERROR, MB_OK)
+ return
+ end
+ durationSelection = durationSelection + 1 -- ComboBox item indices starts at 0 instead of one
+ if durationSelection == #aMute.defaultDurations then
+ muteDuration = guiGetText(aMute.DurationEditBox)
+ muteDuration = tonumber(muteDuration)
+ if not muteDuration or muteDuration <= 0 then
+ messageBox("Invalid mute duration provided.", MB_ERROR, MB_OK)
+ return
+ end
+ else
+ muteDuration = aMute.defaultDurations[durationSelection][2]
+ end
+
+ -- Build mute request "packet" and send to server
+ local actualPlayer -- Actual player may be offline
+ if aMute.playerName then
+ actualPlayer = getPlayerFromName(aMute.playerName)
+ end
+
+ local time
+ if aMute.durationType == "seconds" then
+ time = muteDuration * 1000
+ elseif aMute.durationType == "minutes" then
+ time = muteDuration * 60 * 1000
+ elseif aMute.durationType == "hours" then
+ time = muteDuration * 60 * 60 * 1000
+ elseif aMute.durationType == "days" then
+ time = muteDuration * 60 * 60 * 24 * 1000
+ end
+
+ local data = {
+ playerName = aMute.playerName,
+ reason = muteReason,
+ duration = time,
+ player = actualPlayer
+ }
+
+ triggerServerEvent(
+ EVENT_MUTE,
+ localPlayer,
+ "mute",
+ data
+ )
+ aMute.Close()
+end
\ No newline at end of file
diff --git a/[admin]/admin2/client/widgets/admin_mute_details.lua b/[admin]/admin2/client/widgets/admin_mute_details.lua
new file mode 100644
index 000000000..08a8d1911
--- /dev/null
+++ b/[admin]/admin2/client/widgets/admin_mute_details.lua
@@ -0,0 +1,110 @@
+--[[**********************************
+*
+* Multi Theft Auto - Admin Panel
+*
+* client\widgets\admin_mute_details.lua
+*
+* Original File by omar-o22
+*
+**************************************]]
+aMuteDetails = {
+ Form = nil,
+ Serial = nil
+}
+
+function aMuteDetails.Show(Serial, showUnmtue)
+ if not aMuteDetails.Form then
+ aMuteDetails.Create()
+ end
+
+ aMuteDetails.Serial = Serial
+ local data = aMuteTab.List[Serial]
+
+ guiSetText(aMuteDetails.NickText, "Player name: "..(data.name or "Unknown"))
+ guiSetText(aMuteDetails.SerialText, "Serial: "..(Serial or "None"))
+ guiSetText(aMuteDetails.ReasonText, "Reason: "..(data.reason or "None"))
+ guiSetText(aMuteDetails.AdminText, "Responsible admin: "..(data.admin or "Unknown"))
+ local time
+ if time == 0 then
+ time = "Permanent"
+ else
+ time = secondsToTimeDesc(data.time / 1000)
+ end
+ guiSetText(aMuteDetails.DurationText, "Duration: "..time)
+
+ addEventHandler("onClientGUIClick", aMuteDetails.Form, aMuteDetails.onClick)
+ guiSetVisible(aMuteDetails.Form, true)
+
+ -- Toggle visibilty of certain elements if this is being opened as a confimration dialog for unmute action
+ if showUnmtue then
+ guiSetText(aMuteDetails.ConfirmationText, "Are you sure you want to remove this mute?")
+ guiSetVisible(aMuteDetails.CloseButton, false)
+ guiSetVisible(aMuteDetails.SubmitButton, true)
+ guiSetVisible(aMuteDetails.CancelButton, true)
+ end
+ guiBringToFront(aMuteDetails.Form)
+end
+
+function aMuteDetails.Close(destroy)
+ if destroy then
+ destroyElement(aMuteDetails.Form)
+ aMuteDetails.Form = nil
+ else
+ removeEventHandler("onClientGUIClick", aMuteDetails.Form, aMuteDetails.onClick)
+ guiSetVisible(aMuteDetails.Form, false)
+ aMuteDetails.Reset()
+ end
+ aMuteDetails.Serial = nil
+end
+
+function aMuteDetails.Create()
+ local sx, sy = guiGetScreenSize()
+ aMuteDetails.Form = guiCreateWindow(sx / 2 - 175, sy / 2 - 135, 350, 250, "Mute Details", false)
+ aMuteDetails.ConfirmationText = guiCreateLabel(25, 40, 300, 20, "Mute details:", false, aMuteDetails.Form)
+ aMuteDetails.NickText = guiCreateLabel(50, 70, 300, 20, "Player name: Unknown", false, aMuteDetails.Form)
+ aMuteDetails.SerialText = guiCreateLabel(50, 90, 300, 20, "Serial: None", false, aMuteDetails.Form)
+ aMuteDetails.ReasonText = guiCreateLabel(50, 110, 300, 20, "Reason: None", false, aMuteDetails.Form)
+ aMuteDetails.AdminText = guiCreateLabel(50, 130, 300, 20, "Responsible admin: Unknown", false, aMuteDetails.Form)
+ aMuteDetails.DurationText = guiCreateLabel(50, 150, 300, 20, "Duration: Never", false, aMuteDetails.Form)
+
+ aMuteDetails.SubmitButton = guiCreateButton(105, 200, 60, 40, "Submit", false, aMuteDetails.Form)
+ aMuteDetails.CancelButton = guiCreateButton(185, 200, 60, 40, "Cancel", false, aMuteDetails.Form)
+ aMuteDetails.CloseButton = guiCreateButton(145, 200, 60, 40, "Close", false, aMuteDetails.Form)
+ guiSetVisible(aMuteDetails.CloseButton, true)
+ guiSetVisible(aMuteDetails.SubmitButton, false)
+ guiSetVisible(aMuteDetails.CancelButton, false)
+ aRegister("Mute Details", aMuteDetails.Form, aMuteDetails.Show, aMuteDetails.Close)
+ guiSetVisible(aMuteDetails.Form, false)
+end
+
+function aMuteDetails.Reset()
+ guiSetText(aMuteDetails.Form, "Mute Details")
+ guiSetText(aMuteDetails.ConfirmationText, "Mute details:")
+ guiSetText(aMuteDetails.NickText, "Player name: Unknown")
+ guiSetText(aMuteDetails.SerialText, "Serial: None")
+ guiSetText(aMuteDetails.ReasonText, "Reason: None")
+ guiSetText(aMuteDetails.AdminText, "Responsible admin: Unknown")
+ guiSetText(aMuteDetails.DurationText, "Duration: Never")
+ guiSetVisible(aMuteDetails.CloseButton, true)
+ guiSetVisible(aMuteDetails.SubmitButton, false)
+ guiSetVisible(aMuteDetails.CancelButton, false)
+end
+
+function aMuteDetails.onClick(button, state)
+ if not (button == "left" and state == "up") then
+ return
+ end
+
+ -- Handle cancel button first
+ if source == aMuteDetails.CancelButton or source == aMuteDetails.CloseButton then
+ aMuteDetails.Close()
+ return
+ end
+
+ if source == aMuteDetails.SubmitButton then
+ triggerServerEvent(EVENT_MUTE, localPlayer, "unmute", {serial = aMuteDetails.Serial})
+ aMuteDetails.Close()
+ sync(SYNC_MUTES)
+ return
+ end
+end
\ No newline at end of file
diff --git a/[admin]/admin2/conf/ACL.xml b/[admin]/admin2/conf/ACL.xml
index 35d2574d7..5af3fe12b 100644
--- a/[admin]/admin2/conf/ACL.xml
+++ b/[admin]/admin2/conf/ACL.xml
@@ -14,12 +14,11 @@
+
-
-
@@ -90,6 +89,10 @@
+
+
+
+
@@ -101,12 +104,11 @@
+
-
-
@@ -176,6 +178,10 @@
+
+
+
+
@@ -187,12 +193,11 @@
+
-
-
@@ -262,6 +267,10 @@
+
+
+
+
@@ -273,12 +282,11 @@
+
-
-
@@ -349,5 +357,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/[admin]/admin2/conf/messages.xml b/[admin]/admin2/conf/messages.xml
index 062dd7be5..cf52edd7d 100644
--- a/[admin]/admin2/conf/messages.xml
+++ b/[admin]/admin2/conf/messages.xml
@@ -15,7 +15,7 @@
ADMIN: $admin has kicked $player ($data)
- $player has been muted by $admin.
+ $player has been muted by $admin ($data).
ADMIN: $admin has muted $player
diff --git a/[admin]/admin2/meta.xml b/[admin]/admin2/meta.xml
index b571c9415..696eaecc1 100644
--- a/[admin]/admin2/meta.xml
+++ b/[admin]/admin2/meta.xml
@@ -30,7 +30,8 @@
-
+
+
@@ -46,6 +47,7 @@
+
@@ -67,6 +69,8 @@
+
+
diff --git a/[admin]/admin2/server/admin_functions.lua b/[admin]/admin2/server/admin_functions.lua
index 586aaec68..b2c388fcc 100644
--- a/[admin]/admin2/server/admin_functions.lua
+++ b/[admin]/admin2/server/admin_functions.lua
@@ -31,12 +31,6 @@ aFunctions = {
["ban"] = function(player, data)
setTimer(banPlayer, 100, 1, player, true, true, true, client, data)
end,
- ["mute"] = function(player)
- setPlayerMuted(player, true)
- end,
- ["unmute"] = function(player)
- setPlayerMuted(player, false)
- end,
["freeze"] = function(player)
local vehicle = getPedOccupiedVehicle(player)
diff --git a/[admin]/admin2/server/admin_mute.lua b/[admin]/admin2/server/admin_mute.lua
new file mode 100644
index 000000000..b50527cfb
--- /dev/null
+++ b/[admin]/admin2/server/admin_mute.lua
@@ -0,0 +1,273 @@
+--[[**********************************
+*
+* Multi Theft Auto - Admin Panel
+*
+* server/admin_mute.lua
+*
+* Original File by omar-o22
+*
+**************************************]]
+aMutedList = {}
+
+addEventHandler ( "onPlayerJoin", root,
+ function ()
+ local serial = getPlayerSerial( source )
+ if ( not aHasUnmuteTimer( source ) or isPlayerMuted( source ) ) then
+ return
+ end
+
+ local duration = aGetRemainingUnmuteTime( serial )
+ local reason = aGetRemainingUnmuteReason( serial )
+ if (not duration or duration < 0 or not reason) then
+ return
+ end
+
+ triggerEvent ( EVENT_MUTE, getElementByIndex("console", 0), "mute", { duration = duration, reason = reason, player = source } )
+ end
+)
+
+addEventHandler ( "onPlayerQuit", root,
+ function ()
+ if ( not aHasUnmuteTimer( source ) or not isPlayerMuted( source ) ) then
+ return
+ end
+
+ local serial = getPlayerSerial(source)
+ local name = getPlayerName(source)
+
+ if ( not isTimer(aMutedList[serial].timer) ) then
+ return
+ end
+
+ local time = getTimerDetails( aMutedList[serial].timer )
+ if ( not time ) then
+ return
+ end
+
+ local admin = aMutedList[serial].admin
+ local reason = aMutedList[serial].reason
+
+ aSetRemainingUnmuteTime( source, time )
+ killTimer( aMutedList[serial].timer )
+ aAddOrUpdateMute( serial, name, admin, reason, time )
+ end
+)
+
+addEventHandler ( "onResourceStop", resourceRoot,
+ function ()
+ for serial, data in pairs( aMutedList ) do
+ local remainingTime
+ if ( data.timer and isTimer(data.timer) ) then
+ local time = getTimerDetails( data.timer )
+ if ( time ) then
+ remainingTime = time
+ else
+ remainingTime = data.time
+ end
+ else
+ remainingTime = data.time
+ end
+
+ aAddOrUpdateMute( serial, data.name, data.admin, data.reason, remainingTime, true )
+ end
+ end
+)
+
+addEventHandler ( "onResourceStart", resourceRoot,
+ function ()
+ local result = db.query( "SELECT * FROM mutes" )
+ for index, value in ipairs(result) do
+ aMutedList[value.serial] = {}
+ aMutedList[value.serial].name = value.name
+ aMutedList[value.serial].admin = value.admin
+ aMutedList[value.serial].reason = value.reason
+ aMutedList[value.serial].time = value.time
+
+ for _, player in ipairs( getElementsByType( "player" ) ) do
+ if ( getPlayerSerial(player) == value.serial ) then
+ -- Silent mute
+ aSetPlayerMuted( player, true, value.time, value.admin, value.reason )
+ end
+ end
+ end
+ end
+)
+
+function aSetPlayerMuted ( player, state, time, admin, reason )
+ if isElement(player) and getElementType(player) == "player" then
+ setPlayerMuted ( player, state )
+ end
+
+ if not state then
+ local serial = getPlayerSerial( player )
+ aRemoveUnmuteTimer( serial )
+ return true
+ elseif state then
+ aAddUnmuteTimer( player, admin, reason, time )
+ return true
+ end
+ return false
+end
+
+function aAddUnmuteTimer( player, admin, reason, time )
+ local serial = getPlayerSerial( player )
+ if ( not aMutedList[serial] ) then
+ aMutedList[serial] = {}
+ aMutedList[serial].admin = getPlayerName(admin)
+ aMutedList[serial].reason = reason
+ end
+ aMutedList[serial].name = getPlayerName( player )
+
+ if ( time and time > 0 ) then
+ aMutedList[serial].timer = setTimer(
+ function( )
+ for _, plr in ipairs( getElementsByType( "player" ) ) do
+ if getPlayerSerial( plr ) == serial and isPlayerMuted( plr ) then
+ triggerEvent ( EVENT_MUTE, getElementByIndex("console", 0), "unmute", {player = plr} )
+ end
+ end
+ end,
+ time, 1 )
+ elseif ( time == 0 ) then
+ aSetRemainingUnmuteTime( player, 0 )
+ end
+end
+
+function aRemoveUnmuteTimer( serial )
+ if ( not aMutedList[serial] or not aMutedList[serial].timer ) then
+ return false
+ end
+
+ if ( isTimer( aMutedList[serial].timer ) ) then
+ killTimer( aMutedList[serial].timer )
+ end
+ aMutedList[serial] = nil
+ db.exec( "DELETE FROM mutes WHERE serial = ?", serial )
+ return true
+end
+
+function aSetRemainingUnmuteTime( player, time )
+ local serial = getPlayerSerial( player )
+ if ( not aMutedList[serial] ) then
+ return false
+ end
+
+ aMutedList[serial].time = time
+ return true
+end
+
+function aGetRemainingUnmuteTime( serial )
+ if ( not aMutedList[serial] ) then
+ return false
+ end
+
+ if ( isTimer( aMutedList[serial].timer ) ) then
+ local remainingTime = getTimerDetails( aMutedList[serial].timer )
+ return remainingTime
+ end
+
+ if aMutedList[serial].time then
+ return aMutedList[serial].time
+ end
+
+ return nil
+end
+
+function aGetRemainingUnmuteReason( serial )
+ if ( not aMutedList[serial] or not aMutedList[serial].reason ) then
+ return false
+ end
+
+ return aMutedList[serial].reason
+end
+
+function aHasUnmuteTimer( player )
+ local serial = getPlayerSerial( player )
+ return aMutedList[serial]
+end
+
+function aAddOrUpdateMute(serial, player, admin, reason, time, isRestarted)
+ local result
+ local query_text = "SELECT serial FROM mutes WHERE serial = ?"
+ if ( isRestarted ) then
+ local query = dbQuery( db.connection, query_text, serial )
+ result = dbPoll( query, -1 )
+ else
+ result = db.query( query_text, serial )
+ end
+
+ if #result > 0 then
+ return db.exec( "UPDATE mutes SET name = ?, admin = ?, reason = ?, time = ? WHERE serial = ?", player, admin, reason, time, serial )
+ else
+ return db.exec( "INSERT INTO mutes (serial, name, admin, reason, time) VALUES (?, ?, ?, ?, ?)", serial, player, admin, reason, time )
+ end
+end
+
+function aGetMutesList()
+ return aMutedList
+end
+
+function handleMuteRequest(action, data)
+ -- Basic security check
+ if ( client and source ~= client ) then
+ return
+ end
+
+ -- Permissions check
+ if ( not hasObjectPermissionTo(source, "command."..action, false) ) then
+ outputChatBox( "Access denied for '" .. tostring(action) .. "'", source, 255, 168, 0 )
+ return
+ end
+
+ -- Add mute
+ if action == "mute" then
+ if ( not data or not data.duration or not data.player ) then
+ return
+ end
+
+ if isPlayerMuted( data.player ) then
+ return
+ end
+
+ local time
+ if ( data.duration == 0 ) then
+ time = "Permanent"
+ else
+ time = secondsToTimeDesc( data.duration / 1000 )
+ end
+
+ aSetPlayerMuted( data.player, true, data.duration, source, data.reason )
+ aAction( "player", "mute", source, data.player, time )
+ -- Remove mute
+ elseif ( action == "unmute" ) then
+ local mute = aMutedList[data.serial]
+
+ -- Unlikely to occur, but advise admin if mute failed for some reason
+ if ( not mute ) then
+ outputChatBox( "Mute action failed - check mute details.", source, 255, 0, 0 )
+ return
+ end
+
+ if ( mute ) then
+ -- Checks if the player online
+ local player
+
+ for _, plr in ipairs(getElementsByType("player")) do
+ if ( getPlayerSerial( plr ) == data.serial ) then
+ player = plr
+ break
+ end
+ end
+ if ( player ) then
+ aSetPlayerMuted( player, false )
+ else
+ aRemoveUnmuteTimer( data.serial ) -- if not found remove his serial from table
+ end
+
+ aAction( "player", "unmute", source, ( player and data.player or mute.name ) )
+ end
+ end
+end
+
+addEvent(EVENT_MUTE, true)
+addEventHandler(EVENT_MUTE, root, handleMuteRequest)
\ No newline at end of file
diff --git a/[admin]/admin2/server/admin_server.lua b/[admin]/admin2/server/admin_server.lua
index 2472e44b6..9f831450f 100644
--- a/[admin]/admin2/server/admin_server.lua
+++ b/[admin]/admin2/server/admin_server.lua
@@ -177,7 +177,7 @@ function aAction(type, action, admin, player, data, more)
string = string.gsub(string, "$admin", isAnonAdmin(admin) and "Admin" or (getPlayerName(admin) .. hex))
string = string.gsub(string, "$data2", more or "")
if (player) then
- string = string.gsub(string, "$player", getPlayerName(player) .. hex)
+ string = string.gsub(string, "$player", (isElement(player) and getPlayerName(player) or player) .. hex)
end
return string.gsub(string, "$data", (data and data .. hex or ""))
end
@@ -229,6 +229,9 @@ addEventHandler(
"aPlayer",
root,
function(player, action, ...)
+ if not client then
+ client = source
+ end
if (hasObjectPermissionTo(client, "command." .. action, false)) then
local mdata1, mdata2
local func = aFunctions.player[action]
diff --git a/[admin]/admin2/server/admin_storage.lua b/[admin]/admin2/server/admin_storage.lua
index 89c9bf70e..b5e80102d 100644
--- a/[admin]/admin2/server/admin_storage.lua
+++ b/[admin]/admin2/server/admin_storage.lua
@@ -12,6 +12,7 @@ function aSetupStorage()
db.exec("CREATE TABLE IF NOT EXISTS alias ( ip TEXT, serial TEXT, name TEXT, time INTEGER )")
db.exec("CREATE TABLE IF NOT EXISTS warnings ( ip TEXT, serial TEXT, name TEXT, time INTEGER )")
+ db.exec("CREATE TABLE IF NOT EXISTS mutes ( serial TEXT, name TEXT, admin TEXT, reason TEXT, time INTEGER )")
local node = xmlLoadFile("conf\\interiors.xml")
if (node) then
diff --git a/[admin]/admin2/server/admin_sync.lua b/[admin]/admin2/server/admin_sync.lua
index 8ed315b20..80b8b8da9 100644
--- a/[admin]/admin2/server/admin_sync.lua
+++ b/[admin]/admin2/server/admin_sync.lua
@@ -185,6 +185,15 @@ addEventHandler(
end
tableOut["unread"] = unread
tableOut["total"] = total
+ elseif (type == SYNC_MUTES) then
+ if not hasClientPermissionTo( "general.tab_mutes" ) then
+ return
+ end
+
+ for serial, muteData in pairs(aGetMutesList()) do
+ muteData.time = aGetRemainingUnmuteTime(serial) -- Update time
+ tableOut[serial] = muteData
+ end
end
triggerClientEvent(client or source, EVENT_SYNC, theSource, type, tableOut)
end
diff --git a/[admin]/admin2/shared/utils.lua b/[admin]/admin2/shared/utils.lua
index 8064fc691..0d4dcd448 100644
--- a/[admin]/admin2/shared/utils.lua
+++ b/[admin]/admin2/shared/utils.lua
@@ -63,4 +63,17 @@ function formatDate(format, escaper, timestamp)
end
return formattedDate
+end
+
+function secondsToTimeDesc( seconds )
+ if seconds then
+ local tab = { {"day",60*60*24}, {"hour",60*60}, {"min",60}, {"sec",1} }
+ for i,item in ipairs(tab) do
+ local t = math.floor(seconds/item[2])
+ if t > 0 or i == #tab then
+ return tostring(t) .. " " .. item[1] .. (t~=1 and "s" or "")
+ end
+ end
+ end
+ return ""
end
\ No newline at end of file