Why is my tower placement system breaking?

Raycasting on the server prevents the exploit (if OP is using raycasting to detect if anything is being hit) of parts being spammed into 1 position.

Another issue is you keep triggering your SendPlacementStatus connection every time the button is pressed. You are constantly creating more and more connections… and I was writing this I just found out your issue.

You are creating insane amounts of connections every time the button is pressed. These connections are causing major client desyncs that start slow but end off high. You need to rewrite and properly organize your code as I can’t understand what goes where.

1 Like

Any shot that you are cloning/creating the baseplate on the client? This code is a mess so I can’t tell.

No, I only did a few tower defense systems (Enemy, placement system) and I’m sure no other scripts affect the baseplate in any way (because the entire project is just those 2 systems as of now)

Plus the baseplate is automatically created in the template

EDIT:

This issue happens when I place the tower for the 1st time.

Then how could the baseplate be nil unless of course that’s not the part that your code refers to?

Please double-check that your code is properly referring to whatever part you are saying is nil.

You don’t need to be ray-casting on the server as it does not make any sort of difference. To prevent parts being spammed into one position you can attempt implementing something like a cool down and you can save the coordinates the player has previously placed to verify it’s a legitimate position, as-well as having any other checks.

Something like this:

function BuildUtil.GetMousePosition(IgnoreList)
	local NewRay = Ray.new(Camera.CFrame.Position, (Mouse.Hit.Position - Camera.CFrame.Position).Unit * 300);
	local _, Position, SurfaceNormal = workspace:FindPartOnRayWithIgnoreList(NewRay, IgnoreList);
	local EditedPosition = Position + SurfaceNormal;

	return EditedPosition.X, EditedPosition.Y, EditedPosition.Z
end

You can use a method like that in some utility module to get X, Y, Z coordinates from the mouse hit. You can then send those coordinates to the server and the server can go through multiple checks and whatever you need before placing it. (You can probably use Workspace:Raycast() instead) One of the main things involved in those checks Is the os.time() to check they are not constantly spamming the remote, as-well as doing that on the client. As far as I know, exploits don’t really matter when it comes to ray-casting on the server rather than the client? Maybe I’m wrong…?

You can also put a whitelist in the RaycastParams and only set it to some sort of build-plot, which saves you from having to check the result if it hit a Baseplate.

In a game I’m currently working on, it uses a system like this, you can check the game out: Monster Blast! - Roblox

You definitely don’t need a “SendPlacementStatus” remote to tell the client if they’re able to place, as you are able to do those types of checks on the client.

(Let me note that you can actually place the block on the client first before you fire the remote to tell the server you want to place a block, and then destroy that block in 1-2 seconds. This helps make network lag look like nothing with placement systems)

(The logic I’ve just explained is all used in the game linked)

1 Like

Note that most of the work for placement systems is the actual dragging of the object. You want it to be updated wherever you hover, and then when you click send those coordinates to the server to verify and eventually place. Logic really comes into play here, brainstorming of the system.

You can see that in my game it actually uses a spring library to move around the model. The system has not experienced any issues with exploiters to my knowledge and has not had any issues lagging/network lag is not visible.

(Sorry I keep editing, I just keep thinking of things) (Using a whitelist for the rays is really good with these types of things because it can limit your ray’s vision to ONLY the specified whitelist)

My honest suggestion is that you start this system from scratch and just read up on tutorials & check out some of the advice I mention above. I know there are a couple really good tutorials on this stuff on the forums. Unfortunately most of them don’t really account for any kind of lag though.

1 Like

Alright, I’ll keep my current system in a folder and redo my 2 days of (broken) work. I’m not really looking for performance boosts though, this is just a little project, and I don’t have any current plans for making it a game.

My current system isn’t even lagging a bit. I’ll set an FPS counter and see how bad performance is. If it isn’t that laggy, I might consider fixing my current code. (I mean I did fix it myself a bunch of times, I’ll take your solutions for both of these options and see if they work.)

1 Like

Alright. Can you send me a GIF of the current issue? Just so I can visualize the issue at hand.

You can also attempt to make the whitelist solely the baseplate so there is ensured no issues with it being nil or whatever.

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {workspace.BuildPlot}
raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
raycastParams.IgnoreWater = true

local rayCast = game.Workspace:Raycast(mouseOrigin, Vector3.new(0, -10, 0), raycastParams)
1 Like

Just took a video of it, and I was actually a bit surprised to see it was allowed to place for about .5 seconds before returning to it’s usual state.

Video:

Sorry about the video quality

I just realized, why are you raycasting 10 studs down from the mouse origin?

1 Like

Honestly have no idea, like I said I suck at scripting (Not really though)

Well you are basically taking the origin of the mouse and raycasting all the way under the baseplate LOL.

PlaceConnection = runService.RenderStepped:Connect(function()
	remotes.isPlacingEvent:FireServer(mouse.Hit.Position.X, mouse.Hit.Position.Z, mouse.Hit.Position.Y, mouse.UnitRay.Origin)
end)

This is also a HUGE issue. You are firing a remote every frame.
You realize you can just hook the connection up to a “Mouse.Move”?

There’s not really much I can do in terms of helping other than guiding you onto making the code better – the type of system you have right now is pretty messed up

Consider making the Mouse.Move and then raycasting out and positioning a “Ghost” model of the current model you have, then when the user wants to request a placement, send it to the server to make the placement and do some checks.

1 Like

Thank you for your help. I haven’t solved my problem yet, but I still wonder why is the baseplate nil? No other scripts affect the baseplate in any way, so I’m pretty confused.

EDIT: So the problem is not the baseplate, but the raycast itself. What’s wrong here?

1 Like

Haha. You are raycasting 10 studs down :rofl:
Either that, or it’s because there is something wrong with the blacklist you provided and it’s getting confused.

I changed the blacklist to a whitelist, included the baseplate, and made it raycast 1 stud down instead. I still don’t understand anything.

When you raycast, you give it the ORIGIN and you give it the DIRECTION. The origin is where the ray is meant to start, and then you give it a Vector3 Direction which is where you want the ray going.

You are currently using the origin as the Mouse.Hit, which is wrong because that would have to mean the ray starts in the target position they click.

This is where client comes into play, you can use the function I gave you above to get a normalized mouse position (X, Y, Z) and send that to the server when they want to place. (You can use the Camera.CFrame.Position as the origin point and then raycast in the direction) (Or if you want to keep it simple, you can send the server the Mouse.Hit.Position and then have the server Model:SetPrimaryPartCFrame(CFrame.new(Hit.X, Hit.Y, Hit.Z)) (You could also, if you want to keep the server raycasting for whatever reason, send the server the starting position (origin) and give it the direction for the server to initialize a ray)

Using the Camera.CFrame.Position as the origin worked! (I set the direction to face 100 studs down :laughing: 10 studs down just wasn’t enough) Thanks for the help!

1 Like

Glad to help. You can always come back to this thread in the future if you ever want to completely redo the system!

1 Like

I found something bad, and that bad thing is that I can place on tracks (Unless I’m facing from above)
Here’s how I’m currently raycasting:

local rayCast2 = game.Workspace:Raycast(camPos, Vector3.new(0, -100, 0), raycastParams)

I tried subtracting a value from mousePos and using that for the direction, but it errors when I position my camera up in the sky.

EDIT: Everything’s breaking all over again.

I think this is the time where you should just consider reworking it all. Getting the normalized mouse position on the client, sending coordinates to the server.

Using a Mouse.Move event to determine where to drag the model to. (On the client)

Alternatively, you can use this for the raycasting:

local Direction = (MouseHitPosition - CameraPosition).Unit * 300
local Ray = game.Workspace:Raycast(CameraPosition, Direction, RaycastParams)

Don’t forget, you are doing (what should be done on the client), on the server, so there are bound to be issues.

1 Like