mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local DUPE_SEND_SIZE = 60000
|
||||
|
||||
if ( CLIENT ) then
|
||||
|
||||
--
|
||||
-- Called by the client to save a dupe they're holding on the server
|
||||
-- into a file on their computer.
|
||||
--
|
||||
local LastDupeArm = 0
|
||||
concommand.Add( "dupe_arm", function( ply, cmd, arg )
|
||||
|
||||
if ( !arg[ 1 ] ) then return end
|
||||
|
||||
if ( LastDupeArm > CurTime() and !game.SinglePlayer() ) then ply:ChatPrint( "Please wait a second before trying to load another duplication!" ) return end
|
||||
LastDupeArm = CurTime() + 1
|
||||
|
||||
-- Server doesn't allow us to do this, don't even try to send them data
|
||||
local res, msg = hook.Run( "CanArmDupe", ply )
|
||||
if ( res == false ) then ply:ChatPrint( msg or "Refusing to load dupe, server has blocked usage of the Duplicator tool!" ) return end
|
||||
|
||||
-- Load the dupe (engine takes care of making sure it's a dupe)
|
||||
local dupe = engine.OpenDupe( arg[ 1 ] )
|
||||
if ( !dupe ) then ply:ChatPrint( "Error loading dupe.. (" .. tostring( arg[ 1 ] ) .. ")" ) return end
|
||||
|
||||
local uncompressed = util.Decompress( dupe.data, 5242880 )
|
||||
if ( !uncompressed ) then ply:ChatPrint( "That dupe seems to be corrupted!" ) return end
|
||||
|
||||
--
|
||||
-- And send it to the server
|
||||
--
|
||||
local length = dupe.data:len()
|
||||
local parts = math.ceil( length / DUPE_SEND_SIZE )
|
||||
|
||||
local start = 0
|
||||
for i = 1, parts do
|
||||
local endbyte = math.min( start + DUPE_SEND_SIZE, length )
|
||||
local size = endbyte - start
|
||||
|
||||
net.Start( "ArmDupe" )
|
||||
net.WriteUInt( i, 8 )
|
||||
net.WriteUInt( parts, 8 )
|
||||
|
||||
net.WriteUInt( size, 32 )
|
||||
net.WriteData( dupe.data:sub( start + 1, endbyte + 1 ), size )
|
||||
net.SendToServer()
|
||||
|
||||
start = endbyte
|
||||
end
|
||||
|
||||
end, nil, "Arm a dupe", { FCVAR_DONTRECORD } )
|
||||
|
||||
end
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
--
|
||||
-- Add the name of the net message to the string table (or it won't be able to send!)
|
||||
--
|
||||
util.AddNetworkString( "ArmDupe" )
|
||||
|
||||
net.Receive( "ArmDupe", function( size, client )
|
||||
|
||||
if ( !IsValid( client ) or size < 48 ) then return end
|
||||
|
||||
local res, msg = hook.Run( "CanArmDupe", client )
|
||||
if ( res == false ) then client:ChatPrint( msg or "Server has blocked usage of the Duplicator tool!" ) return end
|
||||
|
||||
local part = net.ReadUInt( 8 )
|
||||
local total = net.ReadUInt( 8 )
|
||||
|
||||
local length = net.ReadUInt( 32 )
|
||||
if ( length > DUPE_SEND_SIZE ) then return end
|
||||
|
||||
local datachunk = net.ReadData( length )
|
||||
|
||||
client.CurrentDupeBuffer = client.CurrentDupeBuffer or {}
|
||||
client.CurrentDupeBuffer[ part ] = datachunk
|
||||
|
||||
if ( part != total ) then return end
|
||||
|
||||
local data = table.concat( client.CurrentDupeBuffer )
|
||||
client.CurrentDupeBuffer = nil
|
||||
|
||||
if ( ( client.LastDupeArm or 0 ) > CurTime() and !game.SinglePlayer() ) then ServerLog( tostring( client ) .. " tried to arm a dupe too quickly!\n" ) return end
|
||||
client.LastDupeArm = CurTime() + 1
|
||||
|
||||
ServerLog( tostring( client ) .. " is arming a dupe, size: " .. data:len() .. "\n" )
|
||||
|
||||
local uncompressed = util.Decompress( data, 5242880 )
|
||||
if ( !uncompressed ) then
|
||||
client:ChatPrint( "Server failed to decompress the duplication!" )
|
||||
MsgN( "Couldn't decompress dupe from " .. client:Nick() .. "!" )
|
||||
return
|
||||
end
|
||||
|
||||
local Dupe = util.JSONToTable( uncompressed )
|
||||
if ( !istable( Dupe ) ) then return end
|
||||
if ( !istable( Dupe.Constraints ) ) then return end
|
||||
if ( !istable( Dupe.Entities ) ) then return end
|
||||
if ( !isvector( Dupe.Mins ) ) then return end
|
||||
if ( !isvector( Dupe.Maxs ) ) then return end
|
||||
|
||||
client.CurrentDupeArmed = true
|
||||
client.CurrentDupe = Dupe
|
||||
|
||||
client:ConCommand( "gmod_tool duplicator" )
|
||||
|
||||
--
|
||||
-- Tell the client we got a dupe on server, ready to paste
|
||||
--
|
||||
local workshopCount = 0
|
||||
if ( Dupe.RequiredAddons ) then workshopCount = #Dupe.RequiredAddons end
|
||||
|
||||
net.Start( "CopiedDupe" )
|
||||
net.WriteUInt( 0, 1 ) -- Can save
|
||||
net.WriteVector( Dupe.Mins )
|
||||
net.WriteVector( Dupe.Maxs )
|
||||
net.WriteString( "Loaded dupe" )
|
||||
net.WriteUInt( table.Count( Dupe.Entities ), 24 )
|
||||
net.WriteUInt( workshopCount, 16 )
|
||||
if ( Dupe.RequiredAddons ) then
|
||||
for _, wsid in ipairs( Dupe.RequiredAddons ) do
|
||||
net.WriteString( wsid )
|
||||
end
|
||||
end
|
||||
net.Send( client )
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
@@ -0,0 +1,280 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
hook.Add( "PostRender", "RenderDupeIcon", function()
|
||||
|
||||
--
|
||||
-- g_ClientSaveDupe is set in transport.lua when receiving a dupe from the server
|
||||
--
|
||||
if ( !g_ClientSaveDupe ) then return end
|
||||
|
||||
--
|
||||
-- Remove the global straight away
|
||||
--
|
||||
local Dupe = g_ClientSaveDupe
|
||||
g_ClientSaveDupe = nil
|
||||
|
||||
local FOV = 17
|
||||
|
||||
--
|
||||
-- This is gonna take some cunning to look awesome!
|
||||
--
|
||||
local Size = Dupe.Maxs - Dupe.Mins
|
||||
local Radius = Size:Length() * 0.5
|
||||
local CamDist = Radius / math.sin( math.rad( FOV ) / 2 ) -- Works out how far the camera has to be away based on radius + fov!
|
||||
local Center = LerpVector( 0.5, Dupe.Mins, Dupe.Maxs )
|
||||
local CamPos = Center + Vector( -1, 0, 0.5 ):GetNormalized() * CamDist
|
||||
local EyeAng = ( Center - CamPos ):GetNormal():Angle()
|
||||
|
||||
--
|
||||
-- The base view
|
||||
--
|
||||
local view = {
|
||||
type = "3D",
|
||||
origin = CamPos,
|
||||
angles = EyeAng,
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = 512,
|
||||
h = 512,
|
||||
aspect = 1,
|
||||
fov = FOV
|
||||
}
|
||||
|
||||
--
|
||||
-- Create a bunch of entities we're gonna use to render.
|
||||
--
|
||||
local entities = {}
|
||||
local i = 0
|
||||
for k, ent in pairs( Dupe.Entities ) do
|
||||
|
||||
if ( ent.Class == "prop_ragdoll" ) then
|
||||
|
||||
entities[ k ] = ClientsideRagdoll( ent.Model or "error.mdl", RENDERGROUP_OTHER )
|
||||
|
||||
if ( istable( ent.PhysicsObjects ) ) then
|
||||
|
||||
for boneid, v in pairs( ent.PhysicsObjects ) do
|
||||
|
||||
local obj = entities[ k ]:GetPhysicsObjectNum( boneid )
|
||||
if ( IsValid( obj ) ) then
|
||||
obj:SetPos( v.Pos )
|
||||
obj:SetAngles( v.Angle )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
entities[ k ]:InvalidateBoneCache()
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
entities[ k ] = ClientsideModel( ent.Model or "error.mdl", RENDERGROUP_OTHER )
|
||||
|
||||
end
|
||||
i = i + 1
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- DRAW THE BLUE BACKGROUND
|
||||
--
|
||||
render.SetMaterial( Material( "gui/dupe_bg.png" ) )
|
||||
render.DrawScreenQuadEx( 0, 0, 512, 512 )
|
||||
render.UpdateRefractTexture()
|
||||
|
||||
--
|
||||
-- BLACK OUTLINE
|
||||
-- AWESOME BRUTE FORCE METHOD
|
||||
--
|
||||
render.SuppressEngineLighting( true )
|
||||
|
||||
-- Rendering icon the way we do is kinda bad and will crash the game with too many entities in the dupe
|
||||
-- Try to mitigate that to some degree by not rendering the outline when we are above 800 entities
|
||||
-- 1000 was tested without problems, but we want to give it some space as 1000 was tested in "perfect conditions" with nothing else happening on the map
|
||||
if ( i < 800 ) then
|
||||
local BorderSize = CamDist * 0.004
|
||||
local Up = EyeAng:Up() * BorderSize
|
||||
local Right = EyeAng:Right() * BorderSize
|
||||
|
||||
render.SetColorModulation( 1, 1, 1 )
|
||||
render.SetBlend( 1 )
|
||||
render.MaterialOverride( Material( "models/debug/debugwhite" ) )
|
||||
|
||||
-- Render each entity in a circle
|
||||
for k, v in pairs( Dupe.Entities ) do
|
||||
|
||||
-- Set the skin and bodygroups
|
||||
entities[ k ]:SetSkin( v.Skin or 0 )
|
||||
for bg_k, bg_v in pairs( v.BodyG or {} ) do entities[ k ]:SetBodygroup( bg_k, bg_v ) end
|
||||
|
||||
for j = 0, math.pi * 2, 0.2 do
|
||||
|
||||
view.origin = CamPos + Up * math.sin( j ) + Right * math.cos( j )
|
||||
cam.Start( view )
|
||||
|
||||
render.Model( {
|
||||
model = v.Model,
|
||||
pos = v.Pos,
|
||||
angle = v.Angle
|
||||
}, entities[ k ] )
|
||||
|
||||
cam.End()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Because we just messed up the depth
|
||||
render.ClearDepth()
|
||||
render.SetColorModulation( 0, 0, 0 )
|
||||
render.SetBlend( 1 )
|
||||
|
||||
-- Try to keep the border size consistent with zoom size
|
||||
BorderSize = CamDist * 0.002
|
||||
Up = EyeAng:Up() * BorderSize
|
||||
Right = EyeAng:Right() * BorderSize
|
||||
|
||||
-- Render each entity in a circle
|
||||
for k, v in pairs( Dupe.Entities ) do
|
||||
|
||||
for j = 0, math.pi * 2, 0.2 do
|
||||
|
||||
view.origin = CamPos + Up * math.sin( j ) + Right * math.cos( j )
|
||||
cam.Start( view )
|
||||
|
||||
render.Model( {
|
||||
model = v.Model,
|
||||
pos = v.Pos,
|
||||
angle = v.Angle
|
||||
}, entities[ k ] )
|
||||
|
||||
cam.End()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- ACUAL RENDER!
|
||||
--
|
||||
|
||||
-- We just fucked the depth up - so clean it
|
||||
render.ClearDepth()
|
||||
|
||||
-- Set up the lighting. This is over-bright on purpose - to make the ents pop
|
||||
render.SetModelLighting( 0, 0, 0, 0 )
|
||||
render.SetModelLighting( 1, 2, 2, 2 )
|
||||
render.SetModelLighting( 2, 3, 2, 0 )
|
||||
render.SetModelLighting( 3, 0.5, 2.0, 2.5 )
|
||||
render.SetModelLighting( 4, 3, 3, 3 ) -- top
|
||||
render.SetModelLighting( 5, 0, 0, 0 )
|
||||
render.MaterialOverride( nil )
|
||||
|
||||
view.origin = CamPos
|
||||
cam.Start( view )
|
||||
|
||||
-- Render each model
|
||||
for k, v in pairs( Dupe.Entities ) do
|
||||
|
||||
render.SetColorModulation( 1, 1, 1 )
|
||||
render.SetBlend( 1 )
|
||||
|
||||
-- EntityMods override this
|
||||
if ( v._DuplicatedColor ) then
|
||||
render.SetColorModulation( v._DuplicatedColor.r / 255, v._DuplicatedColor.g / 255, v._DuplicatedColor.b / 255 )
|
||||
--render.SetBlend( v._DuplicatedColor.a / 255 )
|
||||
end
|
||||
if ( v._DuplicatedMaterial ) then render.MaterialOverride( Material( v._DuplicatedMaterial ) ) end
|
||||
|
||||
if ( istable( v.EntityMods ) ) then
|
||||
|
||||
if ( istable( v.EntityMods.colour ) ) then
|
||||
render.SetColorModulation( v.EntityMods.colour.Color.r / 255, v.EntityMods.colour.Color.g / 255, v.EntityMods.colour.Color.b / 255 )
|
||||
--render.SetBlend( v.EntityMods.colour.Color.a / 255 )
|
||||
end
|
||||
|
||||
if ( istable( v.EntityMods.material ) ) then
|
||||
render.MaterialOverride( Material( v.EntityMods.material.MaterialOverride ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
render.Model( {
|
||||
model = v.Model,
|
||||
pos = v.Pos,
|
||||
angle = v.Angle
|
||||
}, entities[ k ] )
|
||||
|
||||
render.MaterialOverride( nil )
|
||||
|
||||
end
|
||||
|
||||
cam.End()
|
||||
|
||||
-- Enable lighting again (or it will affect outside of this loop!)
|
||||
render.SuppressEngineLighting( false )
|
||||
render.SetColorModulation( 1, 1, 1 )
|
||||
render.SetBlend( 1 )
|
||||
|
||||
--
|
||||
-- Finished with the entities - remove them all
|
||||
--
|
||||
for k, v in pairs( entities ) do
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
--
|
||||
-- This captures a square of the render target, copies it to a jpeg file
|
||||
-- and returns it to us as a (binary) string.
|
||||
--
|
||||
local jpegdata = render.Capture( {
|
||||
format = "jpeg",
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = 512,
|
||||
h = 512,
|
||||
quality = 95
|
||||
} )
|
||||
|
||||
--
|
||||
-- Try to figure out if any of the models/materials/etc came from some addon
|
||||
--
|
||||
duplicator.FigureOutRequiredAddons( Dupe )
|
||||
|
||||
--
|
||||
-- Encode and compress the dupe
|
||||
--
|
||||
local DupeJSON = util.TableToJSON( Dupe )
|
||||
if ( !isstring( DupeJSON ) ) then
|
||||
MsgN( "There was an error converting the dupe to a json string" )
|
||||
end
|
||||
|
||||
DupeJSON = util.Compress( DupeJSON )
|
||||
|
||||
--
|
||||
-- And save it! (filename is automatic md5 in dupes/)
|
||||
--
|
||||
if ( engine.WriteDupe( DupeJSON, jpegdata ) ) then
|
||||
|
||||
-- Disable the save button!!
|
||||
hook.Run( "DupeSaveUnavailable" )
|
||||
hook.Run( "DupeSaved" )
|
||||
|
||||
MsgN( "Saved!" )
|
||||
|
||||
spawnmenu.SwitchCreationTab( "#spawnmenu.category.dupes" )
|
||||
|
||||
end
|
||||
|
||||
end )
|
||||
@@ -0,0 +1,109 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
--
|
||||
-- Add the name of the net message to the string table (or it won't be able to send!)
|
||||
--
|
||||
util.AddNetworkString( "ReceiveDupe" )
|
||||
|
||||
--
|
||||
-- Called by the client to save a dupe they're holding on the server
|
||||
-- into a file on their computer.
|
||||
--
|
||||
concommand.Add( "dupe_save", function( ply, cmd, arg )
|
||||
|
||||
if ( !IsValid( ply ) ) then return end
|
||||
|
||||
-- No dupe to save
|
||||
if ( !ply.CurrentDupe ) then return end
|
||||
|
||||
-- Current dupe was armed from a file. Don't allow immediate resave.
|
||||
if ( ply.CurrentDupeArmed ) then return end
|
||||
|
||||
if ( ply.m_NextDupeSave && ply.m_NextDupeSave > CurTime() && !game.SinglePlayer() ) then
|
||||
ServerLog( tostring( ply ) .. " tried to save a dupe too quickly!\n" )
|
||||
return
|
||||
end
|
||||
ply.m_NextDupeSave = CurTime() + 1
|
||||
|
||||
-- Convert dupe to JSON
|
||||
local json = util.TableToJSON( ply.CurrentDupe )
|
||||
|
||||
-- Compress it
|
||||
local compressed = util.Compress( json )
|
||||
local length = compressed:len()
|
||||
local send_size = 60000
|
||||
local parts = math.ceil( length / send_size )
|
||||
|
||||
ServerLog( tostring( ply ) .. " requested a Dupe. Size: " .. json:len() .. " ( " .. length .. " compressed, " .. parts .. " parts )\n" )
|
||||
|
||||
-- And send it(!)
|
||||
local start = 0
|
||||
for i = 1, parts do
|
||||
|
||||
local endbyte = math.min( start + send_size, length )
|
||||
local size = endbyte - start
|
||||
|
||||
-- print( "S [ " .. i .. " / " .. parts .. " ] Size: " .. size .. " Start: " .. start .. " End: " .. endbyte )
|
||||
|
||||
net.Start( "ReceiveDupe" )
|
||||
net.WriteUInt( i, 8 )
|
||||
net.WriteUInt( parts, 8 )
|
||||
|
||||
net.WriteUInt( size, 32 )
|
||||
net.WriteData( compressed:sub( start + 1, endbyte + 1 ), size )
|
||||
net.Send( ply )
|
||||
|
||||
start = endbyte
|
||||
end
|
||||
|
||||
end, nil, "Save the current dupe!", { FCVAR_DONTRECORD } )
|
||||
|
||||
end
|
||||
|
||||
if ( CLIENT ) then
|
||||
|
||||
local buffer = ""
|
||||
net.Receive( "ReceiveDupe", function( len, client )
|
||||
|
||||
local part = net.ReadUInt( 8 )
|
||||
local total = net.ReadUInt( 8 )
|
||||
|
||||
local length = net.ReadUInt( 32 )
|
||||
local data = net.ReadData( length )
|
||||
|
||||
buffer = buffer .. data
|
||||
|
||||
-- MsgN( "R [ " .. part .. " / " .. total .. " ] Size: " .. data:len() )
|
||||
|
||||
if ( part != total ) then return end
|
||||
|
||||
MsgN( "Received dupe. Size: " .. buffer:len() )
|
||||
|
||||
local uncompressed = util.Decompress( buffer )
|
||||
buffer = ""
|
||||
|
||||
if ( !uncompressed ) then
|
||||
MsgN( "Received dupe - but couldn't decompress!?" )
|
||||
return
|
||||
end
|
||||
|
||||
--
|
||||
-- Set this global so we can pick it up when we're rendering a frame
|
||||
-- See icon.lua for this process
|
||||
--
|
||||
g_ClientSaveDupe = util.JSONToTable( uncompressed )
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user