half the xp on respawn in minecraft 1.14

I run a server with my friend and the majority of the members want keepinv enabled which he's fine with, but he'd like the xp to be half'd in order to create an incentive to not just reset whenever, I'm very new to minecraft's command system and spent the past day trying to write something that would set their xp to the scoreboard and then when they die to divide by 2, but i'm unable to come up with a working solution.


This answer assumes that you want to halve the amount of levels and the amount of points since the last level. Actually removing half the XP points that lead to this level would need a lot of complicated calculations, because levels in Minecraft take different amounts of XP to reach, it doesn't just grow linearly. Enchanting and using an anvil also doesn't differentiate these sizes, so it would be fitting to do the same in this system.


As usual with death-related things, you need a scoreboard tracking custom:deaths:

/scoreboard objectives add deaths custom:deaths

You also need some scoreboards for calculations:

/scoreboard objectives add xpLevels dummy
/scoreboard objectives add xpPoints dummy
/scoreboard objectives add const dummy
/scoreboard players set 2 const 2

Then you can do something for all players that have a 1 in the "deaths" score, in this case storing the XP levels and points in a scoreboard and then take levels and points away.
Everything below here can go in a ticking function or repeating command block chain.

First storing the XP, this is easy:

execute as @a[scores={deaths=1..}] store result score @s xpLevels run xp query @s levels
execute as @a[scores={deaths=1..}] store result score @s xpPoints run xp query @s points

Now the calculation, halving both values:

scoreboard players operation @a[scores={deaths=1..}] xpLevels /= 2 const
scoreboard players operation @a[scores={deaths=1..}] xpPoints /= 2 const

Finally you can reset the score to be ready for the next death:

scoreboard players reset @a[scores={deaths=1..}] deaths

But nothing has happened with the XP yet. For that part, you would theoretically need another loop inside your already looping commands, to take away one level at a time until you reach the half, but firstly that would be more complicated and secondly it would lag the server whenever someone dies, the more XP they had before their death, the longer the server would hang. So here is an alternative approach that spreads the reduction and therefore lag out over multiple ticks. This has the added advantage that you get a cool countdown of the levels, making it extra clear for the players what is happening.
For this the xpLevels and xpPoints scores get changed, but that doesn't matter, because they will be set on the next death anyway.
Just add to your normal command chain these commands:

xp add @a[scores={xpPoints=1..}] -1 points
scoreboard players remove @a[scores={xpPoints=1..}] xpPoints 1
xp add @a[scores={xpLevels=1..,xpPoints=0}] -1 levels
scoreboard players remove @a[scores={xpLevels=1..,xpPoints=0}] xpLevels 1

This will first remove one point and then one lever every tick, 20 times per second, until they have reached 0 in both scores, which means that they have half as much XP and levels as before. The reason for first removing points and only then starting with the levels is that the levels take different amounts of XP to reach, so your points difference from the last level might be much less if you have less levels, so you would get way more than half of the points deducted.
You could also speed this up by adding more cases, like removing 10 levels if the scoreboard is at least at 10 and so on. But then of course the animation wouldn't be as nice.
Note that dying while the points or levels get deducted makes the new goal XP half of what it was when dying the second time. But it should usually be pretty rare to die while this happens. If it is a problem, you can just add these cases for 10, 100, 1000 and so on levels/points.

If you want the effect to be even more noticeable, especially for big amounts of points, you can give the players an effect for the time it takes to deduct points and levels, for example blindness:

effect give @a[scores={xpLevels=1..}] blindness 1