diff --git a/protocolspec/spec.things.txt b/protocolspec/spec.things.txt
--- a/protocolspec/spec.things.txt
+++ b/protocolspec/spec.things.txt
@@ -245,12 +245,16 @@
 	Actor actor
 	Class<AActor> stateOwner
 	Short offset
+	String stateOwnerName
+	String stateAddress
 EndCommand
 
 Command SetThingFrameNF
 	Actor actor
 	Class<AActor> stateOwner
 	Short offset
+	String stateOwnerName
+	String stateAddress
 EndCommand
 
 Command SetWeaponAmmoGive
diff --git a/src/cl_main.cpp b/src/cl_main.cpp
--- a/src/cl_main.cpp
+++ b/src/cl_main.cpp
@@ -5032,7 +5032,7 @@
 
 //*****************************************************************************
 //
-static void client_SetThingFrame( AActor* pActor, const PClass *stateOwner, int offset, bool bCallStateFunction )
+static void client_SetThingFrame( AActor* pActor, const PClass *stateOwner, int offset, bool bCallStateFunction, FString stateOwnerName, FString stateAddress )
 {
 	// Not in a level; nothing to do (shouldn't happen!)
 	if ( gamestate != GS_LEVEL )
@@ -5042,6 +5042,15 @@
 		return;
 
 	FState *state = stateOwner->ActorInfo->OwnedStates + offset;
+	FString myStateAddress = stateOwner->ActorInfo->FindStateAddress( state ).ToString();
+
+	if ( stateAddress.CompareNoCase( myStateAddress ) != 0 )
+	{
+		Printf("client_SetThingFrame: mismatch detected: server wants to set thing %d (%s) state to %s::%s, but it actually is %s::%s.\n",
+		       pActor->lNetID, pActor->GetClass()->TypeName.GetChars(),
+		       stateOwnerName.GetChars(), stateAddress.GetChars(),
+		       stateOwner->TypeName.GetChars(), myStateAddress.GetChars());
+	}
 
 	// [BB] The offset is only guaranteed to work if the actor owns the state.
 	if ( stateOwner->ActorInfo->OwnsState( state ))
@@ -5060,14 +5069,14 @@
 //
 void ServerCommands::SetThingFrame::Execute()
 {
-	client_SetThingFrame( actor, stateOwner, offset, true );
+	client_SetThingFrame( actor, stateOwner, offset, true, stateOwnerName, stateAddress );
 }
 
 //*****************************************************************************
 //
 void ServerCommands::SetThingFrameNF::Execute()
 {
-	client_SetThingFrame( actor, stateOwner, offset, false );
+	client_SetThingFrame( actor, stateOwner, offset, false, stateOwnerName, stateAddress );
 }
 
 //*****************************************************************************
diff --git a/src/info.h b/src/info.h
--- a/src/info.h
+++ b/src/info.h
@@ -172,6 +172,34 @@
 	void Destroy();	// intentionally not a destructor!
 };
 
+// [TP]
+struct StateAddress
+{
+	FString Label;
+	int Offset;
+
+	StateAddress ( const char* label = "", int offset = 0 ) :
+		Label ( label ),
+		Offset ( offset ) {}
+
+	bool IsValid() const
+	{
+		return Label.IsNotEmpty();
+	}
+
+	FString ToString() const
+	{
+		if ( Offset != 0 )
+		{
+			FString result;
+			result.Format( "%s+%d", Label.GetChars(), Offset );
+			return result;
+		}
+		else
+			return Label;
+	}
+};
+
 
 
 FArchive &operator<< (FArchive &arc, FState *&state);
@@ -260,6 +288,9 @@
 	FActorInfo *GetReplacement (bool lookskill=true);
 	FActorInfo *GetReplacee (bool lookskill=true);
 
+	// [TP]
+	StateAddress FindStateAddress ( FState* state );
+
 	PClass *Class;
 	FState *OwnedStates;
 	FActorInfo *Replacement;
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -7881,6 +7881,51 @@
 }
 
 
+
+// [BB/TP] Try to find the state label and the correspoding offset belonging to the target state.
+static StateAddress FindStateAddressHelper ( FActorInfo *actorInfo, FState *state, FString prefix, FStateLabels *list )
+{
+	for ( int i = 0; i < list->NumLabels; ++i )
+	{
+		const FStateLabel &label = list->Labels[i];
+
+		if ( label.State )
+		{
+			FState *rangeEnd;
+
+			if ( i == list->NumLabels - 1 )
+				rangeEnd = actorInfo->OwnedStates + actorInfo->NumOwnedStates;
+			else
+				rangeEnd = list->Labels[i + 1].State;
+
+			if ( state >= label.State && state < rangeEnd )
+			{
+				StateAddress result;
+				result.Label = prefix + label.Label.GetChars();
+				result.Offset = state - label.State;
+				return result;
+			}
+		}
+
+		if ( label.Children )
+		{
+			FString newPrefix = prefix + label.Label.GetChars() + '.';
+			StateAddress result = FindStateAddressHelper( actorInfo, state, newPrefix, label.Children );
+
+			if ( result.IsValid() )
+				return result;
+		}
+	}
+
+	return StateAddress{};
+}
+
+StateAddress FActorInfo::FindStateAddress ( FState* state )
+{
+	return FindStateAddressHelper( this, state, "", StateList );
+}
+
+
 //----------------------------------------------------------------------------
 //
 // DropItem handling
diff --git a/src/sv_commands.cpp b/src/sv_commands.cpp
--- a/src/sv_commands.cpp
+++ b/src/sv_commands.cpp
@@ -1620,6 +1620,8 @@
 			command.SetActor( pActor );
 			command.SetStateOwner( stateOwner );
 			command.SetOffset( offset );
+			command.SetStateOwnerName( stateOwner->TypeName.GetChars() );
+			command.SetStateAddress( stateOwner->ActorInfo->FindStateAddress( pState ).ToString() );
 			command.sendCommandToClients( ulPlayerExtra, flags );
 		}
 		else
@@ -1628,6 +1630,8 @@
 			command.SetActor( pActor );
 			command.SetStateOwner( stateOwner );
 			command.SetOffset( offset );
+			command.SetStateOwnerName( stateOwner->TypeName.GetChars() );
+			command.SetStateAddress( stateOwner->ActorInfo->FindStateAddress( pState ).ToString() );
 			command.sendCommandToClients( ulPlayerExtra, flags );
 		}
 	}
