I solved 1. and 2. by adding verbose:=true when launching empty_world.launch. This seems a bit hacky, but as I understand, when command_args remain blank in empty_world.launch, gzserver does not properly parse the arguments and tries to treat ode as the world_file argument. I discovered this because, when I hard-coded --verbose at the end of the call to gzserver in empty_world.launch:
<node name="gazebo" pkg="gazebo_ros" type="$(arg script_type)" respawn="$(arg respawn_gazebo)" output="screen" args="$(arg command_arg1)$(arg command_arg2) $(arg command_arg3) -e$(arg physics) $(arg extra_gazebo_args)$(arg world_name)" --verbose />
I got the following error: Could not open file[ode]. So there seems to be some problems with how those arguments are passed to gzserver in empty_world.launch.