In order for the client to know what's happening in the server, the server sends data about some (not all) of its objects to the client (replication), however none of this data is an actual object in itself, the server does not replicate the actual objects over the network, what it replicates instead is the following:
- values from properties which are marked as being replicable (set in the replication block at the top of the class or natively set as such in the engine), but only when they are changed.
- function calls (also set in the replication block at the top of the class or natively set as such in the engine).
- the necessary details to make an object of a specific class to be spawned, referenced or destroyed.
and a few other things depending on whether or not the object should be kept in sync with the server or not (such as specific "network id" of sorts), but that's another subject altogether.
In other words, when an object is spawned in the server and is set to be replicated to the client, what the server does is telling the client "
hey, please spawn an object of class X from package Y at this location with this rotation with this owner ...".
However, if the client does not have such a class, since it doesn't even have the package installed where it resides, the client cannot spawn the object the server wants it to spawn.
From there, I don't remember what happens anymore in that case (I think it will either be assigned None in the client or it will fallback to the nearest parent class), but someone else may clarify this better for you.
But, bottom line, the client won't ever be able to spawn a class it does not have.
Also, everything is pretty much a "class" or "object" inside a package in one way or another: textures, sounds, music, meshes, etc..., so when the server says "assign texture X from package Y to actor Z", the server is not actually sending the texture itself, same thing.
Now, how can an object, such as a custom pickup, interact with the player in a way that is visible in the client, without the client having the package the pickup class resides in?
The first thing to understand here is that the server and client are completely independent environments, and they manage their own objects.
They are only synchronized solely through replication of data as I mentioned above, and as shown above it happens pretty much as a "conversation" between the server and the client rather than "transportation" of actual objects over the network, and both the client and server are free to do whatever they see fit with the data they receive afterwards.
This is why we have things like "roles" (ROLE_None, ROLE_DumbProxy, ROLE_SimulatedProxy, ROLE_AutonomousProxy and ROLE_Authority), to describe what the object is in the context of a server or a client, but that's also another subject I won't get to describe in detail here.
Case in point, each one runs its own Tick event (even at different rates as it's often the case), process their own Touch events, process their own physics, etc...
As a consequence, this also means that the player exists as an object (PlayerPawn) in both the client and server, at the same time, but the client still handles its own PlayerPawn independently from the server, and all that connects both is this "network id" I mentioned.
When you press a key to move your player, your intent runs in the client PlayerPawn, and then it replicates this to the server PlayerPawn, but each PlayerPawn will move on its own in each environment, and the server might even send back some movement corrections when you're too astray from where the server thinks you should be, as the server is the authority and is the one to ultimately decide where things are, how they behave and what effects they have on other things including the players themselves.
With that in mind, in the case of a pickup, for example, even if the client is completely unaware of it (by not having the package to begin with), the server itself knows everything about that pickup, including what to do when the player touches it, since the server is where the pickup is loaded in the first place.
So, when the server PlayerPawn touches the pickup, what happens is that the Touch function of that pickup is called, but is called in the server alone.
What happens next however, is that if you affect the PlayerPawn in that function in some way (the one who touched the pickup), such as changing its DrawScale for example, since the DrawScale is a property of the PlayerPawn, which exists in the client and has its own version of a PlayerPawn object and is in sync with the server version of it, then what you see in the end is the replication of this change to the client PlayerPawn, and thus you are simply seeing from the client perspective are the effects of "something" that just happened in the server through the change of a property in an object you have loaded in your side already (the client PlayerPawn).
In other words, the client PlayerPawn never touched the pickup on its side to begin with, since it does not even exist there so it's absolutely clueless on what is going on, the server PlayerPawn did however, and it affected other objects which also already existed and were synchronized in the client.
Perhaps a bit lengthy and confusing, but hopefully this clarifies a bit on how this stuff works.